Class: AceMQ::AMQP::Patterns::Saga::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/acemq/amqp/patterns/saga.rb

Overview

Collects the steps of a AceMQ::AMQP::Patterns::Saga.

Building mutates, in the one place this library allows it and for the same reason Topology and RoutingSlip do: a saga is assembled and then run, and threading a new copy through four chained calls would buy an immutability nobody is using. The AceMQ::AMQP::Patterns::Saga that comes out of #build is frozen, which is the copy that matters.

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Builder

Returns a new instance of Builder.



223
224
225
226
# File 'lib/acemq/amqp/patterns/saga.rb', line 223

def initialize(name)
  @name = name
  @steps = []
end

Instance Method Details

#build ⇒ Saga

Returns:

Raises:

  • (ArgumentError) —

    when it has no steps



276
277
278
279
280
# File 'lib/acemq/amqp/patterns/saga.rb', line 276

def build
  raise ArgumentError, "saga #{@name} has no steps" if @steps.empty?

  Saga.new(name: @name, steps: @steps.map(&:freeze))
end

#compensate_with {|subject| ... } ⇒ Builder

Gives the most recently added step something that undoes it.

Yield Parameters:

  • subject (Object) —

    what the saga is operating on

Returns:



261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/acemq/amqp/patterns/saga.rb', line 261

def compensate_with(&compensation)
  unless compensation
    raise ArgumentError, "compensate_with needs a block that undoes the last step"
  end
  if @steps.empty?
    raise ArgumentError, "there is no step to compensate yet: call step(...) first"
  end

  @steps[-1] = Step.new(name: @steps.last.name, action: @steps.last.action,
                        compensation: compensation)
  self
end

#step(step_name) {|subject| ... } ⇒ Builder

Adds a step, whose block is the work.

A step added this way has no compensation, which is legitimate for a step that changed nothing and a mistake for one that did. There is no warning for the second case, because a library cannot tell them apart — which is the argument for writing the compensation first and the action second.

Parameters:

  • step_name (String) —

    what the step is called

Yield Parameters:

  • subject (Object) —

    what the saga is operating on

Returns:

Raises:

  • (ArgumentError)


239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/acemq/amqp/patterns/saga.rb', line 239

def step(step_name, &action)
  raise ArgumentError, "a saga step needs a name" if step_name.to_s.empty?
  unless action
    raise ArgumentError, "saga step #{step_name} needs a block to do the work"
  end

  step_name = step_name.to_s
  if @steps.any? { |existing| existing.name == step_name }
    raise ArgumentError,
          "saga #{@name} already has a step called #{step_name.inspect}. Names " \
          "identify a step in the compensation report, so two of them would make " \
          "that report ambiguous."
  end

  @steps << Step.new(name: step_name, action: action, compensation: nil)
  self
end