JT's Blog

Do the right things and do the things right.

Ruby Tap / Fetch / Dig / Try

| Comments

Tap

source code 可以看出最後會傳回 self

以下分成兩個用途,分別介紹如何運用此特性

用途一:調測

在使用 chain 時,要查看某個過程的內容,可以使用 #tap 印出內容,卻又不會影響執行

1
2
3
4
(1..10)                .tap {|x| puts "original: #{x.inspect}"}
  .to_a                .tap {|x| puts "array: #{x.inspect}"}
  .select {|x| x%2==0} .tap {|x| puts "evens: #{x.inspect}"}
  .map { |x| x*x }     .tap {|x| puts "squares: #{x.inspect}"}

用途二:簡化代碼

可用在建立一個方法,要傳回一個 String/Array/Hash 的結果

1
2
[].tap {|i| i << "abc"}
''.tap {|i| i << do_some_thing }

Reference

Fetch

取不到值時,會以預設值取代,但沒設定預設值會出現 Error

Hash / Array 皆有實作 #fetch

Array#fetch

1
2
3
4
5
6
a = [ 11, 22, 33, 44 ]
a.fetch(1)               #=> 22
a.fetch(-1)              #=> 44
a.fetch(4, 'cat')        #=> "cat"
a.fetch(4) { |i| i*i }   #=> 16
a.fetch(5)               #=> IndexError

Hash#fetch

1
2
3
4
5
h = { "a" => 100, "b" => 200 }
h.fetch("a")                            #=> 100
h.fetch("z", "go fish")                 #=> "go fish"
h.fetch("z") { |el| "go fish, #{el}"}   #=> "go fish, z"
h.fetch("z")                            #=> KeyError

Reference

Dig

適用在多層

跟 #try 一樣,如果其中一層為 nil,會回傳 nil 而不會出現 Error

Ruby 2.3.0

Hash / Array 皆有實作 #dig

Array#dig

1
2
3
4
a = [[1, [2, 3]]]

a.dig(0, 1, 1)                    #=> 3
a.dig(0, 0, 0)                    #=> nil

Hash#dig

在 rails 取 params 時,會使用以下方式取值

1
name = params[:company][:owner][:name]

要避免遇到 nil,可改寫成

1
name = params[:company][:owner][:name] if params[:company] && params[:company][:owner] && params[:company][:owner][:name]

使用 #dig 處理

1
name = params.dig(:company, :owner, :name)

同樣 case 用 #try 的 chain,看來使用 #dig 比較適合

1
params.try(:[], :company).try(:[], :owner).try(:[], :name)

Reference

try

同場加映,#try 是屬於 Rails Object method

等同 Object#send,但 object 為 nil 時,呼叫 method 會出現 NoMethodError exception

但使用 #try,object 為 nil 時,會回傳 nil ,而不會出現 NoMethodError exception

因此是一個相當實用的功能

最常見的例子像是我們想判斷某些動作只有管理員可以進行操作,因此我們通常會這樣寫

1
2
3
if current_user.is_admin?
    # do something
end

但總會遇到 current_user 有 nil 的時候,這時就適合用 #try 確認 current_user 是否為 nil,再去呼叫 #is_admin?

1
2
3
if current_user.try(:is_admin?)
    # do something
end

#try 也可以實現 block,如果 instance object 不為 nil

1
@person.try { |p| "#{p.first_name}" "#{p.last_name}" }

Reference

Comments