在Rails默认支架中调用其他生成器 [英] Invoking additional generator in Rails default scaffold

查看:57
本文介绍了在Rails默认支架中调用其他生成器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我们的项目中,我们当前正在使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆