コールバックの継承

Rubyは非常に興味深い言語です。 その機能の1つは、クラスにモジュールを追加するときに指定された機能を実行する機能です。 標準的な例は次のとおりです。



module MyModule module InstanceMethods end module ClassMethods end def self.included(base) base.include(InstanceMethods) base.extend(ClassMethods) end end
      
      







ここでは、インスタンスメソッドとクラスメソッドを分離するために、現在のモジュール内に2つのサブモジュールが作成されます。 MyModuleモジュールをクラスに「ミックス」すると、含まれている関数が実行され、必要なクラスメソッドとクラスオブジェクトのメソッドが追加されます。



少し前に、継承中に実行される別の同様の関数を発見しました。



 class Ancestor def self.inherited(successor) end end class Successor < Ancestor end
      
      







私の意見では、状況によっては、この構成は非常に便利な場合があります。たとえば、Ruby on Railsバージョン3.0以降では、継承時にクラス属性をコピーできるInheritableAttributesモジュールがあります。 このモジュールの簡単な使用例を次に示します。



 require "active_support/core_ext/class/inheritable_attributes" class Base class_inheritable_accessor :color end Base.color = "red" class Ancestor < Base end Ancestor.color # => "red" Ancestor.color = "green" Base.color # => "red"
      
      







便利ですか? かなり。 確かに、最近のバージョンのレールでは、inheritable_attributesモジュールは廃止され、class_attributeに置き換えられました。



コールバックの概念が導入されるとすぐに、このコールバックがいつ実行されるかという疑問がすぐに生じます。クラス本体の評価の前または後です。 チェック:



 class Base def self.inherited(m) puts "Hello from inherited callback" end end class NamedClass < Base puts "Hello from class body" end # Output: # Hello from inherited callback # Hello from class body
      
      







つまり コールバックは、クラス本体の評価の前に実行されます。 そして、すべてではありませんが、1つの「しかし」ではありません。ルビーには、名前付きクラスとともに、次のように宣言される匿名クラスもあります。



 anonymous_class = Class.new(Base) do # body end
      
      







よく知られているgem active_attrを移植するとき私はそのような機能に出くわしました:rubyで実行しているとき、すべての仕様はruby 1.9でうまく動作しますが、rvmに古き良きRuby 1.8.7(またはree)を使用するように頼むと、テストの半分は明確な理由なしに落ち始めます(コミットする前はRails 3.0がサポートされていなかったため、すべての仕様はrubyブランチ1.9とrubyブランチ1.8の両方で正常に機能していました)



判明したように、その理由は次のルビー1.8.7機能です。



 class Base def self.inherited(m) puts "--> Hello from inherited callback" end end puts "declare named class" class NamedClass < Base puts "--> Hello from named class" end puts puts "declare anonymous class" Class.new(Base) do puts "--> Hello from anonymous class" end # Output for ruby 1.9.3 # declare named class # --> Hello from inherited callback # --> Hello from named class # # declare anonymous class # --> Hello from inherited callback # --> Hello from anonymous class # Output for ruby 1.8.7 # declare named class # --> Hello from inherited callback # --> Hello from named class # # declare anonymous class # --> Hello from anonymous class # --> Hello from inherited callback
      
      







ruby 1.8.7では、匿名クラスの場合、最初に本文が評価され、次に継承されたコールバックが実行されます。 したがって、空のクラスを予期するInheritableAttributesモジュールは、いくつかのメソッドに遭遇し、正しく動作しません。



この問題を解決するにはどうすればよいですか?

1. ruby​​ 1.9に移動します

2.継承されたコールバックに基づく機能を使用しないでください(この機能を明示的に使用せず、レールのフレームワーク内でのみ使用する場合は、レール3.1に切り替えるだけで十分です)。

3.匿名クラスを使用しないでください

4.最初に空の匿名クラスを作成し、次にclass_evalを使用して目的のコンテンツを追加します。



 c = class.new(Base) c.class_eval do # body end
      
      







結論として、説明したバグは非常に具体的であり、誰もがそれに遭遇するわけではありませんが、一方で、ruby 1.8.7のこの機能を知っているにもかかわらず、説明された状況に陥るので、デバッグに数時間節約できます。



All Articles