[Rails] delegateってなに。　solidus

delegateをしっかり理解しないまま使っていたので、調べようと思います。
##solidus内のdelegate
solidusのmodelから`delegate`が使われている場所を抜粋してみます。
 
```ruby: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_master`に `delegate`されています。

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

[APIドック](https://api.rubyonrails.org)によれば
>
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]



###使用例

```ruby
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に戻ります。

```ruby: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_master`は`product.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'を省略できるようにしているというわけです。

今回は以上になります!