Class: Stannum::Contracts::MapContract

Inherits:
Stannum::Contract show all
Defined in:
lib/stannum/contracts/map_contract.rb

Overview

A MapContract defines constraints on an hash-like object’s values.

Examples:

Creating A Map Contract

Response     = Struct.new(:ok, :data, :signature)
map_contract = Stannum::Contracts::MapContract.new

map_contract.add_constraint(
  negated_type:  'example.is_boolean',
  property:      :ok,
  property_type: :key,
  type:          'example.is_not_boolean'
) { |actual| actual == true || actual == false }
map_contract.add_constraint(
  Stannum::Constraints::Type.new(Hash),
  property:      :data,
  property_type: :key,
)
map_contract.add_constraint(
  Stannum::Constraints::Presence.new,
  property:      :signature,
  property_type: :key,
)

With A Non-Map Object

map_contract.matches?(nil) #=> false
errors = map_contract.errors_for(nil)
#=> [
  {
    type:    'stannum.constraints.does_not_have_methods',
    data:    { methods: [:[], :each, :keys], missing: [:[], :each, :keys] },
    message: nil,
    path:    []
  }
]
map_contract.does_not_match?(nil)          #=> true
map_contract.negated_errors_for?(nil).to_a #=> []

With An Object That Matches None Of The Key Constraints

response = Response.new
map_contract.matches?(response) #=> false
errors = map_contract.errors_for(response)
errors.to_a
#=> [
  { type: 'is_not_boolean', data: {}, path: [:ok], message: nil },
  { type: 'is_not_type', data: { type: Hash }, path: [:data], message: nil },
  { type: 'absent', data: {}, path: [:signature], message: nil }
]

With An Object That Matches Some Of The Key Constraints

response = Response.new(true, nil, '')
map_contract.matches?(response) #=> false
errors = map_contract.errors_for(response)
errors.to_a
#=> [
  { type: 'is_not_type', data: { type: Hash }, path: [:data], message: nil },
  { type: 'absent', data: {}, path: [:signature], message: nil }
]

With An Object That Matches All Of The Key Constraints

response = Response.new(true, {}, 'abc')
hash_contract.matches?(response)        #=> true
hash_contract.errors_for(response).to_a #=> []

Direct Known Subclasses

HashContract

Defined Under Namespace

Classes: Builder

Constant Summary

Constants inherited from Stannum::Constraints::Base

Stannum::Constraints::Base::NEGATED_TYPE, Stannum::Constraints::Base::TYPE

Instance Attribute Summary

Attributes inherited from Stannum::Constraints::Base

#options

Instance Method Summary collapse

Methods inherited from Stannum::Contract

#add_constraint, #add_property_constraint

Methods inherited from Base

#==, #add_constraint, #concat, #does_not_match?, #each_constraint, #each_pair, #errors_for, #match, #matches?, #negated_errors_for, #negated_match

Methods inherited from Stannum::Constraints::Base

#==, #clone, #does_not_match?, #dup, #errors_for, #match, #matches?, #message, #negated_errors_for, #negated_match, #negated_message, #negated_type, #type

Constructor Details

#initialize(allow_extra_keys: false, **options, &block) ⇒ MapContract

Returns a new instance of MapContract.

Parameters:

  • allow_extra_keys (true, false) (defaults to: false)

    If true, the contract will match hashes with keys that are not constrained by the contract.

  • options (Hash<Symbol, Object>)

    Configuration options for the contract. Defaults to an empty Hash.



105
106
107
108
109
110
111
112
113
114
115
# File 'lib/stannum/contracts/map_contract.rb', line 105

def initialize(
  allow_extra_keys: false,
  **options,
  &block
)
  super(
    allow_extra_keys: allow_extra_keys,
    **options,
    &block
  )
end

Instance Method Details

#add_key_constraint(key, constraint, sanity: false, **options) ⇒ self

Adds a key constraint to the contract.

When the contract is called, the contract will find the value of the object for the given key.

Parameters:

  • key (Integer)

    The key of the value to match.

  • constraint (Stannum::Constraints::Base)

    The constraint to add.

  • sanity (true, false) (defaults to: false)

    Marks the constraint as a sanity constraint, which is always matched first and will always short-circuit on a failed match.

  • options (Hash<Symbol, Object>)

    Options for the constraint. These can be used by subclasses to define the value and error mappings for the constraint.

Returns:

  • (self)

    the contract.

See Also:

  • Stannum::Contract#add_constraint.


134
135
136
137
138
139
140
141
142
# File 'lib/stannum/contracts/map_contract.rb', line 134

def add_key_constraint(key, constraint, sanity: false, **options)
  add_constraint(
    constraint,
    property:      key,
    property_type: :key,
    sanity:        sanity,
    **options
  )
end

#allow_extra_keys?true, false

Returns if true, the contract will match hashes with keys that are not constrained by the contract.

Returns:

  • (true, false)

    if true, the contract will match hashes with keys that are not constrained by the contract.



146
147
148
# File 'lib/stannum/contracts/map_contract.rb', line 146

def allow_extra_keys?
  options[:allow_extra_keys]
end

#expected_keysArray

Returns the list of keys expected by the key constraints.

Returns:

  • (Array)

    the list of keys expected by the key constraints.



151
152
153
154
155
156
157
# File 'lib/stannum/contracts/map_contract.rb', line 151

def expected_keys
  each_constraint.reduce([]) do |keys, definition|
    next keys unless definition.options[:property_type] == :key

    keys << definition.options.fetch(:property)
  end
end

#with_options(**options) ⇒ Object

Raises:

  • (ArgumentError)


160
161
162
163
164
# File 'lib/stannum/contracts/map_contract.rb', line 160

def with_options(**options)
  return super unless options.key?(:allow_extra_keys)

  raise ArgumentError, "can't change option :allow_extra_keys"
end