Module: Axn::Core::Contract::InstanceMethods

Defined in:
lib/axn/core/contract.rb

Instance Method Summary collapse

Instance Method Details

#clear_execution_contextObject

Clear any previously set additional execution context



2023
2024
2025
# File 'lib/axn/core/contract.rb', line 2023

def clear_execution_context
  @__additional_execution_context = nil
end

#execution_contextObject

Returns a structured hash for exception reporting and handlers. Contains :inputs, :outputs, any extra keys from set_execution_context / additional_execution_context hook, and (when present) a sensitive-filtered :ambient_context. Framework-owned keys (RESERVED_EXECUTION_CONTEXT_KEYS) from extra context are stripped before merging.



2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
# File 'lib/axn/core/contract.rb', line 2031

def execution_context
  explicit_context = @__additional_execution_context || {}
  hook_context = respond_to?(:additional_execution_context, true) ? additional_execution_context : {}
  extra_context = explicit_context.merge(hook_context).except(*RESERVED_EXECUTION_CONTEXT_KEYS)

  ctx = {
    inputs: _safe_execution_context_slice { inputs_for_logging },
    outputs: _safe_execution_context_slice { outputs_for_logging },
    **extra_context,
  }

  # Resolving/filtering ambient context can raise (e.g. a failing ambient_context_provider
  # whose error is now memoized and re-raised on every read — see
  # Axn::Core::AmbientContext#ambient_context). Building exception-report context must never
  # itself raise, or the real exception never reaches Axn.config.on_exception, so omit
  # ambient_context here rather than propagate.
  ambient = _safe_execution_context_slice do
    ambient_filter = self.class._has_dynamic_sensitive_fields? ? self.class._build_instance_filter(self) : self.class.inspection_filter
    masked = self.class._mask_unfilterable_shapes(ambient_context, self.class._sensitive_ambient_shape_paths(self), self)
    self.class.send(:_filter_tolerating_cycles, ambient_filter, masked)
  end
  ctx[:ambient_context] = ambient if ambient.present?
  ctx
end

#expose(*args, **kwargs) ⇒ Object

Accepts:

  • a single Axn::Result: forwards (result.declared_fields & own outbound declared fields)
  • two positional arguments (key, value)
  • a hash of key/value pairs


1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
# File 'lib/axn/core/contract.rb', line 1988

def expose(*args, **kwargs)
  return _expose_from_result(args.first) if args.size == 1 && kwargs.empty? && args.first.is_a?(Axn::Result)

  if args.any?
    if args.size != 2
      raise ArgumentError,
            "expose must be called with exactly two positional arguments (or a hash of key/value pairs)"
    end

    kwargs.merge!(args.first => args.last)
  end

  kwargs.each do |key, value|
    # Symbolize the exposure key to match the symbol-canonical outbound contract (PRO-2790):
    # `exposes "saved"` declares `:saved`, and the result facade / outbound validation read
    # `exposed_data[:saved]`. Without this a string-keyed write (`expose("saved", v)`,
    # `expose("saved" => v)`, or a string `expose_return_as`) would store under "saved" and
    # the declared field would read nil.
    key = key.to_sym

    raise Axn::ContractViolation::UnknownExposure, key unless result.respond_to?(key)

    @__context.exposed_data[key] = value
  end
end

#inputsObject

Resolved declared-inbound fields as a Hash (defaults/preprocess applied, model: fields resolved to their record), keyed by wire key. Splat into a nested action to forward inputs: Child.call(**inputs, override: x). Reads through internal_context (not raw provided_data) so a model: field supplied by <field>_id forwards the resolved record — the record lives only in the reader. Fields whose resolved value is nil are omitted, so a nested action still applies its own absent/default handling for them.



1975
1976
1977
1978
1979
1980
# File 'lib/axn/core/contract.rb', line 1975

def inputs
  self.class._declared_fields(:inbound).each_with_object({}) do |field, hash|
    value = internal_context.public_send(field)
    hash[field] = value unless value.nil?
  end
end

#internal_contextObject



1966
# File 'lib/axn/core/contract.rb', line 1966

def internal_context = @__internal_context ||= _build_context_facade(:inbound)

#resultObject



1967
# File 'lib/axn/core/contract.rb', line 1967

def result = @__result ||= _build_context_facade(:outbound)

#set_execution_context(**kwargs) ⇒ Object

Set additional context to be included in execution_context for exception reporting/handlers. This context is NOT included in automatic pre/post logging (which only logs inputs/outputs). Framework-owned keys (RESERVED_EXECUTION_CONTEXT_KEYS) are stripped before merging.



2017
2018
2019
2020
# File 'lib/axn/core/contract.rb', line 2017

def set_execution_context(**kwargs)
  @__additional_execution_context ||= {}
  @__additional_execution_context.merge!(kwargs.except(*RESERVED_EXECUTION_CONTEXT_KEYS))
end