feat(cli): add project and resource generator with executable
This commit is contained in:
1 parent
6d46dfbb22
commit
71f81c16ed
2 files changed
+192
No files matched your search
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
||||||
|
require 'prism'
|
||||||
|
|
||||||
|
begin
|
||||||
|
Prism::Cli.new.run(ARGV)
|
||||||
|
rescue Prism::Cli::UsageError => e
|
||||||
|
warn "error: #{e.message}"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
require 'fileutils'
|
||||||
|
require 'time'
|
||||||
|
|
||||||
|
module Prism
|
||||||
|
class Cli
|
||||||
|
class UsageError < StandardError; end
|
||||||
|
|
||||||
|
ServerScript = <<~'RUBY'
|
||||||
|
require_relative '../app'
|
||||||
|
|
||||||
|
server = Prism::Server.new(APP_CLASS, port: (ARGV[0] || 3000).to_i).start
|
||||||
|
puts "listening on #{server.url}"
|
||||||
|
trap('INT') { server.stop; exit }
|
||||||
|
sleep
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
def initialize(root = Dir.pwd)
|
||||||
|
@root = root
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(argv)
|
||||||
|
command, *rest = argv
|
||||||
|
case command
|
||||||
|
when 'new' then new_app(rest.first || raise(UsageError, 'usage: prism new APP_NAME'))
|
||||||
|
when 'generate', 'g' then generate(rest)
|
||||||
|
else raise UsageError, "unknown command #{command.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_app(name)
|
||||||
|
app_dir = File.join(@root, name)
|
||||||
|
raise UsageError, "#{app_dir} already exists" if Dir.exist?(app_dir)
|
||||||
|
|
||||||
|
%w[app/controllers app/models app/views/layouts db/migrate public bin].each do |subdir|
|
||||||
|
FileUtils.mkdir_p(File.join(app_dir, subdir))
|
||||||
|
end
|
||||||
|
|
||||||
|
write_file(File.join(app_dir, 'app', 'views', 'layouts', 'application.erb'), layout_template)
|
||||||
|
write_file(File.join(app_dir, 'app.rb'), app_boot_template(name))
|
||||||
|
write_file(File.join(app_dir, 'config.ru'), config_ru_template(name))
|
||||||
|
write_file(File.join(app_dir, 'bin', 'server'), ServerScript.sub('APP_CLASS', Inflector.camelize(name)))
|
||||||
|
File.chmod(0o755, File.join(app_dir, 'bin', 'server'))
|
||||||
|
app_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_migration(name, fields = [])
|
||||||
|
raise UsageError, 'usage: prism generate migration NAME [field:type ...]' unless name
|
||||||
|
version = next_migration_version
|
||||||
|
class_name = Inflector.camelize(name)
|
||||||
|
path = File.join(@root, 'db', 'migrate', "#{version}_#{Inflector.underscore(name)}.rb")
|
||||||
|
write_file(path, migration_template(class_name, name, fields))
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_model(name, fields = [])
|
||||||
|
raise UsageError, 'usage: prism generate model NAME [field:type ...]' unless name
|
||||||
|
class_name = Inflector.camelize(name)
|
||||||
|
model_path = File.join(@root, 'app', 'models', "#{Inflector.underscore(name)}.rb")
|
||||||
|
write_file(model_path, model_template(class_name))
|
||||||
|
migration_path = generate_migration("create_#{Inflector.tableize(class_name)}", fields)
|
||||||
|
[model_path, migration_path]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def generate(rest)
|
||||||
|
kind, name, *fields = rest
|
||||||
|
case kind
|
||||||
|
when 'migration' then generate_migration(name, fields)
|
||||||
|
when 'model' then generate_model(name, fields)
|
||||||
|
else raise UsageError, "unknown generator #{kind.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_migration_version
|
||||||
|
version = Time.now.utc.strftime('%Y%m%d%H%M%S')
|
||||||
|
version = (version.to_i + 1).to_s while File.exist?(File.join(@root, 'db', 'migrate')) &&
|
||||||
|
Dir.glob(File.join(@root, 'db', 'migrate', "#{version}_*.rb")).any?
|
||||||
|
version
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_file(path, content)
|
||||||
|
FileUtils.mkdir_p(File.dirname(path))
|
||||||
|
File.write(path, content)
|
||||||
|
end
|
||||||
|
|
||||||
|
def layout_template
|
||||||
|
<<~ERB
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head><title><%= defined?(title) ? title : 'Prism App' %></title></head>
|
||||||
|
<body><%== content %></body>
|
||||||
|
</html>
|
||||||
|
ERB
|
||||||
|
end
|
||||||
|
|
||||||
|
def app_boot_template(name)
|
||||||
|
class_name = Inflector.camelize(name)
|
||||||
|
<<~RUBY
|
||||||
|
require 'prism'
|
||||||
|
|
||||||
|
APP_ROOT = __dir__
|
||||||
|
|
||||||
|
Prism::Orm.connect(File.join(APP_ROOT, 'db', 'development.pstore'))
|
||||||
|
Prism::Orm.migrator(File.join(APP_ROOT, 'db', 'migrate')).migrate!
|
||||||
|
|
||||||
|
Prism::Controller.views_root(File.join(APP_ROOT, 'app', 'views'))
|
||||||
|
Prism::Controller.layout(:application)
|
||||||
|
|
||||||
|
Dir[File.join(APP_ROOT, 'app', 'models', '*.rb')].sort.each { |file| require file }
|
||||||
|
Dir[File.join(APP_ROOT, 'app', 'controllers', '*.rb')].sort.each { |file| require file }
|
||||||
|
|
||||||
|
class #{class_name} < Prism::Application
|
||||||
|
set :views, File.join(APP_ROOT, 'app', 'views')
|
||||||
|
use Prism::Middleware::Logger
|
||||||
|
use Prism::Middleware::ParamsParser
|
||||||
|
use Prism::Middleware::Static, root: File.join(APP_ROOT, 'public')
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
def config_ru_template(name)
|
||||||
|
<<~RUBY
|
||||||
|
require_relative 'app'
|
||||||
|
|
||||||
|
run #{Inflector.camelize(name)}
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
def migration_template(class_name, name, fields)
|
||||||
|
match = name.match(/\Acreate_(.+)\z/i)
|
||||||
|
return plain_migration_template(class_name) unless match
|
||||||
|
|
||||||
|
table = match[1]
|
||||||
|
columns = indent(fields.map { |field| field_line(field) }.join("\n"), 6)
|
||||||
|
<<~RUBY
|
||||||
|
class #{class_name} < Prism::Orm::Migration
|
||||||
|
def up
|
||||||
|
create_table :#{table} do |t|
|
||||||
|
#{columns}
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_table :#{table}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
def plain_migration_template(class_name)
|
||||||
|
<<~RUBY
|
||||||
|
class #{class_name} < Prism::Orm::Migration
|
||||||
|
def up
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
def field_line(field)
|
||||||
|
column, type = field.split(':', 2)
|
||||||
|
"t.#{type || 'string'} :#{column}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def indent(text, spaces)
|
||||||
|
return '' if text.empty?
|
||||||
|
text.lines.map { |line| (' ' * spaces) + line }.join
|
||||||
|
end
|
||||||
|
|
||||||
|
def model_template(class_name)
|
||||||
|
<<~RUBY
|
||||||
|
class #{class_name} < Prism::Orm::Model
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in new issue
Block a user