[英]Class Methods Only Included Once When Using Concerns in Rails 3 with Namespaced Models
I have a folder structure that looks like the following: 我的文件夹结构如下所示:
app/models/
concerns/
quxable.rb
foo/
bar.rb
baz.rb
I'm in Rails 3 so I've autoloaded my concerns with: 我在Rails 3中,所以我自动加载了以下问题:
config.autoload_paths += Dir[Rails.root.join('app', 'models', "concerns", '**/')]
And the files are as follows: 和文件如下:
quxable.rb quxable.rb
module Quxable
extend ActiveSupport::Concern
module ClassMethods
def new_method
end
end
end
bar.rb bar.rb
class Foo::Bar < ActiveRecord::Base
include Quxable
end
baz.rb baz.rb
class Foo::Baz < ActiveRecord::Base
include Quxable
end
Now in the console if do this, I get the following outputs: 现在,如果在控制台中执行此操作,则会得到以下输出:
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
So it would seem to only be properly included on the model that is first accessed. 因此,它似乎只能正确包含在首次访问的模型中。 And yet, If I run the following:
但是,如果我运行以下命令:
ActiveRecord::Base.descendants.select{ |c| c.included_modules.include?(Quxable) }.map(&:name)
I get ["Foo::Bar", "Foo::Baz"]
. 我得到
["Foo::Bar", "Foo::Baz"]
。
Any idea what's going on here? 知道这里发生了什么吗? I'm guessing something with autoloading/eagerloading, but I'm not sure why both models aren't getting the new class method.
我正在猜测自动加载/预先加载的东西,但是我不确定为什么两个模型都没有获得新的类方法。
PS - I've tried rewriting the module without ActiveSupport::Concern
(just because I'm on an old Rails version and I'm taking shots in the dark) using: PS-我尝试使用
ActiveSupport::Concern
(只是因为我使用的是旧版Rails,并且在黑暗中拍摄照片)来重写模块,方法是:
def include(base)
base.send :extend, ClassMethods
end
but I still have the same problem. 但我仍然有同样的问题。
EDIT 编辑
I initially left this out (just trying to present the simplest problem), so I apologize to those trying to help earlier. 最初,我忽略了这一点(只是试图提出最简单的问题),所以我向那些试图提供帮助的人表示歉意。 But
quxable.rb
actually looks like this: 但是
quxable.rb
实际上看起来像这样:
module Quxable
extend ActiveSupport::Concern
LOOKUP = {
Foo::Bar => "something",
Foo::Baz => "something else"
}
module ClassMethods
def new_method
end
end
end
So I'm guessing I created some kind of circular dependency defining a constant with the Class objects. 因此,我猜测我创建了某种圆形依赖关系,用于使用Class对象定义一个常量。 Can anyone confirm?
谁能确认? Weird that it just fails silently by not defining the class methods on the class that's accessed second though.
奇怪的是,它只是没有在第二次访问的类上定义类方法而无声地失败了。 I don't know why that is?
我不知道为什么
Based on your edit, this code is problematic: 根据您的编辑,此代码有问题:
LOOKUP = {
Foo::Bar => "something",
Foo::Baz => "something else"
}
It will instantiate Foo::Bar before the module is complete. 它将在模块完成之前实例化Foo :: Bar。 Thus
new_method
will be omitted. 因此
new_method
将被省略。 Tradition in this case is to use strings in the lookup and .constantize them to turn them into classes when needed. 在这种情况下,传统的做法是在查询中使用字符串,并在需要时使用.constantize将它们转换为类。
LOOKUP = {
"Foo::Bar" => "something",
"Foo::Baz" => "something else"
}
then 然后
LOOKUP.keys.first.constantize.new_method
or 要么
result = LOOKUP[Foo::Bar.name]
to use it. 使用它。
I think you have a typo, and concerns include some magic that lets you transcend the limitations on mix-ins. 我认为您有错别字,而且担心包括一些魔术,可让您超越混入的限制。
Also, if you're working in a directory under something that's already autoloaded, like 'models', just namespace everything to that directory name. 另外,如果您在已经自动加载的目录下工作,例如“模型”,则只需将所有内容命名为该目录名称。
Try this: 尝试这个:
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
As far as I remember you shouldn't need the extra autoload directive, as using the namespace in a directory under models will just work. 据我记得,您不需要多余的autoload指令,因为在模型下的目录中使用名称空间就可以了。
Edit after comments: 评论后编辑:
I've set up a Rails project with the following added files: 我已经用以下添加的文件设置了一个Rails项目:
app/models/foo/doer.rb 应用程序/模型/富/ doer.rb
app/models/foo/thinker.rb 应用程序/模型/富/ thinker.rb
app/models/concerns/thingable.rb 应用程序/模型/关注/ thingable.rb
thingable.rb is: somethingable.rb是:
module Concerns
module Thingable
extend ActiveSupport::Concern
included do
def self.thing
end
end
end
end
doer.rb is: doer.rb是:
module Foo
class Doer < ActiveRecord::Base
include Concerns::Thingable
end
end
thinker.rb is: thinker.rb是:
module Foo
class Thinker < ActiveRecord::Base
include Concerns::Thingable
end
end
In a console: 在控制台中:
Loading development environment (Rails 3.2.22) 加载开发环境(Rails 3.2.22)
2.1.3 :001 > Foo::Doer.respond_to? 2.1.3:001> Foo :: Doer.respond_to? :thing
:事情
=> true =>是的
2.1.3 :002 > Foo::Thinker.respond_to? 2.1.3:002> Foo :: Thinker.respond_to? :thing
:事情
=> true =>是的
2.1.3 :003 > reload! 2.1.3:003>重新加载!
Reloading... 重装...
=> true =>是的
2.1.3 :004 > Foo::Doer.respond_to? 2.1.3:004> Foo :: Doer.respond_to? :thing
:事情
=> true =>是的
2.1.3 :005 > Foo::Thinker.respond_to? 2.1.3:005> Foo :: Thinker.respond_to? :thing
:事情
=> true =>是的
2.1.3 :006 > 2.1.3:006>
I did not change the autoloading at all, I relied on Rails to find files based on namespacing. 我根本没有更改自动加载,而是依靠Rails来基于命名空间查找文件。 (Use a namespace for directories under known directories like 'models')
(将名称空间用于已知目录(例如“模型”)下的目录)
I would reset your autoloading to default, then use Rails conventions for file locations and namespacing. 我会将您的自动加载重设为默认设置,然后对文件位置和命名空间使用Rails约定。 If that doesn't work, there may be other things your project is doing that I don't know about.
如果这不起作用,则可能是您的项目正在做的其他事情我不知道。
Let me know if you can provide any more details. 如果您可以提供更多详细信息,请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.