【问题标题】:Best approach to mark specific Rails Models to be used in a module标记要在模块中使用的特定 Rails 模型的最佳方法
【发布时间】:2018-12-25 10:57:44
【问题描述】:

我正在尝试编写一个 lib 插件/扩展来执行我需要知道哪些模型已被标记为与此插件一起使用的操作。

目前,我以acts_as_something 方法的方式标记模型,该方法添加到每个打算与插件一起使用的模型中。

插件的主文件是这样的

# lib/foo.rb
module Foo
  class << self
    attr_accessor :models
  end
  self.models = []

  module Model
    def acts_as_foo
      Foo.models << self
    end
end

ActiveSupport.on_load(:active_record) do
  extend Foo::Model
end

预期用途是然后调用控制器Foo.perform,它需要知道标记的模型才能执行预期的操作,其想法是从Foo.models 获取模型列表。

如果在 development.rb 中将 config.eager_load 设置为 true 时,它​​会按预期工作,否则模型的文件尚未使用/加载,因此 Foo.models 是一个空数组。

我的目标是能够向 Foo 添加更多模型,而无需像这样更改 Foo 的代码。

#app/models/bar.rb
class Bar < ApplicationRecord
  acts_as_foo
end

关于实现这一点的最佳方法的任何想法?

【问题讨论】:

    标签: ruby-on-rails ruby metaprogramming ruby-on-rails-plugins


    【解决方案1】:

    我有一个similar problem before in my gem

    我最终加载了 ONLY 模型文件(这是我的最低要求,因为 DSL 代码就像你的 acts_as_foo 一样在那里),而不是立即加载所有与 Rails 相关的文件文件使用Rails.application.eager_load!

    # lib/foo.rb
    module Foo
      class << self
        attr_accessor :models
      end
    
      self.models = []
    
      class Engine < Rails::Engine
        initializer 'foo.eager_load_models' do |app|
          unless app.config.eager_load
            models_load_path = File.join(Rails.root, 'app', 'models')
    
            # copied from https://apidock.com/rails/Rails/Engine/eager_load%21/class
            matcher = /\A#{Regexp.escape(models_load_path.to_s)}\/(.*)\.rb\Z/
            Dir.glob("#{models_load_path}/**/*.rb").sort.each do |file|
              app.require_dependency file.sub(matcher, '\1')
            end
          end
        end
      end
    
      module Model
        def acts_as_foo
          Foo.models << self
        end
      end
    end
    
    ActiveSupport.on_load(:active_record) do
      extend Foo::Model
    end
    

    【讨论】:

    • 感谢您的回答!它似乎适用于 Railstie,但我需要先按名称引用/使用我的模块,以便它工作。您是否需要使用这种方法运行某种初始化程序?
    • 是的,如果您要更改 lib/foo.rb 文件以使用 Railstie,我会更新我的答案。是的,您可以创建自己的初始化文件或使用config.after_initialize do |app|
    • 我刚刚更新了我的答案以使用 Railstie。未经测试,但我认为这应该已经工作了:)
    • 谢谢,工作就像一个魅力!我将 lib 代码提取到一个 gem 中,它无需运行任何初始化程序就可以工作,而只需要 Foo::Railstie 即可在其中使用您提供的代码加载模型。
    • 没问题! :))
    猜你喜欢
    • 1970-01-01
    • 2016-04-17
    • 1970-01-01
    • 1970-01-01
    • 2010-09-06
    • 2013-10-20
    • 2011-04-13
    • 2013-05-29
    • 1970-01-01
    相关资源
    最近更新 更多