Class: Dry::Transaction::Sequence

Inherits:
Object
  • Object
show all
Defined in:
lib/dry/transaction/sequence.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(steps) ⇒ Sequence

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Sequence.



10
11
12
# File 'lib/dry/transaction/sequence.rb', line 10

def initialize(steps)
  @steps = steps
end

Instance Attribute Details

#stepsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



7
8
9
# File 'lib/dry/transaction/sequence.rb', line 7

def steps
  @steps
end

Instance Method Details

#append(other = nil, **options, &block) ⇒ Dry::Transaction::Sequence

Return a transaction with the steps from the provided transaction appended onto the end of the steps in ‘self`.

Examples:

Append an existing transaction

my_transaction = Dry.Transaction(container: container) do
  step :first
  step :second
end

other_transaction = Dry.Transaction(container: container) do
  step :another
end

my_transaction.append(other_transaction)

Append a transaction defined inline

my_transaction = Dry.Transaction(container: container) do
  step :first
  step :second
end

my_transaction.append(container: container) do
  step :another
end

Parameters:

  • other (Dry::Transaction::Sequence) (defaults to: nil)

    the transaction to append. Optional if you will define a transaction inline via a block.

  • options (Hash)

    the options hash for defining a transaction inline via a block. Optional if the transaction is passed directly as ‘other`.

Options Hash (**options):

  • :container (#[])

    the operations container

Returns:



175
176
177
178
179
# File 'lib/dry/transaction/sequence.rb', line 175

def append(other = nil, **options, &block)
  other = accept_or_build_transaction(other, **options, &block)

  self.class.new(steps + other.steps)
end

#call(input, options = {}, &block) ⇒ Right, Left Also known as: []

Run the transaction.

Each operation will be called in the order it was specified, with its output passed as input to the next operation. Operations will only be called if the previous step was a success.

If any of the operations require extra arguments beyond the main input e.g. with a signature like ‘#call(something_else, input)`, then you must pass the extra arguments as arrays for each step in the options hash.

The return value will be the output from the last operation, wrapped in a [Kleisli](kleisli) ‘Either` object, a `Right` for a successful transaction or a `Left` for a failed transaction.

[kleisli]: rubygems.org/gems/kleisli

Examples:

Running a transaction

my_transaction.call(some_input)

Running a transaction with extra step arguments

my_transaction.call(some_input, step_name: [extra_argument])

Parameters:

  • input
  • options (Hash) (defaults to: {})

    extra step arguments

Returns:

  • (Right, Left)

    output from the final step



43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/dry/transaction/sequence.rb', line 43

def call(input, options = {}, &block)
  assert_valid_options(options)
  assert_options_satisfy_step_arity(options)

  steps = steps_with_options_applied(options)
  result = steps.inject(Right(input), :>>)

  if block
    block.call(ResultMatcher.new(result))
  else
    result
  end
end

#insert(other = nil, before: nil, after: nil, **options, &block) ⇒ Dry::Transaction::Sequence

Return a transaction with the steps from the provided transaction inserted into a specific place among the steps in ‘self`.

Transactions can be inserted either before or after a named step.

Examples:

Insert an existing transaction (before a step)

my_transaction = Dry.Transaction(container: container) do
  step :first
  step :second
end

other_transaction = Dry.Transaction(container: container) do
  step :another
end

my_transaction.insert(other_transaction, before: :second)

Append a transaction defined inline (after a step)

my_transaction = Dry.Transaction(container: container) do
  step :first
  step :second
end

my_transaction.insert(after: :first, container: container) do
  step :another
end

Parameters:

  • other (Dry::Transaction::Sequence) (defaults to: nil)

    the transaction to append. Optional if you will define a transaction inline via a block.

  • before (Symbol) (defaults to: nil)

    the name of the step before which the transaction should be inserted (provide either this or ‘after`)

  • after (Symbol) (defaults to: nil)

    the name of the step after which the transaction should be inserted (provide either this or ‘before`)

  • options (Hash)

    the options hash for defining a transaction inline via a block. Optional if the transaction is passed directly as ‘other`.

Options Hash (**options):

  • :container (#[])

    the operations container

Returns:



222
223
224
225
226
227
228
229
230
231
232
# File 'lib/dry/transaction/sequence.rb', line 222

def insert(other = nil, before: nil, after: nil, **options, &block)
  insertion_step = before || after
  unless steps.map(&:step_name).include?(insertion_step)
    raise ArgumentError, "+#{insertion_step}+ is not a valid step name"
  end

  other = accept_or_build_transaction(other, **options, &block)
  index = steps.index { |step| step.step_name == insertion_step } + (!!after ? 1 : 0)

  self.class.new(steps.dup.insert(index, *other.steps))
end

#prepend(other = nil, **options, &block) ⇒ Dry::Transaction::Sequence

Return a transaction with the steps from the provided transaction prepended onto the beginning of the steps in ‘self`.

Examples:

Prepend an existing transaction

my_transaction = Dry.Transaction(container: container) do
  step :first
  step :second
end

other_transaction = Dry.Transaction(container: container) do
  step :another
end

my_transaction.prepend(other_transaction)

Prepend a transaction defined inline

my_transaction = Dry.Transaction(container: container) do
  step :first
  step :second
end

my_transaction.prepend(container: container) do
  step :another
end

Parameters:

  • other (Dry::Transaction::Sequence) (defaults to: nil)

    the transaction to prepend. Optional if you will define a transaction inline via a block.

  • options (Hash)

    the options hash for defining a transaction inline via a block. Optional if the transaction is passed directly as ‘other`.

Options Hash (**options):

  • :container (#[])

    the operations container

Returns:



134
135
136
137
138
# File 'lib/dry/transaction/sequence.rb', line 134

def prepend(other = nil, **options, &block)
  other = accept_or_build_transaction(other, **options, &block)

  self.class.new(other.steps + steps)
end

#remove(step, ...) ⇒ Dry::Transaction::Sequence

Return a transaction with steps removed.

Examples:

my_transaction = Dry.Transaction(container: container) do
  step :first
  step :second
  step :third
end

my_transaction.remove(:first, :third)

Parameters:

  • step (Symbol)

    the names of a step to remove

  • ... (Symbol)

    more names of steps to remove

Returns:



252
253
254
# File 'lib/dry/transaction/sequence.rb', line 252

def remove(*steps_to_remove)
  self.class.new(steps.reject { |step| steps_to_remove.include?(step.step_name) })
end

#subscribe(listeners) ⇒ Object

Subscribe to notifications from steps.

When each step completes, it will send a ‘[step_name]_success` or `[step_name]_failure` message to any subscribers.

For example, if you had a step called ‘persist`, then it would send either `persist_success` or `persist_failure` messages to subscribers after the operation completes.

Pass a single object to subscribe to notifications from all steps, or pass a hash with step names as keys to subscribe to notifications from specific steps.

Notifications are implemented using the [Wisper](wisper) gem.

[wisper]: rubygems.org/gems/wisper

Examples:

Subscribing to notifications from all steps

my_transaction.subscribe(my_listener)

Subscribing to notifications from specific steps

my_transaction.subscribe(some_step: my_listener, another_step: another_listener)

Parameters:

  • listeners (Object, Hash{Symbol => Object})

    the listener object or hash of steps and listeners



85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/dry/transaction/sequence.rb', line 85

def subscribe(listeners)
  if listeners.is_a?(Hash)
    listeners.each do |step_name, listener|
      steps.detect { |step| step.step_name == step_name }.subscribe(listener)
    end
  else
    steps.each do |step|
      step.subscribe(listeners)
    end
  end

  self
end