开发者

Modules vs. Classes and their influence on descendants of ActiveRecord::Base

Here's a Ruby OO head scratcher for ya, brought about by 开发者_StackOverflowthis Rails scenario:

class Product < ActiveRecord::Base
  has_many(:prices)
  # define private helper methods 
end

module PrintProduct
  attr_accessor(:isbn)
  # override methods in ActiveRecord::Base
end

class Book < Product
  include PrintProduct
end

Product is the base class of all products. Books are kept in the products table via STI. The PrintProduct module brings some common behavior and state to descendants of Product. Book is used inside fields_for blocks in views. This works for me, but I found some odd behavior:

  • After form submission, inside my controller, if I call a method on a book that is defined in PrintProduct, and that method calls a helper method defined in Product, which in turn calls the prices method defined by has_many, I'll get an error complaining that Book#prices is not found.

Why is that? Book is a direct descendant of Product!

More interesting is the following..

As I developed this hierarchy PrintProduct started to become more of an abstract ActiveRecord::Base, so I thought it prudent to redefine everything as such:

class Product < ActiveRecord::Base
end

class PrintProduct < Product
end

class Book < PrintProduct
end

All method definitions, etc. are the same. In this case, however, my web form won't load because the attributes defined by attr_accessor (which are "virtual attributes" referenced by the form but not persisted in the DB) aren't found. I'll get an error saying that there is no method Book#isbn. Why is that?? I can't see a reason why the attr_accessor attributes are not found inside my form's fields_for block when PrintProduct is a class, but they are found when PrintProduct is a Module.

Any insight would be appreciated. I'm dying to know why these errors are occurring!


You might have better luck delaying the attr_accessor call in PrintProduct until mixin-time:

module PrintProduct
  def self.included(base)
    base.attr_accessor :isbn
  end
  # other instance methods here
end

The problem is likely something to do with timing of the attr_accessor call and how that applies to modules mixed in. I'm not certain that the timing is defined by the Ruby spec, so it might vary betweeen implementations or versions.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜