2020年9月17日木曜日

Ruby waterfall を使ってフロー処理を実装してみる

概要

apneadiving/waterfall は Ruby でフロー処理を実現するためのライブラリです
今回は簡単なサンプルを作成して挙動を確認してみました

環境

  • macOS 10.15.6
  • Ruby 2.7.1p83
    • waterfall 1.3.0

簡単なサンプルコード

まずは成功する簡単なサンプルコードを作成しました
waterfall は chain を使って処理を数珠つなぎにすることでフロー処理を実現します
またメインの処理は Waterfall モジュールを include しクラスにすることで管理しやすくしています
また call メソッドを実装することでオブジェクトが生成された際に自動的に処理を開始することができます

結果を受け取る場合は chain にシンボルで引数を与えその名前のシンボルに結果が格納されるようにします

フローを開始する場合は Flow.new します
最後まで成功した場合は最後の chain で結果を受け取ることができます

require 'waterfall'

class MyClass
  include Waterfall

  def initialize(value)
    @value = value
  end

  def call
    self.chain(:value) do
      @value ** 2
    end
  end
end

f = Flow.new
ret = f.chain(value1: :value) do
  MyClass.new(2)
end
.chain(value2: :value) do
  MyClass.new(3)
end
.chain do |result|
  puts result.value1
  puts result.value2
end

失敗した場合

次に途中でフローが失敗した場合のサンプルです
フローが失敗した場合には dam に流すことでフローを失敗にすることができます
前の chain の結果がどうだったか判定するのに when_falsy を使います
このブロック内の結果が false だっと場合に .dam が実行されてエラー処理に流れます

Flow.new 側では dam に流れた処理を受け取るために .on_dam を実装します
dam で返却された内容とエラーの箇所がブロックの引数として渡ってきます

require 'waterfall'

class MyClass
  include Waterfall

  def initialize(value)
    @value = value
  end

  def call
    chain do
      begin
        @value / 0
      rescue ZeroDivisionError
        @value = false
      end
    end
    when_falsy do
      @value
    end
      .dam do
        "ZeroDivisionError"
      end
    chain(:value) do
      @value ** 2
    end
  end
end

f = Flow.new
ret = f.chain(value1: :value) do
  MyClass.new(2)
end
.chain(value2: :value) do
  MyClass.new(3)
end
.chain do |result|
  puts "SUCCESS"
  puts result.value1
  puts result.value2
end
.on_dam do |error, context| 
  puts "ERROR"
  puts error
  puts context
end

halt_chain

dam に流したあとに続けて別のエラー処理をしたい場合に使います
例えば以下のようにエラーになって dam に流れた場合は @value にエラーコード的な値を設定することができます

require 'waterfall'

class MyClass
  include Waterfall

  def initialize(value)
    @value = value
  end

  def call
    chain do
      begin
        @value / 0
      rescue ZeroDivisionError
        @value = false
      end
    end
    .when_falsy do
      @value
    end
      .dam do
        "ZeroDivisionError"
      end
      .halt_chain do |outflow, error_pool, error_pool_context|
        outflow.value = 9999 if error_pool
      end
    chain(:value) do
      @value ** 2
    end
  end
end

f = Flow.new
ret = f.chain(value1: :value) do
  MyClass.new(2)
end
.chain(value2: :value) do
  MyClass.new(3)
end
.chain do |result|
  puts "SUCCESS"
  puts result.value1
  puts result.value2
end
.on_dam do |error, error_pool_context, outflow, waterfall|
  puts "ERROR"
  p waterfall.outflow.value1
end

最後に

Ruby の waterfall を使ってフロー処理を実現してみました
今回は丁寧に do ... end で書きましたが見やすくするために {} で 1 行ブロックで書いてもいいかもしれません
フロー間の引数のやり取りが少し面倒なのでそれに慣れないと使いこなすのは難しいかもしれません
Sidekiq などを使ってもフロー処理は実現できますがブローカが必要だったりするので簡単なフロー処理を実現したいのであれば waterfall でもいいかもしれません

0 件のコメント:

コメントを投稿