Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

Ruby | Collecting Inputs | Use #fetch to assert the presence of Hash keys

概要

Use #fetch to assert the presence of Hash keys

前提

Confident Rubyではメソッド内の処理を次のように分類しています。
・Collecting Inputs(引数チェック、変換など)
・Performing Work(主処理)
・Delivering Output(戻り値に関わる処理)
・Handling Failure(例外処理)

当記事は上記のうち、Collecting Inputsに関する話です。

詳細

状況

メソッドがHashのパラメータをとる場合。
例えば、Hashのキーが適切な内容を要求する場合。

概要

Hash#fetchによって、要求されるキーの存在を表明する

理由

#fetchは簡潔で、冪等性があり、nilやfalseなどを正当な値として扱うHashのバグを避ける。

サンプルコード仕様

Personクラスは名前と年齢をHashで受け取り初期化するクラスです。

名前は文字列
年齢は数値

を主に受け取りますが、falseを指定した場合は
名前なら正体不明
年齢なら年齢不詳
として正常な値として取り扱います。

サンプルコード(適用前)

ハッシュの存在チェックをif(unless)で行うと、nilやfalseなどfalseyな値を
不正な値として判断してしまいます。

require 'pp'

class Person
  def initialize(attributes)
    @name = attributes[:name]
    fail ArgumentError, "Name must be supplied" unless @name
    @age = attributes[:age]
    fail ArgumentError, "Age must be supplied" unless @age
  end
end

tanaka = Person.new({name: "tanaka", age: 34})
pp tanaka

begin
  suzuki = Person.new({name: "suzuki", age: nil})
rescue => e
  puts e.to_s
end

begin
  unknown = Person.new({name: false, age: 55})
rescue => e
  puts e.to_s
end

出力

#<Person:0x2a2a330 @age=34, @name="tanaka">
Age must be supplied
Name must be supplied

サンプルコード(適用後)

Hash#fetchを利用することで、falseyな値は正常な値として扱いつつ
Hashに要求するキーが設定されていない場合のみエラーとして扱うことができます。

require 'pp'

class Person
  def initialize(attributes)
    @name = attributes.fetch(:name)
    @age = attributes.fetch(:age)
  end
end

tanaka = Person.new({name: "tanaka", age: 34})
pp tanaka

suzuki = Person.new({name: "suzuki", age: nil})
pp suzuki

unknown = Person.new({name: false, age: 55})
pp unknown

begin
  sato = Person.new({name: 'sato'})
rescue => e
  puts e.to_s
end

出力

#<Person:0x2e0a4f0 @age=34, @name="tanaka">
#<Person:0x2e097e8 @age=nil, @name="suzuki">
#<Person:0x2e08ae0 @age=55, @name=false>
key not found: :age