2

我有一个如下所示的文件夹结构:

app/models/
    concerns/
        quxable.rb
    foo/
        bar.rb
        baz.rb

我在 Rails 3 中,所以我已经自动加载了我的担忧:

config.autoload_paths += Dir[Rails.root.join('app', 'models', "concerns", '**/')]

文件如下:

quxable.rb

module Quxable
    extend ActiveSupport::Concern        

    module ClassMethods
        def new_method
        end
    end
end

bar.rb

class Foo::Bar < ActiveRecord::Base
    include Quxable
end

baz.rb

class Foo::Baz < ActiveRecord::Base
    include Quxable
end

现在在控制台中,如果这样做,我会得到以下输出:

Foo::Bar.respond_to? :new_method #=> true
Foo::Baz.respond_to? :new_method #=> false
reload!
Foo::Baz.respond_to? :new_method #=> true
Foo::Bar.respond_to? :new_method #=> false

所以它似乎只正确地包含在第一次访问的模型中。然而,如果我运行以下命令:

ActiveRecord::Base.descendants.select{ |c| c.included_modules.include?(Quxable) }.map(&:name)

我明白了["Foo::Bar", "Foo::Baz"]

知道这里发生了什么吗?我猜想自动加载/急切加载,但我不确定为什么两个模型都没有获得新的类方法。

PS - 我尝试过重写模块而不使用ActiveSupport::Concern(只是因为我使用的是旧的 Rails 版本并且我在黑暗中拍摄)使用:

def include(base)
    base.send :extend, ClassMethods
end

但我仍然有同样的问题。

编辑

我最初忽略了这一点(只是试图提出最简单的问题),所以我向那些想早点提供帮助的人道歉。但quxable.rb实际上看起来像这样:

module Quxable
    extend ActiveSupport::Concern 

    LOOKUP = {
        Foo::Bar => "something",
        Foo::Baz => "something else"
    }

    module ClassMethods
        def new_method
        end
    end
end

所以我猜我创建了某种循环依赖项,用 Class 对象定义了一个常量。任何人都可以确认吗?奇怪的是,它只是通过没有在第二个访问的类上定义类方法而静默失败。我不知道这是为什么?

4

2 回答 2

2

根据您的编辑,此代码有问题:

LOOKUP = {
    Foo::Bar => "something",
    Foo::Baz => "something else"
}

它将在模块完成之前实例化 Foo::Bar。因此new_method将被省略。在这种情况下,传统是在查找中使用字符串并 .constantize 在需要时将它们转换为类。

LOOKUP = {
    "Foo::Bar" => "something",
    "Foo::Baz" => "something else"
}

然后

LOOKUP.keys.first.constantize.new_method

或者

result = LOOKUP[Foo::Bar.name]

使用它。

于 2015-12-11T18:10:32.900 回答
1

我认为你有一个错字,问题包括一些让你超越混音限制的魔法。

此外,如果您在已经自动加载的目录下工作,例如“模型”,只需将所有内容命名为该目录名称即可。

试试这个:

module Concerns
  module Quxable

    extend ActiveSupport::Concern

    included do
      def self.new_method
      end
    end
  end
end


module Foo
  class Baz < ActiveRecord::Base
    include Concerns::Quxable
  end
end

据我所知,您不需要额外的自动加载指令,因为在模型下的目录中使用命名空间就可以了。


评论后编辑:

我已经使用以下添加的文件设置了一个 Rails 项目:

应用程序/模型/foo/doer.rb

应用程序/模型/foo/thinker.rb

应用程序/模型/关注/thingable.rb

thingable.rb 是:

module Concerns
  module Thingable
    extend ActiveSupport::Concern
    included do
      def self.thing
      end
    end
  end
end

doer.rb 是:

module Foo
  class Doer < ActiveRecord::Base
    include Concerns::Thingable
  end
end

thinker.rb 是:

module Foo
  class Thinker < ActiveRecord::Base
    include Concerns::Thingable
  end
end

在控制台中:

加载开发环境(Rails 3.2.22)

2.1.3 :001 > Foo::Doer.respond_to? :事物

=> 真

2.1.3 :002 > Foo::Thinker.respond_to? :事物

=> 真

2.1.3 :003 > 重新加载!

正在重新加载...

=> 真

2.1.3 :004 > Foo::Doer.respond_to? :事物

=> 真

2.1.3 :005 > Foo::Thinker.respond_to? :事物

=> 真

2.1.3:006 >

我根本没有更改自动加载,我依靠 Rails 根据命名空间查找文件。(对已知目录下的目录使用命名空间,例如“models”)

我会将您的自动加载重置为默认值,然后将 Rails 约定用于文件位置和命名空间。如果这不起作用,那么您的项目可能还有其他我不知道的事情正在做。

让我知道您是否可以提供更多详细信息。

于 2015-12-11T16:51:13.213 回答