JT's Blog

Do the right things and do the things right.

Rails Delegate

| Comments

屬於 ActiveSupport Delegate method

主要運用在 activerecord associations

Spec

1
delegate(*methods) public

Options

  • :to 指定目標物件
  • :prefix 設為true 就是以 :to 的物件名稱當作前綴字,或是自行設定前綴字
  • :allow_nil 設為 true 不會產生 NoMethodError Exception,預設為 false

Description

Provides a delegate class method to easily expose contained objects’ public methods as your own.

The target method must be public, otherwise it will raise NoMethodError.

Delegation Design Pattern

Delegation 設計模式是讓物件曝露一些性質,但這些性質其實是取用與當前實例相關連的其它實例的性質而來的

Explanation of Rails Delegate

根據以下範例瞭解 Rails Delegate 運作方式。建議可以查看 delegation.rb 瞭解實作方式

1
2
3
4
5
6
7
class Post < ApplicationRecord
  belongs_to :user
end

class User < ApplicationRecord
  has_many :posts
end

根據以上的關聯,要如何設計呼叫 @post.name 可直接取得 @post.user.name 又不會出現 NoMethodError 呢?

1.自行寫 #name 裡面在使用 #try 取 user.name

1
2
3
4
5
6
7
class Post < ApplicationRecord
  belongs_to :user

  def name
    user.try(:name)
  end
end

2.以 #delegate 表示

注意:要加prefix,才會有作用

1
2
3
4
class Post < ApplicationRecord
  belongs_to :user
  delegate :name, to: :user, :prefix => true, allow_nil: true
end

Practice

一般在寫 Rails 會直覺使用 associations 取得資料

1
2
3
4
5
6
7
8
9
# models
class Invoice < ApplicationRecord
 belongs_to :user
end

# views
<%= @invoice.user.name %>
<%= @invoice.user.address %>
<%= @invoice.user.cellphone %>

若比較嚴謹來看,這樣是違反 Lask Knowledge Priciple

而且 Views 看起來也不漂亮

這時可透過 Rails Delegate 優化

1
2
3
4
5
6
7
8
9
10
11
12
13
# models
class Invoice < ApplicationRecord
 belongs_to :user
 delegate :name, :address, :cellphone,
 to: :user,
 prefix: true,
 allow_nil: true
end

# views
<%= @invoice.user_name %>
<%= @invoice.user_address %>
<%= @invoice.user_cellphone %>

這樣看起來是不是清爽多了~

Others

參考 Ruby Forwardable

Reference

Comments