Tbpgr Blog

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

Ruby | Collecting Inputs | Receive policies instead of data

概要

Receive policies instead of data

前提

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

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

詳細

状況

異なるクライアントのメソッドが異なる方法で潜在的なエッジケースを取り扱う場合。
例えば、ある呼び出し元のファイルを削除するメソッドは、存在しないファイルを削除しようとして
警告を欲するかもしれない。
他のメソッドはファイルがなくても無視したいかもしれない。

概要

エッジケースの方針を決定できるブロックやProcを受け取る

理由

特定の呼び元がエッジケースをどのように扱うか、最適な決定できる。
エッジケースに対して引数を追加することもできるが、インターフェースの動作を知るには
メソッド定義を参照する必要がある。

サンプルコード仕様

文字列を大文字小文字交互に変換するstripeメソッドを作成します。

サンプルコード1では空文字の場合に任意の処理を実行できるように、yieldでブロックを受け取るようにします。
サンプルコード2では複数のエッジケースを扱えるようにオプションでProcを渡します。

サンプルコード1

yieldを活用して、空文字に対して処理無し・例外の発生を自由に選択できるようになっています。

class String
  def stripe
    if empty?
      if block_given?
        yield
      end
    end
    updowns = %w(upcase downcase)
    index = 1
    chars.reduce([]) do |ret, char|
      ret << char.send(updowns[index % 2])
      index += 1
      ret
    end.join
  end
end

ret = "hogehoge".stripe
puts ret

ret = "".stripe
puts ret.empty?

begin
  ret = "".stripe { fail "String is empty" }
rescue => e
  puts e
end

出力

hOgEhOgE
true
String is empty

サンプルコード1

オプションでProc(lambda)を複数渡すことができるようにします。

class String
  def stripe(options = {})
    empty_policy = options.fetch(:on_empty) { -> { }} # nothing
    single_char_policy = options.fetch(:on_single) { -> (x) {}} # nothing
    empty_policy.call if empty?
    single_char_policy.call(self) if size == 1
    updowns = %w(upcase downcase)
    index = 1
    chars.reduce([]) do |ret, char|
      ret << char.send(updowns[index % 2])
      index += 1
      ret
    end.join
  end
end

ret = "hogehoge".stripe
puts ret

ret = "".stripe
puts ret.empty?

begin
  ret = "".stripe({on_empty: -> { fail 'String is Empty' }})
rescue => e
  puts e
end

begin
  ret = "a".stripe({on_single: -> (x) { fail "String(#{x}) is Single" }})
rescue => e
  puts e
end
hOgEhOgE
true
String is Empty
String(a) is Single