読者です 読者をやめる 読者になる 読者になる

Tbpgr Blog

Ruby プログラマ tbpgr(てぃーびー) のブログ

Ruby | Collecting Inputs | Use #fetch for defaults

概要

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>