module Interaktor::Callable::ClassMethods

Public Instance Methods

call(context = {}) click to toggle source

Invoke an Interaktor. This is the primary public API method to an interaktor. Interaktor failures will not raise an exception.

@param context [Hash, Interaktor::Context] the context object as a hash

with attributes or an already-built context

@return [Interaktor::Context] the context, following interaktor execution

# File lib/interaktor/callable.rb, line 296
def call(context = {})
  execute(context, false)
end
call!(context = {}) click to toggle source

Invoke an Interaktor. This method behaves identically to `#call`, but if the interaktor is failed, `Interaktor::Failure` is raised.

@param context [Hash, Interaktor::Context] the context object as a hash

with attributes or an already-built context

@raises [Interaktor::Failure]

@return [Interaktor::Context] the context, following interaktor execution

# File lib/interaktor/callable.rb, line 309
def call!(context = {})
  execute(context, true)
end
failure(schema = nil, &block) click to toggle source

@param schema [Dry::Schema::Params, nil] a predefined schema object @yield a new Dry::Schema::Params definition block

# File lib/interaktor/callable.rb, line 188
def failure(schema = nil, &block)
  raise "No schema or schema definition block provided to interaktor failure method." if schema.nil? && !block

  raise "Provided both a schema and a schema definition block for interaktor failure method." if schema && block

  if schema
    raise "Provided argument is not a Dry::Schema::Params object." unless schema.is_a?(Dry::Schema::Params)

    @failure_schema = schema
  elsif block
    @failure_schema = Dry::Schema.Params { instance_eval(&block) }
  end
end
failure_attributes() click to toggle source

The complete list of failure attributes.

@return [Array<Symbol>]

# File lib/interaktor/callable.rb, line 157
def failure_attributes
  required_failure_attributes + optional_failure_attributes
end
failure_schema() click to toggle source

Get the failure attribute schema. Fall back to an empty schema with a configuration that will deny ALL provided attributes - not defining an failure schema should mean the interaktor has no failure attributes.

@return [Dry::Schema::Params]

# File lib/interaktor/callable.rb, line 166
def failure_schema
  @failure_schema || Dry::Schema.Params
end
input(schema = nil, &block) click to toggle source

@param schema [Dry::Schema::Params, nil] a predefined schema object @yield a new Dry::Schema::Params definition block

# File lib/interaktor/callable.rb, line 88
def input(schema = nil, &block)
  raise "No schema or schema definition block provided to interaktor input." if schema.nil? && !block

  raise "Provided both a schema and a schema definition block for interaktor input." if schema && block

  if schema
    raise "Provided argument is not a Dry::Schema::Params object." unless schema.is_a?(Dry::Schema::Params)

    @input_schema = schema
  elsif block
    @input_schema = Dry::Schema.Params { instance_eval(&block) }
  end

  # define the getters and setters for the input attributes
  @input_schema.key_map.keys.each do |key| # rubocop:disable Style/HashEachMethods
    attribute_name = key.id

    # Define getter
    define_method(attribute_name) { @context.send(attribute_name) }

    # Define setter
    define_method("#{attribute_name}=".to_sym) do |value|
      @context.send("#{attribute_name}=".to_sym, value)
    end
  end
end
input_attributes() click to toggle source

The complete list of input attributes.

@return [Array<Symbol>]

# File lib/interaktor/callable.rb, line 57
def input_attributes
  required_input_attributes + optional_input_attributes
end
input_schema() click to toggle source

Get the input attribute schema. Fall back to an empty schema with a configuration that will deny ALL provided attributes - not defining an input schema should mean the interaktor has no input attributes.

@return [Dry::Schema::Params]

# File lib/interaktor/callable.rb, line 66
def input_schema
  @input_schema || Dry::Schema.Params
end
optional_failure_attributes() click to toggle source

The list of attributes which are not required to be provided when failing the interaktor.

@return [Array<Symbol>]

# File lib/interaktor/callable.rb, line 133
def optional_failure_attributes
  # Adding an optional attribute with NO predicates with Dry::Schema is
  # sort of a "nothing statement" - the schema can sort of ignore it. The
  # problem is that the optional-with-no-predicate key is not included in
  # the #info results, so we need to find an list of keys elsewhere, find
  # the ones that are listed there but not in the #info results, and find
  # the difference. The result are the keys that are omitted from the #info
  # result because they are optional and have no predicates.
  #
  # See https://github.com/dry-rb/dry-schema/issues/347
  @optional_failure_attributes ||= begin
      attributes_in_info = failure_schema.info[:keys].keys
      all_attributes = failure_schema.key_map.keys.map(&:id)
      optional_attributes_by_exclusion = all_attributes - attributes_in_info

      explicitly_optional_attributes = failure_schema.info[:keys].reject { |_, info| info[:required] }.keys

      explicitly_optional_attributes + optional_attributes_by_exclusion
    end
end
optional_input_attributes() click to toggle source

The list of attributes which are not required to be passed in when calling the interaktor.

@return [Array<Symbol>]

# File lib/interaktor/callable.rb, line 33
def optional_input_attributes
  # Adding an optional attribute with NO predicates with Dry::Schema is
  # sort of a "nothing statement" - the schema can sort of ignore it. The
  # problem is that the optional-with-no-predicate key is not included in
  # the #info results, so we need to find an list of keys elsewhere, find
  # the ones that are listed there but not in the #info results, and find
  # the difference. The result are the keys that are omitted from the #info
  # result because they are optional and have no predicates.
  #
  # See https://github.com/dry-rb/dry-schema/issues/347
  @optional_input_attributes ||= begin
      attributes_in_info = input_schema.info[:keys].keys
      all_attributes = input_schema.key_map.keys.map(&:id)
      optional_attributes_by_exclusion = all_attributes - attributes_in_info

      explicitly_optional_attributes = input_schema.info[:keys].reject { |_, info| info[:required] }.keys

      explicitly_optional_attributes + optional_attributes_by_exclusion
    end
end
optional_success_attributes() click to toggle source

The list of attributes which are not required to be provided when failing the interaktor.

@return [Array<Symbol>]

# File lib/interaktor/callable.rb, line 220
def optional_success_attributes
  # Adding an optional attribute with NO predicates with Dry::Schema is
  # sort of a "nothing statement" - the schema can sort of ignore it. The
  # problem is that the optional-with-no-predicate key is not included in
  # the #info results, so we need to find an list of keys elsewhere, find
  # the ones that are listed there but not in the #info results, and find
  # the difference. The result are the keys that are omitted from the #info
  # result because they are optional and have no predicates.
  #
  # See https://github.com/dry-rb/dry-schema/issues/347
  @optional_success_attributes ||= begin
      attributes_in_info = success_schema.info[:keys].keys
      all_attributes = success_schema.key_map.keys.map(&:id)
      optional_attributes_by_exclusion = all_attributes - attributes_in_info

      explicitly_optional_attributes = success_schema.info[:keys].reject { |_, info| info[:required] }.keys

      explicitly_optional_attributes + optional_attributes_by_exclusion
    end
end
required_failure_attributes() click to toggle source

The list of attributes which are required to be provided when failing the interaktor.

@return [Array<Symbol>]

# File lib/interaktor/callable.rb, line 123
def required_failure_attributes
  @required_failure_attributes ||= failure_schema.info[:keys]
    .select { |_, info| info[:required] }
    .keys
end
required_input_attributes() click to toggle source

The list of attributes which are required to be passed in when calling the interaktor.

@return [Array<Symbol>]

# File lib/interaktor/callable.rb, line 23
def required_input_attributes
  @required_input_attributes ||= input_schema.info[:keys]
                                             .select { |_, info| info[:required] }
                                             .keys
end
required_success_attributes() click to toggle source

The list of attributes which are required to be provided when the interaktor succeeds.

@return [Array<Symbol>]

# File lib/interaktor/callable.rb, line 210
def required_success_attributes
  @required_success_attributes ||= success_schema.info[:keys]
                                                 .select { |_, info| info[:required] }
                                                 .keys
end
success(schema = nil, &block) click to toggle source

@param schema [Dry::Schema::Params, nil] a predefined schema object @yield a new Dry::Schema::Params definition block

# File lib/interaktor/callable.rb, line 275
def success(schema = nil, &block)
  raise "No schema or schema definition block provided to interaktor success method." if schema.nil? && !block

  raise "Provided both a schema and a schema definition block for interaktor success method." if schema && block

  if schema
    raise "Provided argument is not a Dry::Schema::Params object." unless schema.is_a?(Dry::Schema::Params)

    @success_schema = schema
  elsif block
    @success_schema = Dry::Schema.Params { instance_eval(&block) }
  end
end
success_attributes() click to toggle source

The complete list of success attributes.

@return [Array<Symbol>]

# File lib/interaktor/callable.rb, line 244
def success_attributes
  required_success_attributes + optional_success_attributes
end
success_schema() click to toggle source

Get the success attribute schema. Fall back to an empty schema with a configuration that will deny ALL provided attributes - not defining an success schema should mean the interaktor has no success attributes.

@return [Dry::Schema::Params]

# File lib/interaktor/callable.rb, line 253
def success_schema
  @success_schema || Dry::Schema.Params
end
validate_failure_schema(context) click to toggle source

@param context [Hash]

@return [void]

# File lib/interaktor/callable.rb, line 173
def validate_failure_schema(context)
  return unless failure_schema

  result = failure_schema.call(context)

  if result.errors.any?
    raise Interaktor::Error::AttributeSchemaValidationError.new(
      self,
      result.errors.to_h,
    )
  end
end
validate_input_schema(context) click to toggle source

@param context [Hash]

@return [void]

# File lib/interaktor/callable.rb, line 73
def validate_input_schema(context)
  return unless input_schema

  result = input_schema.call(context)

  if result.errors.any?
    raise Interaktor::Error::AttributeSchemaValidationError.new(
      self,
      result.errors.to_h,
    )
  end
end
validate_success_schema(context) click to toggle source

@param context [Hash]

@return [void]

# File lib/interaktor/callable.rb, line 260
def validate_success_schema(context)
  return unless success_schema

  result = success_schema.call(context)

  if result.errors.any?
    raise Interaktor::Error::AttributeSchemaValidationError.new(
      self,
      result.errors.to_h,
    )
  end
end

Private Instance Methods

execute(context, raise_exception) click to toggle source

The main execution method triggered by the public `#call` or `#call!` methods.

@param context [Hash, Interaktor::Context] the context object as a hash

with attributes or an already-built context

@param raise_exception [Boolean] whether or not to raise exception on

failure

@raises [Interaktor::Failure]

@return [Interaktor::Context] the context, following interaktor execution

# File lib/interaktor/callable.rb, line 326
def execute(context, raise_exception)
  run_method = raise_exception ? :run! : :run

  case context
  when Hash
    # Silently remove any attributes that are not included in the schema
    allowed_keys = input_schema.key_map.keys.map { |k| k.name.to_sym }
    context.select! { |k, _| allowed_keys.include?(k.to_sym) }

    validate_input_schema(context)

    new(context).tap(&run_method).instance_variable_get(:@context)
  when Interaktor::Context
    new(context).tap(&run_method).instance_variable_get(:@context)
  else
    raise ArgumentError,
          "Expected a hash argument when calling the interaktor, got a #{context.class} instead."
  end
end