Class: Hyrax::Transactions::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/hyrax/transactions/transaction.rb

Overview

Queues up a set of ‘#steps` to run over a value via `#call`. Steps are given by name, and resolved at runtime via `container`, which defaults to `Hyrax::Transactions::Container`. This allows injection of specific handling for named steps at any time.

‘#call` will ALWAYS return a `Result` (`Success`|`Failure`) and it’s recommended for users to handle the output in a way that directly addresses the ‘Failure` case. The simplest way to do this is to use `#value_or`: `tx.call(my_value).value_or { |failure| handle_failure(f) }`.

Examples:

running a transaction for a set of steps

steps = ['change_set.validate', 'change_set.save']
tx    = Hyrax::Transactions::Transaction.new(steps: steps)

change_set = Hyrax::ChangeSet.for(Hyrax::Work.new)
change_set.title = ['comet in moominland']

tx.call(change_set) # => Success(#<Hyrax::Work ...>)

with a failure

class ChangeSetWithTitleValidation < Hyrax::ChangeSet
  self.fields = [:title]
  validates :title, presence: true
end

change_set = ChangeSetWithTitleValidation.new(Hyrax::Work.new)
change_set.title = []

tx.call(change_set)
# => Failure([:failed_validation, #<Reform::Form::ActiveModel::Errors ...>]

unwapping values safely with handling for failures

tx.call(change_set).value_or { |failure| "uh oh: #{failure} }

when there is no need to unwrap the value, handle errors with ‘#or`

tx.call(change_set).or { |failure| "uh oh: #{failure} }

a pattern for subclassing to create new transactions

class CustomTransaction < Transaction
  DEFAULT_STEPS = ['step.1', 'step.2', 'step.3'].freeze

  def initialize(container: Container, steps: DEFAULT_STEPS)
    super
  end
end

tx = CustomTransaction.new
tx.call(:some_value)

Since:

  • 3.0.0

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(container: Container, steps:) ⇒ Transaction

Returns a new instance of Transaction.

Parameters:

  • container (Container) (defaults to: Container)
  • steps (Array<String>)

Since:

  • 3.0.0



76
77
78
79
# File 'lib/hyrax/transactions/transaction.rb', line 76

def initialize(container: Container, steps:)
  self.container = container
  self.steps     = steps
end

Instance Attribute Details

#containerContainer

Returns:



69
70
71
# File 'lib/hyrax/transactions/transaction.rb', line 69

def container
  @container
end

#stepsArray<String>

Returns:

  • (Array<String>)


69
# File 'lib/hyrax/transactions/transaction.rb', line 69

attr_accessor :container, :steps

Instance Method Details

#call(value) ⇒ Dry::Monads::Result

Run each step name in ‘#steps` by resolving the name in the given `container` and passing a value to call. Each step must return a `Result`. In the event of a `Success`, the wrapped value is passed through to the next step; for `Failure` the whole chain is short- circuited and the failure result is given.

Parameters:

  • value (Object)

Returns:

  • (Dry::Monads::Result)

    either the ‘Success` result of the entire `#steps` chain over `value`, or a `Failure` from the failed step.

See Also:

  • Dry::Monads::Do

Since:

  • 3.0.0



95
96
97
98
99
100
101
102
# File 'lib/hyrax/transactions/transaction.rb', line 95

def call(value)
  Success(
    steps.inject(value) do |val, step_name|
      args = step_arguments_for(step_name)
      yield container[step_name].call(val, *args[0..-2], **Hash(args[-1]))
    end
  )
end

#with_step_args(**args) ⇒ Transaction

Sets arguments to pass to a given step in the transaction.

This makes it easy for individual steps to require pieces of information without other steps having to handle them. This is desirable since it avoids passing mutable values between steps, which commonly results in tight interdependence and less flexible composibility between steps.

Instead we expect the caller to provide the correct data to each step when the transaction starts.

Examples:

passing arguments for a named step

tx = Hyrax::Transactions::Transaction.new(steps: [:first_step, :second_step])
result = tx.with_step_args(second_step: {named_parameter: :param_value}).call(:value)

Parameters:

  • args (Hash<Object, Array>)

Returns:

Raises:

  • (ArgumentError)

Since:

  • 3.0.0



123
124
125
126
127
128
129
# File 'lib/hyrax/transactions/transaction.rb', line 123

def with_step_args(**args)
  raise(ArgumentError, key_err_msg(args.keys)) if
    args.keys.any? { |key| !step?(key) }

  @_step_args = args
  self
end