delegateをしっかり理解しないまま使っていたので、調べようと思います。
##solidus内のdelegate
solidusのmodelからdelegate
が使われている場所を抜粋してみます。
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ドックによれば
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に戻ります。
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
で定義されているインスタンスメソッドです。
def find_or_build_master
master || build_master
end
使用例に当てはめてみると、本来なら関係付けを利用してproduct.master.price
としなければいけないところを、product.price
と直接productモデルのインスタンスproduct
から使えるようにしているわけですね。
下のdelegate
も同様にproduct.master.images
としなければいけないところを、product.images
と'master'を省略できるようにしているというわけです。
今回は以上になります!