日本語版
最新ニュース
科学&テクノロジー

30分でRubyで最小限のデコレーターを構築します

タグ。 しかし、もし私がこのコードをそのまま実行するなら、私は美しいものになります 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…

30分でRubyで最小限のデコレーターを構築します

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で最小限のデコレーターを構築します

執筆者について: nipponese

Nipponese News編集部は、国内外のニュースを日本語で分かりやすくお届けします。