概要
Use #fetch for defaults
前提
Confident Rubyではメソッド内の処理を次のように分類しています。
・Collecting Inputs(引数チェック、変換など)
・Performing Work(主処理)
・Delivering Output(戻り値に関わる処理)
・Handling Failure(例外処理)
当記事は上記のうち、Collecting Inputsに関する話です。
詳細
状況
メソッドがHashの値をパラメータとしてとる場合。
幾つかのキーはオプショナルで、値が特定できなければデフォルト値を設定する。
概要
Hash#fetchを利用site、オプショナルのハッシュキーにデフォルト値を提供する
理由
#fetchは簡潔に意図を表し、nilやfalseなどを正当な値として扱うHashのバグを避ける。
サンプルコード仕様
Personクラスは名前と年齢を通常の引数で受け取り、性別をオプショナルのHashで受け取り初期化するクラスです。
性別は女性(FEMALE = 0)、男性(MALE = 1)、性別不明(UNKNOWN = 2)として内部的に扱いますが、
Personクラスの利用者は {sex: false}を指定することで性別不明(UNKNOWN = 2)を設定できるようにします。
サンプルコード(適用前)
インスタンス変数の初期化時によく使われるイディオムである、
@v = v || default
を利用するとfalse利用時にもdefaultが適用されてしまう。
require 'pp' class Person FEMALE = 0 MALE = 1 UNKNOWN = 2 def initialize(name, age, optional = {}) @name = name @age = age @sex = optional[:sex] || FEMALE end end tanaka = Person.new("tanaka", 34, {sex: Person::FEMALE}) pp tanaka suzuki = Person.new("suzuki", 34, {sex: Person::MALE}) pp suzuki unknown1 = Person.new("unknown", 34, {sex: Person::UNKNOWN}) pp unknown1 unknown2 = Person.new("unknown", 34, {sex: false}) pp unknown2
出力
#<Person:0x2d4a358 @age=34, @name="tanaka", @sex=0> #<Person:0x2d492a8 @age=34, @name="suzuki", @sex=1> #<Person:0x2d482a0 @age=34, @name="unknown", @sex=2> #<Person:0x2d5f520 @age=34, @name="unknown", @sex=0>
サンプルコード(適用後)
Hash#fetch+ブロックを利用することで、falseyな値は正常な値として扱いつつ
別途falseyな値に対して再設定を行うことができます。
require 'pp' class Person FEMALE = 0 MALE = 1 UNKNOWN = 2 def initialize(name, age, optional = {}) @name = name @age = age @sex = optional.fetch(:sex) { FEMALE } @sex = UNKNOWN unless @sex end end tanaka = Person.new("tanaka", 34, {sex: Person::FEMALE}) pp tanaka suzuki = Person.new("suzuki", 34, {sex: Person::MALE}) pp suzuki unknown1 = Person.new("unknown", 34, {sex: Person::UNKNOWN}) pp unknown1 unknown2 = Person.new("unknown", 34, {sex: false}) pp unknown2
出力
#<Person:0x2abe2c0 @age=34, @name="tanaka", @sex=0> #<Person:0x2abd210 @age=34, @name="suzuki", @sex=1> #<Person:0x2abc208 @age=34, @name="unknown", @sex=2> #<Person:0x2acf468 @age=34, @name="unknown", @sex=2>