プラグインの改善されたイディオム

この投稿は2009年11月12日に公開されましたが、Railsのプラグイン(だけでなく)も関連性があるため、その関連性は失われていません。



最近、カールと私はプラグインシステムに取り組んでいます。 特に、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/>





このコード

  1. モジュールが含まれるときにClassMethods



    メソッドでクラスが展開されるように、フックをClassMethods



  2. その中で( ClassMethods



    InstanceMethods



    を含むメソッドを宣言します
  3. acts_as_something



    acts_as_something



    をコードで使用できます
これらすべての異常な点は、Rubyが既に持っているモジュールシステムを再発明することです。 それは完全に同一です:



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



ます。



明確にするために、私はこれらのイディオムがいくつかの特別な、高度なケースで必要ではないことを言っていません。 一方、私は、最も一般的な場合、コードで非常に乱雑になり、実際の機能を隠し、ユーザーを行き止まりに導くと言います。



All Articles