class Stannum::Constraints::Types::HashType
A Hash type constraint asserts that the object is a Hash.
@example Using a Hash type constraint
constraint = Stannum::Constraints::Types::HashType.new constraint.matches?(nil) # => false constraint.matches?(Object.new) # => false constraint.matches?({}) # => true constraint.matches?({ key: 'value' }) # => true
@example Using a Hash type constraint with a key constraint
constraint = Stannum::Constraints::Types::HashType.new(key_type: String) constraint.matches?(nil) # => false constraint.matches?(Object.new) # => false constraint.matches?({}) # => true constraint.matches?({ key: 'value' }) # => false constraint.matches?({ 'key' => 'value' }) # => true
@example Using a Hash type constraint with a value constraint
constraint = Stannum::Constraints::Types::HashType.new(value_type: String) constraint.matches?(nil) # => false constraint.matches?(Object.new) # => false constraint.matches?({}) # => true constraint.matches?({ key: :value }) # => false constraint.matches?({ key: 'value' }) # => true
@example Using a Hash type constraint with a presence constraint
constraint = Stannum::Constraints::Types::HashType.new(allow_empty: false) constraint.matches?(nil) # => false constraint.matches?(Object.new) # => false constraint.matches?({}) # => false constraint.matches?({ key: :value }) # => true constraint.matches?({ key: 'value' }) # => true
Public Class Methods
@param allow_empty [true, false] If false, then the constraint will not
match against a Hash with no keys.
@param key_type
[Stannum::Constraints::Base, Class, nil] If set, then the
constraint will check the types of each key in the Hash against the expected type and will fail if any keys do not match.
@param value_type
[Stannum::Constraints::Base, Class, nil] If set, then
the constraint will check the types of each value in the Hash against the expected type and will fail if any values do not match.
@param options [Hash<Symbol, Object>] Configuration options for the
constraint. Defaults to an empty Hash.
Stannum::Constraints::Type::new
# File lib/stannum/constraints/types/hash_type.rb, line 54 def initialize(allow_empty: true, key_type: nil, value_type: nil, **options) super( ::Hash, allow_empty: !!allow_empty, key_type: coerce_key_type(key_type), value_type: coerce_value_type(value_type), **options ) end
Public Instance Methods
@return [true, false] if false, then the constraint will not
match against a Hash with no keys.
# File lib/stannum/constraints/types/hash_type.rb, line 66 def allow_empty? options[:allow_empty] end
Checks that the object is not a Hash instance.
@return [true, false] true if the object is not a Hash instance, otherwise
false.
@see Stannum::Constraints::Types::HashType#matches?
# File lib/stannum/constraints/types/hash_type.rb, line 76 def does_not_match?(actual) !matches_type?(actual) end
(see Stannum::Constraints::Base#errors_for
)
Stannum::Constraints::Type#errors_for
# File lib/stannum/constraints/types/hash_type.rb, line 81 def errors_for(actual, errors: nil) return super unless actual.is_a?(expected_type) errors ||= Stannum::Errors.new return add_presence_error(errors) unless presence_matches?(actual) update_key_errors_for(actual: actual, errors: errors) update_value_errors_for(actual: actual, errors: errors) errors end
@return [Stannum::Constraints::Base, nil] the expected type for the keys
in the hash.
# File lib/stannum/constraints/types/hash_type.rb, line 97 def key_type options[:key_type] end
Checks that the object is a Hash instance and that the keys/values match.
If the constraint was configured with a key_type
, each key in the hash will be compared to the expected type. Likewise, if the constraint was configured with a value_type
, each value in the hash will be compared. If any keys and/or values do not match the expectation, then matches?
will return false.
@return [true, false] true if the object is a Hash instance with matching
keys and values, otherwise false.
@see Stannum::Constraints::Types::HashType#does_not_match?
Stannum::Constraints::Type#matches?
# File lib/stannum/constraints/types/hash_type.rb, line 113 def matches?(actual) return false unless super return false unless presence_matches?(actual) return false unless key_type_matches?(actual) return false unless value_type_matches?(actual) true end
@return [Stannum::Constraints::Base, nil] the expected type for the values
in the hash.
# File lib/stannum/constraints/types/hash_type.rb, line 128 def value_type options[:value_type] end
Private Instance Methods
# File lib/stannum/constraints/types/hash_type.rb, line 134 def add_presence_error(errors) errors.add( Stannum::Constraints::Presence::TYPE, **error_properties ) end
# File lib/stannum/constraints/types/hash_type.rb, line 141 def coerce_key_type(key_type) Stannum::Support::Coercion.type_constraint( key_type, allow_nil: true, as: 'key type' ) end
# File lib/stannum/constraints/types/hash_type.rb, line 149 def coerce_value_type(value_type) Stannum::Support::Coercion.type_constraint( value_type, allow_nil: true, as: 'value type' ) end
Stannum::Constraints::Type#error_properties
# File lib/stannum/constraints/types/hash_type.rb, line 157 def error_properties super().merge(allow_empty: allow_empty?) end
# File lib/stannum/constraints/types/hash_type.rb, line 161 def key_type_matches?(actual) return true unless key_type return true if actual.nil? actual.each_key.all? { |key| key_type.matches?(key) } end
# File lib/stannum/constraints/types/hash_type.rb, line 169 def non_matching_keys(actual) return [] unless key_type && actual.is_a?(Hash) actual.each_key.reject { |key| key_type.matches?(key) } end
# File lib/stannum/constraints/types/hash_type.rb, line 175 def non_matching_values(actual) return [] unless value_type && actual.is_a?(Hash) actual.each.reject { |_, value| value_type.matches?(value) } end
# File lib/stannum/constraints/types/hash_type.rb, line 181 def presence_matches?(actual) allow_empty? || !actual.empty? end
# File lib/stannum/constraints/types/hash_type.rb, line 185 def update_key_errors_for(actual:, errors:) non_matching_keys(actual).each do |key| mapped_key = Stannum::Support::Coercion.error_key(key) key_type.errors_for(key, errors: errors[:keys][mapped_key]) end end
# File lib/stannum/constraints/types/hash_type.rb, line 193 def update_value_errors_for(actual:, errors:) non_matching_values(actual).each do |key, value| mapped_key = Stannum::Support::Coercion.error_key(key) value_type.errors_for(value, errors: errors[mapped_key]) end end
# File lib/stannum/constraints/types/hash_type.rb, line 201 def value_type_matches?(actual) return true unless value_type return true if actual.nil? actual.each_value.all? { |value| value_type.matches?(value) } end