最近、カールと私はプラグインシステムに取り組んでいます。 特に、Railsプラグインガイドを確認しました。 ガイドを読むと、そこに提示されているイディオムの多くの過剰に気付きました。
私はガイドの作者を非難しません。 これらのイディオムは、Railsの初期の頃から使用されていたものとまったく同じです。 一方、それらを見て、そのようなコードを見たときに、Rubyには魔法の呪文がいっぱいで、比較的単純なことには特別な儀式(タンバリンと踊るなど)が必要だったように思えた当時を思い出しました。
以下に例を示します。
Copy Source | Copy HTML<br/> module Yaffle<br/> def self .included(base)<br/> base.send :extend, ClassMethods<br/> end <br/> <br/> module ClassMethods<br/> # , , Hickwall <br/> def acts_as_something <br/> send : include , InstanceMethods<br/> end <br/> end <br/> <br/> module InstanceMethods<br/> # , , @hickwall <br/> end <br/> end <br/>
まず、送信はまったく必要ありません。
acts_as_something
メソッドはクラス自体で呼び出され、プライベートな
include
メソッドへのアクセスを許可します。
このコードは次のように使用されます。
Copy Source | Copy HTML<br/> class ActiveRecord::Base <br/> include Yaffle<br/> end <br/> <br/> class Article < ActiveRecord::Base <br/> acts_as_yaffle<br/> end <br/>
このコード
- モジュールが含まれるときに
ClassMethods
メソッドでクラスが展開されるように、フックをClassMethods
- その中で(
ClassMethods
)InstanceMethods
を含むメソッドを宣言します -
acts_as_something
、acts_as_something
をコードで使用できます
Copy Source | Copy HTML<br/> module Yaffle<br/> # , , Hickwall <br/> def acts_as_something <br/> send : include , InstanceMethods<br/> end <br/> <br/> module InstanceMethods<br/> # , , @hickwall <br/> end <br/> end <br/>
後で使用するには:
Copy Source | Copy HTML<br/> class ActiveRecord::Base <br/> extend Yaffle<br/> end <br/> <br/> class Article < ActiveRecord::Base <br/> acts_as_yaffle<br/> end <br/>
一言で言えば、
include
をオーバーライドしても意味がなく、Rubyに両方がある場合は
extend
ように動作します!
できること:
Copy Source | Copy HTML<br/> module Yaffle<br/> # , , @hickwall, <br/> # , ! <br/> end <br/> <br/>
後で使用するには:
Copy Source | Copy HTML<br/> class Article < ActiveRecord::Base <br/> include Yaffle<br/> end <br/> <br/>
実際、最初のコード(モジュールをさらに含む
extend
を介してクラスを
extend
するためのインクルードのオーバーライドフック)は、Rubyへのシンプルなインクルードを取り巻く2つの抽象化層です!
さらにいくつかの例を見てみましょう。
Copy Source | Copy HTML<br/> module Yaffle<br/> def self .included(base)<br/> base.send :extend, ClassMethods<br/> end <br/> <br/> module ClassMethods<br/> def acts_as_yaffle (options = {})<br/> cattr_accessor :yaffle_text_field<br/> self .yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s<br/> end <br/> end <br/> end <br/> <br/> ActiveRecord::Base .send : include , Yaffle <br/>
繰り返しますが、
include
overrideイディオムは、
extend
!を呼び出すだけではなく、
extend
ように動作します。
ソリューションの方が優れています:
Copy Source | Copy HTML<br/> module Yaffle<br/> def acts_as_yaffle (options = {})<br/> cattr_accessor :yaffle_text_field<br/> self .yaffle_text_field = options[:yaffle_text_field].to_s || "last_squawk" <br/> end <br/> end <br/> <br/> ActiveRecord::Base .extend Yaffle <br/>
この場合、通常の拡張ではカプセル化できない追加オプションを提供するため、
acts_as_yaffle
を使用
acts_as_yaffle
必要があります。 (不思議なフレーズ。オリジナル: この場合、通常のRuby拡張を使用してカプセル化できない追加オプションを提供しているため、acts_as_yaffleを使用するのが適切です 。
別の「より高度な」ケース:
Copy Source | Copy HTML<br/> module Yaffle<br/> def self .included(base)<br/> base.send :extend, ClassMethods<br/> end <br/> <br/> module ClassMethods<br/> def acts_as_yaffle (options = {})<br/> cattr_accessor :yaffle_text_field<br/> self .yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s<br/> send : include , InstanceMethods<br/> end <br/> end <br/> <br/> module InstanceMethods<br/> def squawk (string)<br/> write_attribute( self . class .yaffle_text_field, string.to_squawk)<br/> end <br/> end <br/> end <br/> <br/> ActiveRecord::Base .send : include , Yaffle <br/>
再度、オーバーライドを含めて
extend
を実行し、
send
を呼び出し
send
、これは必須ではありません。 同一の機能:
Copy Source | Copy HTML<br/> module Yaffle<br/> def acts_as_yaffle (options = {})<br/> cattr_accessor :yaffle_text_field<br/> self .yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s<br/> include InstanceMethods<br/> end <br/> <br/> module InstanceMethods<br/> def squawk (string)<br/> write_attribute( self . class .yaffle_text_field, string.to_squawk)<br/> end <br/> end <br/> end <br/> <br/> ActiveRecord::Base .extend Yaffle <br/>
もちろん、これを行うことができます:
Copy Source | Copy HTML<br/> module Yaffle<br/> def squawk (string)<br/> write_attribute( self . class .yaffle_text_field, string.to_squawk)<br/> end <br/> end <br/> <br/> class ActiveRecord::Base <br/> def self .acts_as_yaffle(options = {})<br/> cattr_accessor :yaffle_text_field<br/> self .yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s<br/> include Yaffle<br/> end <br/> end <br/>
モジュールは常に
ActiveRecord::Base
含まれているため、モジュールを追加し、
extend
を使用
extend
以前のコード
extend
、クラスを再度
acts_as_yaffle
、
acts_as_yaffle
メソッドを直接追加するよりも悪く
extend
ません。 これで、
Yaffle
メソッドを
Yaffle
モジュール内に
Yaffle
でき、そこから簡単に迷子になります。
たぶんこれはそれほど重要ではないかもしれませんが、プラグイン作成テンプレートの不正な魔法の量は著しく減少し、ユーザーがアクセスしやすくなります。 さらに、新しいユーザーは、プラグインが機能するために、魔法の呪文、
send
の使用、
ClassMethods
などの特別なモジュールの必要性の誤った印象を与える
extend
なく、
include
および
extend
作業をすばやく理解することが
send
ます。
明確にするために、私はこれらのイディオムがいくつかの特別な、高度なケースで必要ではないことを言っていません。 一方、私は、最も一般的な場合、コードで非常に乱雑になり、実際の機能を隠し、ユーザーを行き止まりに導くと言います。