class Atacama::Contract

This class enables a DSL for creating a contract for the initializer

Constants

ContextInterface

@private

NameInterface

@private

RESERVED_KEYS

@private

Types

Namespace alias for easier reading when defining types.

Attributes

context[R]

Public Class Methods

call(context = {}, &block) click to toggle source

The main interface to executing contracts. Given a set of options it will check the parameter types as well as return types, if defined.

@param arguments [Hash] keyword arguments that match the defined options

@yield the block is evaluated in the context of the instance call method

@return The value of the call instance method.

# File lib/atacama/contract.rb, line 54
def call(context = {}, &block)
  new(context: context).call(&block).tap { |result| validate_return(result) }
end
inherited(subclass) click to toggle source

Executed by the Ruby VM at subclass time. Ensure that all internal state is copied to the new subclass.

# File lib/atacama/contract.rb, line 109
def inherited(subclass)
  subclass.returns(return_type)

  options.each do |name, parameter|
    subclass.option(name, type: parameter.type)
  end
end
inject(injected) click to toggle source

Inject dependencies statically in to the Contract object. Allows for easier composition of contracts when used in a Transaction.

@example

SampleClass.inject(user: User.new).call(attributes: { name: "Cindy" })

@param injected [Hash] the options to inject in to the initializer

@return [Class] a new class object that contains the injection

# File lib/atacama/contract.rb, line 67
def inject(injected)
  Validator.call({
    options: Hash[injected.keys.zip(options.values_at(*injected.keys))],
    context: Context.new(injected),
    klass: self
  })

  Class.new(self) do
    self.injected = injected
  end
end
injected() click to toggle source

@private

# File lib/atacama/contract.rb, line 123
def injected
  # Silences the VM warning about accessing uninitalized ivar
  defined?(@injected) ? @injected : {}
end
injected=(hash) click to toggle source

@private

# File lib/atacama/contract.rb, line 118
def injected=(hash)
  @injected = Types::Strict::Hash[hash]
end
new(context: {}, **) click to toggle source

@raise [Dry::Types::ConstraintError] a type check failure

@param context [Hash] the values to satisfy the option definition

# File lib/atacama/contract.rb, line 134
def initialize(context: {}, **)
  ContextInterface[context] # Validate the type

  @context = Context.new(self.class.injected).tap do |ctx|
    ctx.merge!(context.is_a?(Context) ? context.to_h : context)
  end

  Validator.call(options: self.class.options, context: @context, klass: self.class)
end
option(name, type: nil) click to toggle source

Define an initializer value.

@example Set an option

option :model. type: Types.Instance(User)

@param name [Symbol] name of the argument @param type [Dry::Type?] the type object to optionally check

# File lib/atacama/contract.rb, line 31
def option(name, type: nil)
  options[NameInterface[name]] = Parameter.new(name: name, type: type)

  define_method(name) { @context[name] }
  define_method("#{name}?") { !!@context[name] }
end
options() click to toggle source

The defined options on the contract.

@return [Hash<String, Atacama::Parameter>]

# File lib/atacama/contract.rb, line 103
def options
  @options ||= {}
end
return_type() click to toggle source

The defined return type on the Contract.

@return [Dry::Type?] the type object to optionally check

# File lib/atacama/contract.rb, line 82
def return_type
  defined?(@returns) && @returns
end
returns(type) click to toggle source

Set the return type for the contract. This is only validated automatically through the call class method.

@param type [Dry::Type?] the type object to optionally check

# File lib/atacama/contract.rb, line 42
def returns(type) # rubocop:disable Style/TrivialAccessors
  @returns = type
end
validate_return(value) click to toggle source

Execute type checking on a value for the defined return value. Useful when executing `new` on these objects.

@raise [Dry::Types::ConstraintError] a type check failure

@param value [Any] the object to type check

# File lib/atacama/contract.rb, line 92
def validate_return(value)
  Atacama.check(return_type, value) do |e|
    raise ReturnTypeMismatchError, Atacama.format_exception(self, e,
      'The return value was an incorrect type.',
    )
  end
end

Public Instance Methods

call() click to toggle source

@abstract The default entrypoint for all Contracts. This is executed and type checked when called from the Class#call.

# File lib/atacama/contract.rb, line 157
def call
  self
end
inspect() click to toggle source

@private

# File lib/atacama/contract.rb, line 145
def inspect
  "#<#{self.class}:0x%x %s>" % [
    object_id,
    self.class.options.keys.map do |option|
      "#{option}: #{context.send(option).inspect[0..20]}"
    end.join(' ')
  ]
end