如何构造命名空间模块

时间:2016-01-29 15:32:18

标签: ruby-on-rails-4 rubygems rails-engines

我在Rails引擎gem的上下文中遇到了Ruby类(常量)查找的问题。

我有一个宝石MyGem,它是一个Rails引擎。它定义了预期被MainApp覆盖的非命名空间模型,其中包括gem和命名空间模块,这些模块包含在gem和main_app的模型中,以便以DRY方式定义可重用代码。

以下是一个示例代码结构:

两种模式

# in /app/models/user.rb        
class User < ActiveRecord::Base
  include MyGem::User::CommonExt
end

# in /app/models/comment.rb        
class Comment < ActiveRecord::Base
  include MyGem::Comment::CommonExt
end

他们的两个模块

# in /app/models/concerns/my_gem/user/common_ext.rb
module MyGem::User::CommonExt
  def load_comment(id)
    return Comment.find(id)
  end
end

# in /app/models/concerns/my_gem/comment/common_ext.rb
module MyGem::Comment::CommonExt
  def load_user(id)
    return User.find(id)
  end
end

现在,如果我打电话

User.new.load_comment(1)

我得到undefined method #find for MyGem::Comment::Module

我想我理解为什么会这样 - 在#load_comment定义的上下文中,MyGem下的命名空间,Comment常量查找返回MyGem :: Comment,而不是更多遥远的::Comment

我不希望在::之前添加每个模型实例 是否有文件结构,模型/类定义或配置更改我可以用来调用Comment返回模型Comment,而不是MyGem :: Comment模块?

1 个答案:

答案 0 :(得分:0)

在这种情况下,我会使用继承而不是mixin。

因此,在您的gem /引擎中,您可以定义与此类似的公共类:

module MyGem
  module Common
    class Base < ActiveRecord::Base
      # common functionality goes here
      def load(record_type, id)
        record_type.find(id)
      end
    end
  end
end

然后在您的main_app代码中

class User < MyGem::Common::Base
  ...
end

现在你可以这样做:

User.new.load(Comment, 1)

这违反了Law of Demeter,但希望它说明了这一点。

这样做是干的,并且还有一个额外的好处,就是它可以防止你的宝石必须知道超出它自己范围的类。