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

この投稿は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. その中で( ClassMethodsInstanceMethodsを含むメソッドを宣言します
  3. acts_as_somethingacts_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_yaffleacts_as_yaffleメソッドを直接追加するよりも悪くextendません。 これで、 YaffleメソッドをYaffleモジュール内にYaffleでき、そこから簡単に迷子になります。

たぶんこれはそれほど重要ではないかもしれませんが、プラグイン作成テンプレートの不正な魔法の量は著しく減少し、ユーザーがアクセスしやすくなります。 さらに、新しいユーザーは、プラグインが機能するために、魔法の呪文、 sendの使用、 ClassMethodsなどの特別なモジュールの必要性の誤った印象を与えるextendなく、 includeおよびextend作業をすばやく理解することがsendます。

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

Source: https://habr.com/ru/post/J87651/


All Articles