在Rails默认支架中调用其他生成器 [英] Invoking additional generator in Rails default scaffold
问题描述
在我们的项目中,我们当前正在使用Policy类来允许用户授权.每个模型都有此功能,因此我想将 policy_generator
和 policy_spec_generator
添加到现有的 rails g scaffold
命令中,以便创建相关的模型,控制器,视图和我的新策略文件(包括Policy和spec)都在一个命令中.
In our project, we are currently using Policy classes to allow authorization for users. This is present for every model, so I want to add the policy_generator
and policy_spec_generator
to the existing rails g scaffold
command so it will create the relevant models, controllers, views and my new policy files (both the Policy and spec) all in a single command.
我该如何去做?我最初的想法是调查Railties并使用 lib
文件夹编辑文件,但是我似乎无法弄清楚在什么地方或什么地方添加代码.谢谢!
How do I go about doing this? My initial thought would be to look into Railties and edit the files using the lib
folder, but I can't seem to figure out what or where to add the code. Thanks!
因此,在一天的大部分时间里,我一直在进行试验和测试,并提出了解决方案.我所做的是将rails的 scaffold_generator.rb
的代码复制到我的 lib
BUT 中,该代码必须具有正确的命名空间(在我的情况下, lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
).我敢肯定,在任何Rails项目中它都是相同的路径,但要花点时间来确保.
So I've been trial and testing for a good part of the day and came up with a solution. What I did was to copy the code for rails' scaffold_generator.rb
into my lib
BUT it must be with the correct namespacing (in my case, lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
). I'm sure it'd be the same path in any rails project but give it a few go to make sure.
此外,我注意到修改生成器文件的模式类似于 lib/rails/generators/rails/< generator_folder>/< generator_file> _generator.rb
,它是此模板路径 lib/templates/rails/< generator_folder>/< generator_file> .rb
.一开始这很令人困惑,因为它与Railties或其他gem中的 lib
路径不匹配.
Also, I notice that the patterns for modifying generator files have been like so lib/rails/generators/rails/<generator_folder>/<generator_file>_generator.rb
and it's template in this path lib/templates/rails/<generator_folder>/<generator_file>.rb
. It's pretty confusing at first since it doesn't match the lib
path in Railties or other gems.
至于实际方法本身,这是 scaffold_controller_generator.rb
的副本.我在添加 create_policy_file
方法的位置添加了注释.其余的保持不变.
As for the actual method itself, here's a copy of the scaffold_controller_generator.rb
. I've added a comment where I added my create_policy_file
method. The rest are unchanged.
module Rails
module Generators
class ScaffoldControllerGenerator < NamedBase # :nodoc:
include ResourceHelpers
check_class_collision suffix: "Controller"
class_option :helper, type: :boolean
class_option :orm, banner: "NAME", type: :string, required: true,
desc: "ORM to generate the controller for"
class_option :api, type: :boolean,
desc: "Generates API controller"
class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_controller_files
template_file = options.api? ? "api_controller.rb" : "controller.rb"
template template_file, File.join("app/controllers", controller_class_path, "#{controller_file_name}_controller.rb")
end
# My new method to generate policy files
def create_policy_files
template "policy.rb", File.join("app/policies", controller_class_path, "#{singular_name}_policy.rb")
end
hook_for :template_engine, as: :scaffold do |template_engine|
invoke template_engine unless options.api?
end
hook_for :resource_route, required: true do |route|
invoke route unless options.skip_routes?
end
hook_for :test_framework, as: :scaffold
# Invoke the helper using the controller name (pluralized)
hook_for :helper, as: :scaffold do |invoked|
invoke invoked, [ controller_name ]
end
private
def permitted_params
attachments, others = attributes_names.partition { |name| attachments?(name) }
params = others.map { |name| ":#{name}" }
params += attachments.map { |name| "#{name}: []" }
params.join(", ")
end
def attachments?(name)
attribute = attributes.find { |attr| attr.name == name }
attribute&.attachments?
end
end
end
end
对于一般的规范文件,您甚至可以使用相同的方法将其添加到上面的文件中,即使它不是那么漂亮.然后,我要做的就是将其添加到rspec的 scaffold_generator.rb
中.
For generatig the spec files, you can just add it inside the file above in the same method even but it's not that pretty. What I did then is to add it inside rspec's scaffold_generator.rb
.
这是 scaffold_generator.rb
.命名间隔很重要!矿井位于 lib/rails/generatots/rspec/scaffold/scaffold_generator.rb
.要找出我在哪里得到这个路径,请查看RSpec的repo中的 lib
文件夹,并按照相同的模式将其添加到您的项目中.
Here's the scaffold_generator.rb
. Namespacing it is important! Mine's at lib/rails/generatots/rspec/scaffold/scaffold_generator.rb
. To find out where I get this path, look into RSpec's lib
folder in its repo and follow the same pattern to add it to your project.
require 'generators/rspec'
require 'rails/generators/resource_helpers'
module Rspec
module Generators
# @private
class ScaffoldGenerator < Base
include ::Rails::Generators::ResourceHelpers
source_paths << File.expand_path('../helper/templates', __dir__)
argument :attributes, type: :array, default: [], banner: "field:type field:type"
class_option :orm, desc: "ORM used to generate the controller"
class_option :template_engine, desc: "Template engine to generate view files"
class_option :singleton, type: :boolean, desc: "Supply to create a singleton controller"
class_option :api, type: :boolean, desc: "Skip specs unnecessary for API-only apps"
class_option :controller_specs, type: :boolean, default: false, desc: "Generate controller specs"
class_option :request_specs, type: :boolean, default: true, desc: "Generate request specs"
class_option :view_specs, type: :boolean, default: true, desc: "Generate view specs"
class_option :helper_specs, type: :boolean, default: true, desc: "Generate helper specs"
class_option :routing_specs, type: :boolean, default: true, desc: "Generate routing specs"
class_option :policy_specs, type: :boolean, default: true, desc: "Generate policy specs"
def initialize(*args, &blk)
@generator_args = args.first
super(*args, &blk)
end
def generate_controller_spec
return unless options[:controller_specs]
if options[:api]
template 'api_controller_spec.rb', template_file(folder: 'controllers', suffix: '_controller')
else
template 'controller_spec.rb', template_file(folder: 'controllers', suffix: '_controller')
end
end
def generate_request_spec
return unless options[:request_specs]
if options[:api]
template 'api_request_spec.rb', template_file(folder: 'requests')
else
template 'request_spec.rb', template_file(folder: 'requests')
end
end
def generate_view_specs
return if options[:api]
return unless options[:view_specs] && options[:template_engine]
copy_view :edit
copy_view :index unless options[:singleton]
copy_view :new
copy_view :show
end
def generate_routing_spec
return unless options[:routing_specs]
template_file = File.join(
'spec/routing',
controller_class_path,
"#{controller_file_name}_routing_spec.rb"
)
template 'routing_spec.rb', template_file
end
# My new method to generate policy spec files
def generate_policy_spec
return unless options[:policy_specs]
template_file = File.join(
'spec/policies',
controller_class_path,
"#{singular_name}_policy_spec.rb"
)
template 'policy_spec.rb', template_file
end
protected
attr_reader :generator_args
def copy_view(view)
template "#{view}_spec.rb",
File.join("spec/views", controller_file_path, "#{view}.html.#{options[:template_engine]}_spec.rb")
end
# support for namespaced-resources
def ns_file_name
return file_name if ns_parts.empty?
"#{ns_prefix.map(&:underscore).join('/')}_#{ns_suffix.singularize.underscore}"
end
# support for namespaced-resources
def ns_table_name
return table_name if ns_parts.empty?
"#{ns_prefix.map(&:underscore).join('/')}/#{ns_suffix.tableize}"
end
def ns_parts
@ns_parts ||= begin
parts = generator_args[0].split(/\/|::/)
parts.size > 1 ? parts : []
end
end
def ns_prefix
@ns_prefix ||= ns_parts[0..-2]
end
def ns_suffix
@ns_suffix ||= ns_parts[-1]
end
def value_for(attribute)
raw_value_for(attribute).inspect
end
def raw_value_for(attribute)
case attribute.type
when :string
attribute.name.titleize
when :integer, :float
@attribute_id_map ||= {}
@attribute_id_map[attribute] ||= @attribute_id_map.keys.size.next + attribute.default
else
attribute.default
end
end
def template_file(folder:, suffix: '')
File.join('spec', folder, controller_class_path, "#{controller_file_name}#{suffix}_spec.rb")
end
def banner
self.class.banner
end
end
end
end
希望这也可以帮助那些正在与代码生成作斗争的人!
Hope this helps others who's battling code generation too!
推荐答案
Please have a look into the official guide here https://guides.rubyonrails.org/generators.html#creating-your-first-generator.
简短摘要,在 lib/generators/initializer_generator.rb
中创建一个具有以下内容的文件:
Short summary, create a file in lib/generators/initializer_generator.rb
with this content:
class InitializerGenerator < Rails::Generators::Base
def create_initializer_file
create_file "config/initializers/initializer.rb", "# Add initialization content here"
end
end
并使用 rails生成初始化程序
来执行.
甚至还有一个生成器可以为您执行此操作 https://guides.rubyonrails.org/generators.html#creating-generators-with-generators .
There is even a generator which can do this for you https://guides.rubyonrails.org/generators.html#creating-generators-with-generators.
这篇关于在Rails默认支架中调用其他生成器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!