Class 在 Rails 3 命名空间模型中使用关注点时仅包含一次方法
Class Methods Only Included Once When Using Concerns in Rails 3 with Namespaced Models
我的文件夹结构如下所示:
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"]
.
知道这里发生了什么吗?我在猜测 autoloading/eagerloading,但我不确定为什么这两个模型都没有得到新的 class 方法。
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 对象定义了一个常量。任何人都可以确认吗?奇怪的是,它只是因为没有在 class 上定义 class 方法而默默地失败,尽管它是第二次访问的。不知道为什么?
我认为您有错字,问题包括一些可以让您超越混入限制的魔法。
此外,如果您在已经自动加载的目录下工作,例如 'models',只需将所有内容命名为该目录名称即可。
试试这个:
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 项目,添加了以下文件:
app/models/foo/doer.rb
app/models/foo/thinker.rb
app/models/concerns/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 文件位置和命名空间约定。如果这不起作用,可能是您的项目正在做的其他事情我不知道。
如果您能提供更多详细信息,请告诉我。
根据您的编辑,此代码有问题:
LOOKUP = {
Foo::Bar => "something",
Foo::Baz => "something else"
}
它将在模块完成之前实例化Foo::Bar。因此 new_method
将被省略。这种情况下的传统是在查找中使用字符串,并在需要时将它们常量化以将它们转换为 类。
LOOKUP = {
"Foo::Bar" => "something",
"Foo::Baz" => "something else"
}
然后
LOOKUP.keys.first.constantize.new_method
或
result = LOOKUP[Foo::Bar.name]
使用它。
我的文件夹结构如下所示:
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"]
.
知道这里发生了什么吗?我在猜测 autoloading/eagerloading,但我不确定为什么这两个模型都没有得到新的 class 方法。
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 对象定义了一个常量。任何人都可以确认吗?奇怪的是,它只是因为没有在 class 上定义 class 方法而默默地失败,尽管它是第二次访问的。不知道为什么?
我认为您有错字,问题包括一些可以让您超越混入限制的魔法。
此外,如果您在已经自动加载的目录下工作,例如 'models',只需将所有内容命名为该目录名称即可。
试试这个:
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 项目,添加了以下文件:
app/models/foo/doer.rb
app/models/foo/thinker.rb
app/models/concerns/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 文件位置和命名空间约定。如果这不起作用,可能是您的项目正在做的其他事情我不知道。
如果您能提供更多详细信息,请告诉我。
根据您的编辑,此代码有问题:
LOOKUP = {
Foo::Bar => "something",
Foo::Baz => "something else"
}
它将在模块完成之前实例化Foo::Bar。因此 new_method
将被省略。这种情况下的传统是在查找中使用字符串,并在需要时将它们常量化以将它们转换为 类。
LOOKUP = {
"Foo::Bar" => "something",
"Foo::Baz" => "something else"
}
然后
LOOKUP.keys.first.constantize.new_method
或
result = LOOKUP[Foo::Bar.name]
使用它。