module Dry::Transaction::InstanceMethods

Attributes

listeners[R]
operations[R]
stack[R]
steps[R]

Public Class Methods

new(steps: self.class.steps, listeners: nil, **operations) click to toggle source
# File lib/dry/transaction/instance_methods.rb, line 16
def initialize(steps: self.class.steps, listeners: nil, **operations)
  @steps = steps.map { |step|
    operation = resolve_operation(step, **operations)
    step.with(operation: operation)
  }
  @operations = operations
  @stack = Stack.new(@steps)
  subscribe(listeners) unless listeners.nil?
end

Public Instance Methods

call(input = nil, &block) click to toggle source
# File lib/dry/transaction/instance_methods.rb, line 26
def call(input = nil, &block)
  assert_step_arity

  result = stack.(Success(input))

  if block
    ResultMatcher.(result, &block)
  else
    result.or { |step_failure|
      # Unwrap the value from the StepFailure and return it directly
      Failure(step_failure.value)
    }
  end
end
subscribe(listeners) click to toggle source
# File lib/dry/transaction/instance_methods.rb, line 41
def subscribe(listeners)
  @listeners = listeners

  if listeners.is_a?(Hash)
    listeners.each do |step_name, listener|
      steps.detect { |step| step.name == step_name }.subscribe(listener)
    end
  else
    steps.each do |step|
      step.subscribe(listeners)
    end
  end
end
with_step_args(**step_args) click to toggle source
# File lib/dry/transaction/instance_methods.rb, line 55
def with_step_args(**step_args)
  assert_valid_step_args(step_args)

  new_steps = steps.map { |step|
    if step_args[step.name]
      step.with(call_args: step_args[step.name])
    else
      step
    end
  }

  self.class.new(steps: new_steps, listeners: listeners, **operations)
end

Private Instance Methods

assert_step_arity() click to toggle source
# File lib/dry/transaction/instance_methods.rb, line 112
def assert_step_arity
  steps.each do |step|
    num_args_required = step.arity >= 0 ? step.arity : ~step.arity
    num_args_supplied = step.call_args.length + 1 # add 1 for main `input`

    if num_args_required > num_args_supplied
      raise ArgumentError, "not enough arguments supplied for step +#{step.name}+"
    end
  end
end
assert_valid_step_args(step_args) click to toggle source
# File lib/dry/transaction/instance_methods.rb, line 104
def assert_valid_step_args(step_args)
  step_args.each_key do |step_name|
    unless steps.any? { |step| step.name == step_name }
      raise ArgumentError, "+#{step_name}+ is not a valid step name"
    end
  end
end
method_missing(name, *args, &block) click to toggle source
Calls superclass method
# File lib/dry/transaction/instance_methods.rb, line 75
def method_missing(name, *args, &block)
  step = steps.detect { |s| s.name == name }
  super unless step

  operation = operations[step.name]
  unless operation
    raise NotImplementedError,
          "no operation +#{step.operation_name}+ defined for step +#{step.name}+"
  end

  operation.(*args, &block)
end
resolve_operation(step, **operations) click to toggle source
# File lib/dry/transaction/instance_methods.rb, line 88
def resolve_operation(step, **operations)
  # rubocop:disable Lint/DuplicateBranch
  if step.internal? && operations[step.name]
    operations[step.name]
  elsif methods.include?(step.name) || private_methods.include?(step.name)
    method(step.name)
  elsif operations[step.name].respond_to?(:call)
    operations[step.name]
  elsif operations[step.name]
    raise InvalidStepError, step.name
  else
    raise MissingStepError, step.name
  end
  # rubocop:enable Lint/DuplicateBranch
end
respond_to_missing?(name, _include_private = false) click to toggle source
# File lib/dry/transaction/instance_methods.rb, line 71
def respond_to_missing?(name, _include_private = false)
  steps.any? { |step| step.name == name }
end