Skip to content

Around hook

Chains might use a around hook eg. for wrapping the entire execution in a database transaction. There hooks gets total control over the execution, so it needs to take care of calling the chain and returning it's result.

require "teckel/chain"

class CreateUser
  include ::Teckel::Operation

  result!
  input  Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer.optional)
  output Types.Instance(User)
  error  Types::Hash.schema(message: Types::String, errors: Types::Array.of(Types::Hash))

  def call(input)
    user = User.new(name: input[:name], age: input[:age])
    if user.save
      success!(user)
    else
      fail!(message: "Could not safe User", errors: user.errors)
    end
  end
end

class AddFriend
  include ::Teckel::Operation

  result!
  settings Struct.new(:fail_befriend)
  input  Types.Instance(User)
  output Types::Hash.schema(user: Types.Instance(User), friend: Types.Instance(User))
  error  Types::Hash.schema(message: Types::String)

  def call(user)
    if settings&.fail_befriend == :fail
      fail!(message: "Did not find a friend.")
    else
      success!(user: user, friend: User.new(name: "A friend", age: 42))
    end
  end
end

LOG = []

class MyChain
  include Teckel::Chain

  around ->(chain, input) {
    result = nil
    begin
      LOG << :before
      FakeDB.transaction do
        # The hook needs to call the chain:    
        result = chain.call(input)

        raise FakeDB::Rollback if result.failure?
      end
      LOG << :after

      result # ... and return the success result
    rescue FakeDB::Rollback
      LOG << :rollback

      result # ... and return the failure result
    end
  }

  step :create, CreateUser
  step :befriend, AddFriend
end

failure_result = MyChain.with(befriend: :fail).call(name: "Bob", age: 23)
failure_result
#=> #<Teckel::Chain::Result:<...>>

# triggered DB rollback
LOG
#=> [:before, :rollback]

# additional step information
failure_result.step
#=> :befriend

# behaves just like a normal +Result+
failure_result.failure?
#=> true

failure_result.failure
#=> {:message=>"Did not find a friend."}

Last update: 2021-09-28 15:32:53