Chain basics
Chains run multiple Operations ("steps") in order, returning the success value of the last step.
When any step returns a failure, the chain is stopped and that failure is returned.
Operations used as steps need to return result objects (implementing Teckel::Result
).
Chains always return a result object including the name of the step they origin from.
This is especially useful to switch error handling for failure results.
Example
Defining a simple Chain with three steps.
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 save User", errors: user.errors)
end
end
end
class LogUser
include ::Teckel::Operation
result!
input Types.Instance(User)
error none
output input
def call(usr)
Logger.new(File::NULL).info("User #{usr.name} created")
success!(usr) # we need to return the correct output type
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
class MyChain
include Teckel::Chain
step :create, CreateUser
step :log, LogUser
step :befriend, AddFriend
finalize!
end
result = MyChain.call(name: "Bob", age: 23)
#=> #<Teckel::Chain::Result:<...>>
result.success[:user]
#=> #<User:<...> @age=23, @name="Bob">
result.success[:friend]
#=> #<User:<...> @age=42, @name="A friend">
failure_result = MyChain.with(befriend: :fail).call(name: "Bob", age: 23)
failure_result
#=> #<Teckel::Chain::Result:<...>>
# 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."}
Pattern matching
Hash style:
result = case MyChain.call(name: "Bob", age: 23)
in { success: false, step: :befriend, value: value }
["Failed", value]
in { success: true, value: value }
["Success result", value]
end
result
#=> ["Success result",
{:friend=>#<User:<...> @age=42, @name="A friend">,
:user=>#<User:<...> @age=23, @name="Bob">}]
Array style:
result = case MyChain.with(befriend: :fail).call(name: "Bob", age: 23)
in [false, :befriend, value]
["Failed", value]
in [true, value]
["Success result", value]
end
result
#=> ["Failed", {:message=>"Did not find a friend."}]
Last update: 2021-09-28 15:32:53