嵌套类中的Ruby常量。元编程

时间:2011-02-09 22:17:24

标签: ruby-on-rails namespaces metaprogramming nested-class

问:如何在嵌套类中共享有关父类

的信息
class Post
  MODEL = self
  extend PostMod
  has_many :comments, :class_name => 'Post::Comment'

  class Comment
    include CommentMod
    belongs_to :model, :class_name => "Post"
    def method_in_comment
      puts MODEL
    end
  end
  class Another

  end
end
module  CommentMod
  def method_in_mod1
    puts self.class::MODEL
  end
  def method_in_mod2
    puts MODEL
  end
end
module  PostMod
  def method_in_mod1
    puts self::Comment
  end
end
b = Post::Comment.new
b.method_in_comment # Post
b.method_in_mod1 # uninitialized constant Post::Comment::MODEL (NameError)
b.method_in_mod2 # uninitialized constant CommentMod::MODEL (NameError)

这种设计的原因,在这个例子中(实际系统要复杂得多),就是通过“include module”为任何模型添加注释。多数民众赞成将添加控制器,视图和模型方法

所有型号的评论行为都类似。但是,如果需要调整某些内容,模型可以覆盖类Comment中的任何方法。

棘手的部分是,模块对顶级类(Post)和同级别的类(Comment和Another)一无所知,但是他们需要在它们上面调用一些类方法。< / p>

我现在正在使用class.name解析来获取顶级类的类名,但应该有另一种方法。

欢迎任何建议,包括更改设计。

更新

帖子和评论只是一个例子,我的项目中没有这个模型。

我正在从下划线非法(或CamelCase)迁移到嵌套类(从ArticleTranslation到Article :: Translation)。 这看起来对我来说更清楚了。 在previos版本中,我使用模型名称来调用类上的类方法(在ModelTranslation等上) 现在,在重构lib模块之后,我不再需要知道model_name。

但我陷入了陷阱: 在ruby中你可以重新打开类,比如

class Post < ActiveRecord::Base

end
class Post::Comment < ActiveRecord::Base
  belongs_to  :language
end

class Post::Comment::Another1 < ActiveRecord::Base

end

class Post::Comment::Another2 < ActiveRecord::Base

end

class Language < ActiveRecord::Base
  has_many :post_comments, :class_name => "Post::Comment"
end

我有一个问题:如果在服务器启动后立即加载页面 - 一切正常 但是对该页面的下一次调用会引发错误:没有这样的关联;调用包含的模块方法 - 没有方法错误。 我认为,rails为Post :: Coment加载了错误的文件,最糟糕的是我无法调试此错误... 但这是另一个问题。

UPDATE2

第二个问题解决了。问题出在助手班。

3 个答案:

答案 0 :(得分:1)

您能否澄清“仅通过”include module“”为任何模型添加评论。你的意思是你有一个ActiveRecord模型注释,你想多态地将它与许多其他模型相关联,例如。

Post
  has_many :comments

Article
 has_many :comments

...等?

答案 1 :(得分:1)

所以,基于你后来的评论,这里有点像你使用Module.extended之后的东西:

module PostMod
  def self.extended(klass)
    klass.send :include, InstanceMethods
  end

  module InstanceMethods
    def method_in_mod1
      puts "from PostMod#method_in_mod1: #{self.class.inspect}"
    end
  end
end

module CommentMod
  def self.extended(klass)
    klass.send :include, InstanceMethods
  end

  module InstanceMethods
    def method_in_mod1
      puts "from CommentMod#method_in_mod1: #{self.class.inspect}"
    end

    def method_in_mod2
      puts "from CommentMod#method_in_mod2: #{self.class.ancestors.inspect}"
    end
  end
end

class Post
  extend PostMod

  class Comment
    extend CommentMod

    def method_in_comment
      puts "from Post::Comment#method_in_comment: #{self.class.inspect}"
    end
  end

  class Another

  end
end


b = Post::Comment.new
b.method_in_comment # Post::Comment
b.method_in_mod1 # Post::Comment
b.method_in_mod2 # [Post::Comment, CommentMod::InstanceMethods, Object, Kernel]

虽然输出与您的示例不完全相同。可能很难以您尝试的方式共享类变量,因为您实际上并未使用任何继承,只是嵌套类。

您能否详细说明共享这些变量的意图,或许有更好的方法来实现目标?

答案 2 :(得分:1)

我通过重构代码解决了问题。因此不再需要共享class.name。如果我最需要这个是使用class.name.scan或匹配得到顶级类名,那么重构代码并删除class.name.scan =) 分享一个班级名称是一个坏主意。