class_inheritable_accessorからclass_attributeに移行する際の注意点
rails3.1でclass_inheritable_accessorがdeprecatedになりました。
warningメッセージ
class_inheritable_attribute is deprecated, please use class_attribute method instead. Notice their behavior are slightly different, so refer to class_attribute documentation first
代わりにclass_attributeを使えとのことなのですが単純置換してはだめな場合がありました。
問題点
配列等に値を追加する際に破壊的メソッドで変更すると問題がでます。(API Docに載ってます)
class_inheritable_accessorでは問題ありません。
解決方法
変数名=でコピーされた値を代入する。
サンプル
class Base class_attribute :setting end class Subclass < Base end Base.setting = [] Base.setting => [] Subclass.setting => [] # <<で破壊的に追加すると親クラスにも影響がでる。問題!! Subclass.setting << :foo Base.setting => [:foo] Subclass.setting => [:foo] # += [値]で適用すると問題なし。 Base.setting = [] Subclass.setting += [:foo] # このタイミングで=が呼ばれてSubclassに読み取り用アクセサメソッドが動的に定義される Base.setting => [] Subclass.setting => [:foo] # 次のハッシュのテストのために配列テストで定義されたSubclassのsettingメソッドを消す。 Subclass.singleton_class.class_eval do remove_possible_method(:setting) end # ハッシュの場合はmerge!の代わりにmergeを使う。肝は=メソッドを使うことにあり。=で設定することでSubclassに読み取り用アクセサメソッドが動的に定義される Base.setting = {} Subclass.setting.merge!(:foo => "test") Base.setting => {:foo => "test"} Subclass.setting => {:foo => "test"} Base.setting = {} Subclass.setting = Subclass.setting.merge(:foo => "test") Base.setting => {} Subclass.setting => {:foo => "test"}
実装の違い
- class_inheritable_accessor
- 値をクラスインスタンス変数のハッシュで保持。継承する際にコピーしている。
- class_attribute
- =メソッドで代入された際にそのクラスに読み取り用アクセサメソッドが動的に定義される。
- あとは通常のクラス継承の仕組みで動く、Subclassで定義した場合にはそれが呼ばれ、定義してない場合はBaseクラスのメソッドが呼ばれる。
- そのためパフォーマンスが良くなった。