Rails:self.inherited回调和急切的类加载(不是非常渴望...)

时间:2011-07-24 21:52:08

标签: ruby-on-rails ruby metaprogramming lazy-loading eager-loading

设置

在我的gem中有一个Component基类:

module Component
  class Base
    def self.inherited(component_class)
      Component.class_eval do
        define_method(component_class.to_s.underscore) do |*args, &block|
          component_class.new(*args, &block)
        end
      end
    end
  end
end

对于从此基类继承的每个类(例如FancyComponent < Component::Base),应在Component模块中定义一个帮助器(例如fancy_component)。

这适用于随我的宝石一起提供的任何组件,即Component.instance_methods.include? :fancy_component #=> true

现在Rails发挥作用

我希望我的gem用户能够添加组件。这些内容存储在app/components

此文件夹包含在以下所有内容中:

  • MyApp的:: Application.config.load_paths
  • MyApp的:: Application.config.autoload_paths
  • MyApp的:: Application.config.eager_load_paths

新组件UserComponent < Component::Base存储在app/components/user_component.rb

问题

如果我启动rails控制台,情况如下:

Loading development environment (Rails 3.0.4, ruby-1.9.2-p0)  
Component.instance_methods.include? :fancy_component   #=> true 
Component.instance_methods.include? :user_component    #=> false
UserComponent                                          #=> UserComponent
Component.instance_methods.include? :user_component    #=> true

因此,在以某种方式访问​​组件类之前,辅助方法不可用。

那么如何强制加载该类以便继承执行?

2 个答案:

答案 0 :(得分:0)

你的想法很好但是我非常建议你不要实现这样的东西,`这通常是通过在Rails注意到它们之前预加载模型来完成的,这可能导致难以计算加载问题你的应用程序(就像应该重新加载但不是的类)。

这是一个基本的例子,你实现这个功能的方法是,在你的“root”gem文件中(如果你的gem命名为“my_component”,“lib / my_component.rb”文件)做类似的事情这样:

require 'component/base/component'
# require here all other classes necessary for your gem

rb_files = File.join( Rails.root, 'app', 'components', '**', '*.rb' )
Dir.glob( rb_files ).each do |file|
  require file.gsub('.rb')
end

有了这个你将加载“app / components”下的所有文件,但是Rails不会重新加载这些对象,因为它们不是Rails所要求的,而是你自己的宝石。如果您不介意无法更改这些文件,这可能没问题,但是您可能会在开发环境(或任何“cache_classes”设置为false的环境)中遇到问题。

答案 1 :(得分:0)

这是rails中的一个错误(但是wontfix),请参阅此票证:

https://github.com/rails/rails/issues/3364