在Rails默认脚手架中调用额外的生成器 [已关闭]。

问题描述 投票:1回答:1

在我们的项目中,我们目前使用Policy类来允许用户的授权。这在每个模型中都是存在的,所以我想在项目中添加 policy_generatorpolicy_spec_generator 对现有 rails g scaffold 命令,这样它将在一条命令中创建相关的模型、控制器、视图和我的新策略文件(包括策略和规范)。

我该怎么做呢?我最初的想法是查看Railties,然后使用以下命令编辑文件 lib 文件夹,但我似乎不知道该在哪里添加代码。谢谢

更新

于是我试探了大半天,想出了一个办法。我所做的是把rails的代码复制过来。scaffold_generator.rb 进入我 lib 但是 它必须使用正确的命名间距(在我的例子中。lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb). 我相信在任何一个rails项目中都会是同样的路径,但为了确保安全,还是要多去几次。

另外,我注意到修改生成器文件的模式一直是这样的。lib/rails/generators/rails/<generator_folder>/<generator_file>_generator.rb 和它的模板在这个路径 lib/templates/rails/<generator_folder>/<generator_file>.rb. 一开始很困惑,因为它不符合我们的要求。lib 路径中的Railties或其他宝石。

至于实际的方法本身,这里有一份关于 scaffold_controller_generator.rb. 我加了一个评论,在那里我加了我的。create_policy_file 方法。其余的都没有变化。

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.

这是 scaffold_generator.rb. 命名间距很重要! 我的是在 lib/rails/generatots/rspec/scaffold/scaffold_generator.rb. 要知道我从哪里得到这个路径,请看RSpec的 lib 文件夹在其repo中,按照同样的模式将其添加到你的项目中。

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

希望这也能帮助其他正在与代码生成打交道的人!

ruby-on-rails ruby code-generation
1个回答
0
投票

请看这里的官方指南 https:/guides.rubyonrails.orggenerators.html#creating-your-first-generator#。.

简单总结一下,创建一个文件,在 lib/generators/initializer_generator.rb 与此内容。

class InitializerGenerator < Rails::Generators::Base
  def create_initializer_file
    create_file "config/initializers/initializer.rb", "# Add initialization content here"
  end
end

并执行它与 rails generate initializer.

甚至有一个发电机,可以为你做这个。https:/guides.rubyonrails.orggenerators.html#creating-generators-with-generators。.

© www.soinside.com 2019 - 2024. All rights reserved.