85
83

More than 3 years have passed since last update.

[Rails] delegateってなに。 solidus

Last updated at Posted at 2018-09-06

delegateをしっかり理解しないまま使っていたので、調べようと思います。

solidus内のdelegate

solidusのmodelからdelegateが使われている場所を抜粋してみます。

product.rb
has_one :master, 
-> { where(is_master: true).with_deleted },
   inverse_of: :product, 
   class_name: 'Spree::Variant', 
   autosave: true 
...
def find_or_build_master 
  master || build_master 
end 
...
MASTER_ATTRIBUTES = [
      :cost_currency,
      :cost_price,
      :depth,
      :height,
      :price,
      :sku,
      :weight,
      :width,
    ]
MASTER_ATTRIBUTES.each do |attr|
  ->delegate :"#{attr}", :"#{attr}=", to: :find_or_build_master
end

->delegate :amount_in,
         :display_amount,
         :display_price,
         :has_default_price?,
         :images,
         :price_for,
         :price_in,
         :rebuild_vat_prices=,
         to: :find_or_build_master

alias_method :master_images, :images

例えば、spree/product.rbから抜粋した上記の部分ですが、いくつかのメソッドまたはテーブルカラムがto: :find_or_build_masterdelegateされています。

delegateとは

[英語の意味]
delegate: 【自動】
権限を委任[委譲・委託・委嘱]する
【他動】
〔人を〕代表[代理]に立てる[として派遣する]
〔権限・任務などを人に〕委任する、委譲する、委託する、委嘱する

APIドックによれば

delegate(*methods, to: nil, prefix: nil, allow_nil: nil)
Provides a delegate class method to easily expose contained objects' public methods as your own.
Options
:to - Specifies the target object
:prefix - Prefixes the new method with the target name or a custom prefix
:allow_nil - if set to true, prevents a Module::DelegationError from being raised
The macro receives one or more method names (specified as symbols or strings) and the name of the target object via the :to option (also a symbol or string).
Delegation is particularly useful with Active Record associations:
[https://api.rubyonrails.org]

使用例

class Aisatsu < ActiveRecord::Base
  def hello
    'hello'
  end

  def goodbye
    'goodbye'
  end
end

class Taro < ActiveRecord::Base
  has_one :aisatsu
  delegate :hello, to: :Aisatsu
end

taro = Taro.new
taro.hello   # => "hello"
taro.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>

というかんじで、delegate はメソッドを異なるクラス間で簡単に使えるようにするマクロ的な使い方ができることが分かります。
上記の例ではAisatsuクラスとTaroクラスが関連付けがされています。
本来なら、taro.aisatsu.helloとしなければならないところを、delegateで直接helloメソッドを使えるようにしています。

イメージとしては
delegate :hello, to: :Aisatsuとしてあげると
hello = (aisatsu.hello)相当として使用できるようになるので、taro.(aisatsu.)helloが使えるようになるという感じでしょうか。
他にもto: 〇〇には、定数to: :CONSTANTや、@インスタンス変数to: :@instanceを当てはめることもできます。

本題に戻る

本題のsolidusのproduct.rbに戻ります。

product.rb
has_one :master, 
-> { where(is_master: true).with_deleted },
   inverse_of: :product, 
   class_name: 'Spree::Variant', 
   autosave: true 
...
def find_or_build_master 
  master || build_master 
end 
...
MASTER_ATTRIBUTES = [
      :cost_currency,
      :cost_price,
      :depth,
      :height,
      :price,
      :sku,
      :weight,
      :width,
    ]
MASTER_ATTRIBUTES.each do |attr|
  ->delegate :"#{attr}", :"#{attr}=", to: :find_or_build_master
end

->delegate :amount_in, 
         :display_amount,
         :display_price,
         :has_default_price?,
         :images,
         :price_for,
         :price_in,
         :rebuild_vat_prices=,
         to: :find_or_build_master

alias_method :master_images, :images

抜粋部分では 二箇所でdelegateが使用されています。
"#{attr}", :"#{attr}="というような複雑な書き方をしているところもありますが、今回はそちらは置いておいてto: :find_or_build_masterという部分に注目します。
:find_or_build_masterproduct.rbで定義されているインスタンスメソッドです。

product.rb
def find_or_build_master 
  master || build_master 
end 

使用例に当てはめてみると、本来なら関係付けを利用してproduct.master.price としなければいけないところを、product.priceと直接productモデルのインスタンスproductから使えるようにしているわけですね。
下のdelegateも同様にproduct.master.imagesとしなければいけないところを、product.imagesと'master'を省略できるようにしているというわけです。

今回は以上になります!

85
83
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
85
83