Class: Serviced::Flow

Inherits:
Object
  • Object
show all
Includes:
ResultHelpers
Defined in:
lib/serviced/flow.rb

Overview

Composes several steps into a single callable pipeline.

A step is anything that responds to call(context) and returns a Result: usually a Service subclass, but a lambda or any callable works too. Steps run in order, each receiving an immutable context hash. The first failing step halts the flow and its failure is returned. When every step succeeds, whatever hash each step returned as its success value is merged into the context, and the final context is returned as the success value.

class RegisterPatient < Serviced::Flow
transactional # wrap every step in one database transaction

step CreatePatient   # returns success(patient: patient)
step CreateChart     # reads :patient from the context
step SendWelcome
end

result = RegisterPatient.call(name: "Ada", age: 36)
result.value # => merged context hash of everything the steps produced

A flow can also be built inline:

RegisterPatient = Serviced::Flow.define(transaction: true) do
step CreatePatient
step CreateChart
end

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context = {}) ⇒ Flow

Returns a new instance of Flow.

Parameters:

  • context (Hash) (defaults to: {})

    initial context passed to the first step



88
89
90
# File 'lib/serviced/flow.rb', line 88

def initialize(context = {})
  @context = normalize(context).freeze
end

Class Method Details

.call(context = {}) ⇒ Serviced::Result

Runs the flow.

Parameters:

  • context (Hash) (defaults to: {})

    initial context

Returns:



74
75
76
# File 'lib/serviced/flow.rb', line 74

def call(context = {})
  new(context).call
end

.define(transaction: false, &block) ⇒ Class

Builds an anonymous flow subclass from a block.

Parameters:

  • transaction (Boolean) (defaults to: false)

    whether to wrap the steps in a transaction

Returns:

  • (Class)

    a new Serviced::Flow subclass



64
65
66
67
68
69
# File 'lib/serviced/flow.rb', line 64

def define(transaction: false, &block)
  Class.new(self) do
    transactional if transaction
    class_eval(&block) if block
  end
end

.step(callable) ⇒ Object

Registers a step. Steps run in the order they are declared.

Parameters:

  • callable (#call)

    a Service subclass, lambda, or any callable that returns a Serviced::Result



46
47
48
# File 'lib/serviced/flow.rb', line 46

def step(callable)
  steps << callable
end

.stepsArray<#call>

Returns the steps registered on this flow.

Returns:

  • (Array<#call>)

    the steps registered on this flow



39
40
41
# File 'lib/serviced/flow.rb', line 39

def steps
  @steps ||= []
end

.transactionalObject

Marks the flow as transactional: all steps run inside a single transaction that rolls back if any step fails or raises.



52
53
54
# File 'lib/serviced/flow.rb', line 52

def transactional
  @transactional = true
end

.transactional?Boolean

Returns whether the flow runs inside a transaction.

Returns:

  • (Boolean)

    whether the flow runs inside a transaction



57
58
59
# File 'lib/serviced/flow.rb', line 57

def transactional?
  @transactional
end

Instance Method Details

#callServiced::Result

Returns:



93
94
95
# File 'lib/serviced/flow.rb', line 93

def call
  self.class.transactional? ? within_transaction { run_steps } : run_steps
end