Inheritable Attributesについて
元ネタ:http://railstips.org/2006/11/18/class-and-instance-variables-in-ruby
Ruby on Railsでの実装:http://www.koders.com/ruby/fid1F286C43B89819E4D009281393E1ECAAC371B700.aspx?s=def#L10
クラス変数とクラスのインスタンス変数の中間的なもの。
おさらい:クラス変数の挙動
class Polygon @@sides = 10 def self.sides @@sides end end puts Polygon.sides # => 10 class Rectangle < Polygon @@sides = 4 end puts Polygon.sides # => 4
クラス変数はこのように親/サブクラスで共通なので、サブクラスでの変更が親クラスにも影響してしまう。
おさらい:クラスのインスタンス変数の挙動
class Polygon class << self; attr_accessor :sides end @sides = 8 end class Triangle < Polygon @sides = 3 end puts Triangle.sides # => 3 puts Polygon.sides # => 8
その点、クラスのインスタンス変数は親クラスとサブクラスで異なる値を持てる。
ただし、これには罠があって…
class Octogon < Polygon; end puts Octogon.sides # => nil
継承したクラスには値は伝搬されない。
Inheritable Attributes
ここまでのまとめ兼Inheritable Attributesの立ち位置
クラス変数 | クラスのインスタンス変数 | Inheritable Attributes | |
---|---|---|---|
親クラスの値を継承する | ○ | × | ○ |
親クラス/サブクラス間で異なる値を持つ | × | ○ | ○ |
まーなんか大げさな名前はついてるけど、実際はinherited()呼び出しで「クラスのインスタンス変数」をコピーする様にしているだけ。
という訳でリンク先を参考に作ったのがこれ:
http://websvn.nyaxtstep.com/viewvc.cgi/sandpit/ruby/inheritable_attr/
module InheritableAttr module ClassMethods def inheritable_attr @inheritable_attr ||= {} end def get_inheritable_attr(key) inheritable_attr[key] end def set_inheritable_attr(key, val) inheritable_attr[key] = val end private def inherited(child) child.instance_variable_set('@inheritable_attr', inheritable_attr.dup) end end def self.included(base) base.extend(ClassMethods) end end
使い方はこんな感じ:
require 'inheritable_attr' describe InheritableAttr do it "should handle inheritance correctly" do class Lang include InheritableAttr set_inheritable_attr(:ringo, "unknown") end Lang.get_inheritable_attr(:ringo).should == "unknown" class Unknown < Lang; end Unknown.get_inheritable_attr(:ringo).should == "unknown" class English < Lang set_inheritable_attr(:ringo, "Apple") end English.get_inheritable_attr(:ringo).should == "Apple" class Japanese < Lang set_inheritable_attr(:ringo, "ringo") end Japanese.get_inheritable_attr(:ringo).should == "ringo" English.get_inheritable_attr(:ringo).should == "Apple" Unknown.get_inheritable_attr(:ringo).should == "unknown" Lang.get_inheritable_attr(:ringo).should == "unknown" end end