如何动态生成关联名称?

时间:2012-09-26 15:14:33

标签: ruby-on-rails ruby ruby-on-rails-3 refactoring squeel

我正在使用Ruby on Rails 3.2.2和Squeel gem。我有以下声明,我正在尝试重构 Mixin模块中的my_squeel_query方法(因为它被我的许多模型使用):

# Note: 'article_comment_associations' and 'model_as_like_article_comment_associations'
# refer to database table names.

class Article < ActiveRecord::Base
  def my_squeel_query
    commenters.
      .where{
        article_comment_associations.article_id.eq(my{self.id}) & ...
      }
  end
end

class ModelAsLikeArticle < ActiveRecord::Base
  def my_squeel_query
    commenters.
      .where{
        model_as_like_article_comment_associations.article_id.eq(my{self.id}) & ...
      }
  end
end

我的问题是我可以通过在Mixin模块中生成动态名称来重构article_comment_associationsmodel_as_like_article_comment_associations语句。也就是说,如果那是String,我可以使用"#{self.class.to_s.singularize}_comment_associations"之类的内容动态生成相关名称,如下所示:

class Article < ActiveRecord::Base
  include MyModule
end

class ModelAsLikeArticle < ActiveRecord::Base
  include MyModule
end

module MyModule
  def my_squeel_query
    commenters.
      .where{
        # Note: This code doesn't work. It is just an sample.
        "#{self.class.to_s.singularize}_comment_associations".article_id.eq(my{self.id}) & ...
      }
  end
end

但是,由于不是我的情况,我无法“建立”名称并使my_squeel_query在模型之间“共享”。

如何动态生成与Squeel gem相关的关联名称?我应该考虑以另一种方式进行重构吗?你有什么建议?

3 个答案:

答案 0 :(得分:1)

如果您动态生成方法,则可以执行此操作。为此目的提供了Module.included方法:

module ModuleAsLikeArticle
  def self.included(base)
    base.send(:define_method, "#{base.to_s.singularize}_comment_associations") do
      # ...
    end
  end
end

使用include导入模块时会触发此操作,并允许您创建专门为此定制的方法。

作为注释,您可能希望使用base.name.underscore.singularize来获得更易读的方法名称。按照惯例,方法名称中不应包含大写字母,尤其不应作为第一个字符。

传统的Rails类型应用程序使用不同的方法,而是定义一个可用于按需创建这些类的方法:

module ModuleAsLikeArticle
  def has_comments
    base.send(:define_method, "#{base.to_s.singularize}_comment_associations") do
      # ...
    end
  end
end

这将使用如下:

class ModelAsLikeArticle < ActiveRecord::Base
  extend MyModule

  has_comments
end

由于在调用has_comments之前不会创建该方法,因此您可以安全地扩展ActiveRecord::Base,然后在需要该功能的所有类中插入适当的调用。

答案 1 :(得分:1)

由于DSL是instance_evaled,你实际上可以这样说:

def my_squeel_query
  base = self
  commenters.
    .where{
      # Note: This code does work. Because it's awesome.
      __send__("#{base.class.to_s.singularize}_comment_associations").
        article_id.eq(my{self.id})
    }
end

答案 2 :(得分:0)

我想你可能会在Rails Reflection课程中找到你需要的东西(http://api.rubyonrails.org/classes/ActiveRecord/Reflection/ClassMethods.html),如页面所示,它允许您可以询问有关其关联和聚合的ActiveRecord类。