概要
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