概要
Define conversion functions
前提
Confident Rubyではメソッド内の処理を次のように分類しています。
・Collecting Inputs(引数チェック、変換など)
・Performing Work(主処理)
・Delivering Output(戻り値に関わる処理)
・Handling Failure(例外処理)
当記事は上記のうち、Collecting Inputsに関する話です。
詳細
状況
様々な形式で入力を受け付けるPublic APIが欲しい。
しかし、内部ではnormalizeされた一つの方のオブジェクトが欲しい。
概要
どのような入力オブジェクトに対しても適用できる冪等性のある変換関数を定義する。
理由
入力が必要とされる型に変換されれば、
不確かな入力に対して心配する時間を節約できて、ビジネスロジックの記述により多くの時間を使うことができる。
サンプルコード仕様
名前と年齢をフィールドに持つPersonクラスの変換関数を作成する。
受け入れる入力は、Array, String, Hashとする。
Arrayは要素が1つなら1要素目を name に設定。
Arrayは要素が2つなら1要素目を name に設定。2要素目を age に設定。
Stringは文字列内にセミコロンがなければ、最初の要素を name に設定。
Stringは文字列内にセミコロンがあれば、セパレートして最初の要素をnameに設定。最後の要素を age に設定。
Hashはkeyに :name があれば、対応する value を name に設定。
Hashはkeyに :age があれば、対応する value を age に設定。
すべて、設定対象がない場合はデフォルトを適用する。name のデフォルトは「unknown」。age のデフォルトは「20」
サンプルコード
require 'pp' module TalkablePerson module Conversion module_function def Person(args) name = Person::DEFAULT_NAME age = Person::DEFAULT_AGE case args when Array name = args[0] if args[0] age = args[1] if args[1] when String if args.include? ':' name_age = args.split(':') name = name_age.first age = name_age.last.to_i else name = args end when Hash name = args[:name] if args[:name] age = args[:age] if args[:age] else fail 'invalid type' end Person.new(name, age) end end class Person DEFAULT_NAME = "unknown" DEFAULT_AGE = 20 attr_reader :name, :age def initialize(name, age) @name, @age = name, age end def say puts "私は#{name}。#{age}歳です" end end end tanakas = [] tanakas << TalkablePerson::Conversion::Person(["Tanaka Array1", 25]) tanakas << TalkablePerson::Conversion::Person(["Tanaka Array2"]) tanakas << TalkablePerson::Conversion::Person("Tanaka String1:25") tanakas << TalkablePerson::Conversion::Person("Tanaka String2") tanakas << TalkablePerson::Conversion::Person(name: "Tanaka Hash1", age: 25) tanakas << TalkablePerson::Conversion::Person(name: "Tanaka Hash2") tanakas.each(&:say)
出力
私はTanaka Array1。25歳です 私はTanaka Array2。20歳です 私はTanaka String1。25歳です 私はTanaka String2。20歳です 私はTanaka Hash1。25歳です 私はTanaka Hash2。20歳です