railsのhash#tryでちょっと躓いたのでメモ
railsでhashにtryをかけているとエラーが出たので、その時に調べた内容をメモ。(一度先輩に教えてもらった気がするのですが完全に忘れてましたorz
本題
この書き方だとhashにkeyが存在する時は問題無いのですが、
{ name: 'yantera' }.try(:fetch, :name) # => "yantera"
hashにkeyが存在しない時はerrorになります
{}.try(:fetch, :name) # KeyError: key not found: :name # from /usr/local/bundle/gems/activesupport-4.1.8/lib/active_support/core_ext/object/try.rb:45:in `fetch'
解決するには以下の2パターンがありました。
- 自分でdefault値を設定する
- 同時に好きな値を定義できる
- []メソッドを設定する
- メリットはシンプルに書ける
{}.try(:fetch, :name, nil) {}.try(:fetch, :name, 1) {}.try(:fetch, :name, 'yantera') # => nil # => 1 # => "yantera" {}.try(:[], :name) # => nil
参考リンク
小ネタ
- tryの中身はこのようになってます
- aにはtryを呼ぶときの引数が配列で入って、bにはブロック文が入ります。
def try(*a, &b) if a.empty? && block_given? yield self else public_send(*a, &b) if respond_to?(a.first) end end
要は下のように呼ぶと
{}.try(:fetch, :test, 1) do p 'test' end # a # => [:fetch, :test, 1] # b # => #<Proc:0x007f5b9bf15f88@(pry):2>
が入ります。
- if文を見るとaがemptyでかつブロック文が投げられている時、自分で作成したブロックぶんが呼ばれるという内容ですね。
{}.try() do p 'test' end # => 'test'
というように使えます。(用途はわからないですが)
続いてelseの方ですが
def try(*a, &b) if a.empty? && block_given? yield self else public_send(*a, &b) if respond_to?(a.first) end end
self(Hash)にa.firstのメソッドがあれば実行されるので
{}.try(:fetch, :test, 1) do p 'test' end # a # => [:fetch, :test, 1]
この場合fetchが呼ばれます。fetchメソッドははhashのkeyをとって実行するので、
{}.fetch(:test, 1) # => 1 {}.fetch(:test) # KeyError: key not found: :test
の形になり、hashの中にkeyが存在していると、そのkeyの値が返りますが、デフォルト値を定義していないとKeyErrorになります。
[]はhashからkeyを取るメソッドなので
{}.[] :test # => nil
という風にかけます。
また、
hoge = {} hoge.[] :test # => nil hoge.default = 1 hoge.[] :test # => 1
hashにdefault値を設定できるみたいで、指定しなければnilが返りますが、指定すると好きな値に出来るみたいでした。