Skip to content

Instantly share code, notes, and snippets.

@xiaohui-zhangxh
Last active March 6, 2023 06:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xiaohui-zhangxh/6d1fd342bb1d6ca6f0caf706ff9aaf61 to your computer and use it in GitHub Desktop.
Save xiaohui-zhangxh/6d1fd342bb1d6ca6f0caf706ff9aaf61 to your computer and use it in GitHub Desktop.
Rails 项目在 engines/ 目录下生成子模块 engine
#!/usr/bin/env ruby
name = ARGV[0]
if name.to_s.strip.empty?
puts "usage: bin/create_engine <name>"
exit(1)
end
cmd = <<-CMD
bin/rails plugin new engines/#{name}_engine \
-m plugin_template.rb \
--full \
--database postgresql \
--javascript esbuild \
--css tailwind \
--asset-pipeline propshaft \
--skip-jbuilder \
-T \
--dummy-path spec/dummy
CMD
Dir.chdir(File.expand_path('..', __dir__)) do
system(cmd)
end
# 1. 把这两个文件复制在 Rails 项目目录
# 2. 下载之后,把 create_engine 放入 bin/目录,赋予可执行权限,执行
#
# chmod +x bin/create_engine
#
# 3. 执行以下命令创建 engine
#
# bin/create_engine <name>
#
bin/create_engine cms
# 通过 bin/create_engine xxx 创建的 engine 项目,会使用 plugin_template.rb 作为模板。
engine_name = @name.sub('_engine', '')
remove_dir '.git'
# remove TODO comments
gsub_file "#{@name}.gemspec", %(TODO: Set to 'http://mygemserver.com'), ''
gsub_file "#{@name}.gemspec", %("TODO: Put your gem's public repo URL here."), 'spec.homepage'
gsub_file "#{@name}.gemspec", %("TODO: Put your gem's CHANGELOG.md URL here."), 'spec.homepage'
gsub_file "#{@name}.gemspec", %(spec.homepage = "TODO"), %(spec.homepage = "http://www.tanmer.com")
gsub_file "#{@name}.gemspec", /\s*TODO:\s*/, ''
inject_into_file "#{@name}.gemspec", after: "Gem::Specification.new do |spec|\n" do
" spec.required_ruby_version = '~> 3.2.0'
"
end
append_file '.gitignore', "/node_modules/\n"
# add gems
gem 'rspec-rails'
gem 'factory_bot_rails'
gem 'shoulda-matchers'
gem 'annotate'
gem 'faker'
gem 'guard'
gem 'guard-rspec'
gem 'pry-remote'
# install gems
bundle_path = Gem.bin_path("bundler", "bundle")
require 'bundler'
Bundler.with_unbundled_env do
exec_bundle_command bundle_path, 'install', ''
# config gems
generate 'rspec:install'
generate 'annotate:install'
run 'bundle exec guard init rspec'
end
create_file 'bin/pry-remote', <<~CODE
#!/usr/bin/env ruby
puts "Starting pry-remote, keep watching remote pry..."
while system("pry-remote -w"); end
CODE
chmod 'bin/pry-remote', 0755 # rubocop: disable Style/NumericLiteralPrefix
gsub_file 'spec/rails_helper.rb', "require_relative '../config/environment'", "require_relative './dummy/config/environment'"
inject_into_file 'spec/rails_helper.rb', before: "ActiveRecord::Migration.maintain_test_schema!" do
"ActiveRecord::Migrator.migrations_paths = [File.expand_path('../dummy/db/migrate', __dir__)]
"
end
inject_into_file 'spec/rails_helper.rb', before: "RSpec.configure do |config|" do
"
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
"
end
inject_into_file 'spec/rails_helper.rb', after: "RSpec.configure do |config|\n" do
" config.include FactoryBot::Syntax::Methods
"
end
# add admin route path
inject_into_module "lib/#{@name}.rb", @camelized do
" mattr_accessor :admin_route_path, default: 'admin'
mattr_accessor :admin_base_controller, default: 'ApplicationController'
mattr_accessor :desk_base_controller, default: 'ApplicationController'
"
end
inject_into_file "lib/#{@name}/engine.rb", after: "Engine < ::Rails::Engine\n" do
" engine_name '#{engine_name}'
config.#{@name} = ActiveSupport::OrderedOptions.new
config.generators do |g|
g.test_framework :rspec
g.fixture_replacement :factory_bot
g.factory_bot dir: 'spec/factories'
end
initializer '#{@name}.set_factory_paths', after: 'factory_bot.set_factory_paths' do |_app|
FactoryBot.definition_file_paths += [File.expand_path('../../spec/factories', __dir__)] if defined?(FactoryBotRails)
end
initializer '#{@name}.add_migration_paths', before: :before_initialize do |app|
app.config.paths['db/migrate'] << root.join('db/migrate') unless app.root.to_s.match?(root.to_s)
end
initializer '#{@name}.add_routing_paths', before: :add_routing_paths do |app|
app.config.paths['config/routes'] << root.join('config/routes')
end
initializer '#{@name}.add_assets_paths', before: :before_initialize do |app|
app.config.paths['app/assets'] << root.join('app/assets')
end
config.after_initialize do
config.after_initialize do
%i[admin_route_path admin_base_controller desk_base_controller].each do |key|
#{@camelized}.send \"\#{key}=\", config.#{@name}[key] if config.#{@name}.key?(key)
end
end
end
config.to_prepare do
ActiveSupport.on_load 'models.organization.associations' do
# has_many :communities, dependent: :destroy
end
end
"
end
# rename dummy database names
remove_file "#{dummy_path}/config/database.yml"
create_file "#{dummy_path}/config/database.yml", <<~YAML
default: &default
adapter: postgresql
encoding: unicode
development:
<<: *default
database: #{@name}_dummy_development
test:
<<: *default
database: #{@name}_dummy_test
YAML
# create routes directory
remove_file 'config/routes.rb'
empty_directory 'config/routes'
create_file 'config/routes.rb', <<~RUBY
Rails.application.routes.draw do
# !!! usually you don't need to define route from here.
end
RUBY
create_file "config/routes/#{@name}.rb", <<~RUBY
# place your routes here, usually draw all routes from sub directories
draw '#{@name}/admin'
draw '#{@name}/desk'
draw '#{@name}/portal'
RUBY
empty_directory "config/routes/#{@name}"
create_file "config/routes/#{@name}/admin.rb", <<~RUBY
# place your admin routes here
scope #{@camelized}.admin_route_path do
namespace :#{engine_name} do
namespace :admin, patgh: '' do
# root to: 'dashboard#index'
# resources :users
end
end
end
RUBY
create_file "config/routes/#{@name}/desk.rb", <<~RUBY
# place your desk routes here
namespace :#{engine_name} do
# setup your app resources
root to: redirect("/#{engine_name}/apps")
resources :apps, except: [:show]
namespace :desk, path: ':#{engine_name}_identifier' do
root to: 'dashboard#index'
scope '-' do
# resources :users
end
end
end
RUBY
create_file "config/routes/#{@name}/portal.rb", <<~RUBY
# place your portal routes here
namespace :#{engine_name}, path: '' do
namespace :portal, path: '' do
# get '(/*path)', to: 'pages#show', as: :page
end
end
RUBY
inject_into_file "#{dummy_path}/config/routes.rb", after: "Rails.application.routes.draw do\n" do
" draw '#{@name}'
"
end
create_file "#{dummy_path}/config/initializers/#{@name}.rb", <<~RUBY
Rails.application.config.#{@name}.admin_route_path = '/admin'
RUBY
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment