简体   繁体   English

在Rails 3和命名空间模型中使用关注点时,仅包含类方法一次

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM