class Stannum::Contracts::TupleContract
A TupleContract
defines constraints for an ordered, indexed object.
In order to match a TupleContract
, the object must respond to the :[], :each, and :size methods, and the items in the object at each index must match the item constraint defined for that index. Finally, unless the :allow_extra_items option is set to true, the object must not have any extra items.
@example Creating A Contract
With Item Constraints
third_base_constraint = Stannum::Constraint.new do |actual| actual == "I Don't Know" end tuple_contract = Stannum::Contracts::TupleContract.new do item { |actual| actual == 'Who' } item { |actual| actual == 'What' } item third_base_constraint end
@example With A Non-Tuple Object
tuple_contract.matches?(nil) #=> false errors = tuple_contract.errors_for(nil) errors.to_a #=> [ { type: 'stannum.constraints.does_not_have_methods', data: { methods: [:[], :each, :size], missing: [:[], :each, :size] }, message: nil, path: [] } ]
@example With An Object With Missing Items
tuple_contract.matches?(['Who']) #=> false errors = tuple_contract.errors_for(['Who']) errors.to_a #=> [ { type: 'stannum.constraints.invalid', data: {}, path: [1], message: nil }, { type: 'stannum.constraints.invalid', data: {}, path: [2], message: nil } ]
@example With An Object With Incorrect Items
tuple_contract.matches?(['What', 'What', "I Don't Know"]) #=> false errors = tuple_contract.errors_for(['What', 'What', "I Don't Know"]) errors.to_a #=> [ { type: 'stannum.constraints.invalid', data: {}, path: [0], message: nil } ]
@example With An Object With Valid Items
tuple_contract.matches?(['Who', 'What', "I Don't Know"]) #=> true errors = tuple_contract.errors_for(['What', 'What', "I Don't Know"]) errors.to_a #=> []
@example With An Object With Extra Items
tuple_contract.matches?(['Who', 'What', "I Don't Know", 'Tomorrow', 'Today']) #=> false errors = tuple_contract.errors_for(['Who', 'What', "I Don't Know", 'Tomorrow', 'Today']) errors.to_a #=> [ { type: 'stannum.constraints.tuples.extra_items', data: {}, path: [3], message: nil }, { type: 'stannum.constraints.tuples.extra_items', data: {}, path: [4], message: nil } ]
Public Class Methods
@param allow_extra_items [true, false] If false, then a tuple with extra
items after the last expected item will not match the contract.
@param options [Hash<Symbol, Object>] Configuration options for the
contract. Defaults to an empty Hash.
Stannum::Contracts::Base::new
# File lib/stannum/contracts/tuple_contract.rb, line 119 def initialize(allow_extra_items: false, **options, &block) super(allow_extra_items: allow_extra_items, **options, &block) end
Public Instance Methods
Adds an index constraint to the contract.
When the contract is called, the contract will find the value of the object at the given index.
@param index [Integer] The index of the value to match. @param constraint [Stannum::Constraints::Base] The constraint to add. @param sanity [true, false] Marks the constraint as a sanity constraint,
which is always matched first and will always short-circuit on a failed match.
@param options [Hash<Symbol, Object>] Options for the constraint. These
can be used by subclasses to define the value and error mappings for the constraint.
@return [self] the contract.
@see Stannum::Contract#add_constraint
.
# File lib/stannum/contracts/tuple_contract.rb, line 140 def add_index_constraint(index, constraint, sanity: false, **options) add_constraint( constraint, property: index, property_type: :index, sanity: sanity, **options ) end
@return [true, false] If false, then a tuple with extra items after the
last expected item will not match the contract.
# File lib/stannum/contracts/tuple_contract.rb, line 152 def allow_extra_items? !!options[:allow_extra_items] end
(see Stannum::Contracts::Base#with_options
)
Stannum::Constraints::Base#with_options
# File lib/stannum/contracts/tuple_contract.rb, line 157 def with_options(**options) return super unless options.key?(:allow_extra_items) raise ArgumentError, "can't change option :allow_extra_items" end
Protected Instance Methods
# File lib/stannum/contracts/tuple_contract.rb, line 165 def expected_count each_constraint.reduce(0) do |count, definition| next count unless definition.options[:property_type] == :index index = 1 + definition.options.fetch(:property, -1) index > count ? index : count end end
Stannum::Contract#map_value
# File lib/stannum/contracts/tuple_contract.rb, line 175 def map_value(actual, **options) return super unless options[:property_type] == :index actual[options[:property]] end
Stannum::Contract#valid_property?
# File lib/stannum/contracts/tuple_contract.rb, line 181 def valid_property?(property: nil, property_type: nil, **options) return super unless property_type == :index property.is_a?(Integer) end
Stannum::Contract#validate_property?
# File lib/stannum/contracts/tuple_contract.rb, line 187 def validate_property?(**options) options[:property_type] == :index || super end
Private Instance Methods
# File lib/stannum/contracts/tuple_contract.rb, line 193 def add_extra_items_constraint return if allow_extra_items? count = -> { expected_count } add_constraint Stannum::Constraints::Tuples::ExtraItems.new(count) end
# File lib/stannum/contracts/tuple_contract.rb, line 201 def add_type_constraint add_constraint Stannum::Constraints::Signatures::Tuple.new, sanity: true end
Stannum::Contracts::Base#define_constraints
# File lib/stannum/contracts/tuple_contract.rb, line 205 def define_constraints(&block) add_type_constraint add_extra_items_constraint super end