1749718127
2025-06-12 07:22:00
しかし、もし私がこのコードをそのまま実行するなら、私は美しいものになります NoMethodError。なぜ?
私の見解は、のインスタンスを処理しません Teacher もう。彼らはのインスタンスを処理します TeacherDecorator。だから、私が定義されているパブリック方法を呼び出しているとき Teacher、デコレーターは彼らをどうするかを知りません。
私のデコレーターは、独自のパブリック方法と、基礎となるレコードで定義されているパブリック方法の両方を処理できる必要があります(Teacher、 この場合)。
そして、私たちはそれをルビーを使用して行います method_missing。
ルビー method_missing 救助に
method_missing Rubyは、前述のメソッドが定義されていないオブジェクトで作成されたメソッド呼び出しを処理する方法です。 Rubyはメソッド呼び出しを渡します 祖先チェーン 解決するか、上昇するかのいずれかになるまで NoMethodError。
電話するとき @teacher.full_name、私の装飾者に救助してほしい NoMethodError、そしてフォワード #full_name の基礎となるインスタンスへ Teacher。
そのためには、Rubyを再開する必要があります method_missing、カスタム動作を追加してから許可します method_missing 通常のコースを実行します。
class TeacherDecorator
attr_reader :teacher
def initialize(teacher)
@teacher = teacher
end
def availability_as_background
case teacher.max_number_of_students teacher.available_places
when -1 then "background-danger"
when 0 then "background-warning"
when 1 then "background-success"
end
end
private
def method_missing(method, *args, &)
return teacher.public_send(method, *args, &) if teacher.respond_to?(method)
super
end
def respond_to_missing?(name, include_private = false)
teacher.respond_to?(name) || super
end
end
この例では、Rubyの元の署名を保持しています method_missing。
私が微調整する唯一のことは、メソッド呼び出しを基礎となるものに転送することです teacher。私はそれを転送するだけです teacher メソッドに応答します。次に、Rubyに元の動作を再開させます 。
今、 @teacher.full_name 基礎となる事例に適切に転送されます Teacher。
今クールなのは、他のデコレーターがこの動作を共有できるようにすることです。
動作を正規化して他のデコレータを作成します
さまざまなデコレータで共有されているデフォルトの動作を収集する1つの方法は、相続に依存することです。作成できます ApplicationDecorator その仕事は、インスタンス化を処理し、基礎となる記録への転送方法呼び出しを処理することです。
それから、私は私のものを持つことができます TeacherDecorator から継承します ApplicationDecorator。
class ApplicationDecorator
def initialize(record)
@record = record
end
private
attr_reader :record
def method_missing(method, *args, &block)
if record.respond_to?(method)
record.public_send(method, *args, &block)
else
super
end
end
def respond_to_missing?(name, include_private = false)
record.respond_to?(name) || super
end
end
class TeacherDecorator ApplicationDecorator
attr_reader :teacher
def availability_as_background
case teacher.max_number_of_students teacher.available_places
when -1 then "background-danger"
when 0 then "background-warning"
when 1 then "background-success"
end
end
private
alias_method :teacher, :record
end
私の TeacherDecorator 親によって処理されているため、初期化を気にする必要はありません ApplicationDecorator。私が追加した唯一のものは、 record として teacher したがって、どのようなレコードを使用しているかが明確です。
Railsデフォルトの動作がうまく機能することを確認してください
一部のRailsネイティブヘルパーは、私のデコレーターを処理するのに苦労します。
このコードを検討してください:
`edit_teacher_path(@teacher)` # => Should generate teachers/1/edit
しかし、場合 @teacher 私のインスタンスを参照してください TeacherDecorator、生成されたパスはです teachers/#TeacherDecorator/edit。
デコレーターをRailsデフォルトの動作と統合するにはどうすればよいですか?
再び開くことができます to_param (とりわけ)レコードをそのに変える責任のある方法 id、およびその行動を記録に委任します。
class ApplicationDecorator
def initialize(record)
@record = record
end
delegate :to_param, to: :record
private
attr_reader :record
def method_missing(method, *args, &block)
if record.respond_to?(method)
record.public_send(method, *args, &block)
else
super
end
end
def respond_to_missing?(name, include_private = false)
record.respond_to?(name) || super
end
end
もちろん、すべてのRailsデフォルトの動作を基礎となるレコードに転送することは、素晴らしい戦略ではありません(複雑すぎます)。それで、私はそれをどうすればいいですか?
Ruby Standard SimpledeLegatorを使用してください
SimpledeLegator サポートされているすべてのメソッド呼び出しをコンストラクターに渡すオブジェクトに委任する手段を提供します。
これは、SimpleDeLegatorを使用することにより、初期化と委任ロジックを自分から削除できることを意味します。 ApplicationDelegator。
require "delegate"
class ApplicationDecorator SimpleDelegator ; end
すべてが抽象化されています。そして、それはJust Works™です。 @record 私のためにもう利用できません TeacherDecorator 参照するために、しかしSimpledeLegatorはaを公開します __getobj__ それは私の以前とまったく同じように機能します @record Ivar。
最終実装
これが私が終わったことです:
require "delegate"
class ApplicationDecorator SimpleDelegator ; end
class TeacherDecorator ApplicationDecorator
def availability_as_background
case teacher.max_number_of_students teacher.available_places
when -1 then "background-danger"
when 0 then "background-warning"
when 1 then "background-success"
end
end
alias_method :teacher, :__getobj__
end
それでおしまい! 30分間の最小限のデコレーター プレーンルビー。
乾杯、
レミ – @[email protected]
PS:私は 雇用可能。
#30分でRubyで最小限のデコレーターを構築します