
gems.virtus-1.0.5.lib.virtus.attribute.hash.rb Maven / Gradle / Ivy
The newest version!
module Virtus
class Attribute
# Handles attributes with Hash type
#
class Hash < Attribute
primitive ::Hash
default primitive.new
# @api private
attr_reader :key_type, :value_type
# FIXME: remove this once axiom-types supports it
#
# @private
Type = Struct.new(:key_type, :value_type) do
def self.infer(type)
if axiom_type?(type)
new(type.key_type, type.value_type)
else
type_options = infer_key_and_value_types(type)
key_class = determine_type(type_options.fetch(:key_type, Object))
value_class = determine_type(type_options.fetch(:value_type, Object))
new(key_class, value_class)
end
end
# @api private
def self.pending?(primitive)
primitive.is_a?(String) || primitive.is_a?(Symbol)
end
# @api private
def self.axiom_type?(type)
type.is_a?(Class) && type < Axiom::Types::Type
end
# @api private
def self.determine_type(type)
return type if pending?(type)
if EmbeddedValue.handles?(type)
type
else
Axiom::Types.infer(type)
end
end
# @api private
def self.infer_key_and_value_types(type)
return {} unless type.kind_of?(::Hash)
if type.size > 1
raise ArgumentError, "more than one [key => value] pair in `#{type}`"
else
key_type, value_type = type.keys.first, type.values.first
key_primitive =
if key_type.is_a?(Class) && key_type < Attribute && key_type.primitive
key_type.primitive
else
key_type
end
value_primitive =
if value_type.is_a?(Class) && value_type < Attribute && value_type.primitive
value_type.primitive
else
value_type
end
{ :key_type => key_primitive, :value_type => value_primitive}
end
end
# @api private
def coercion_method
:to_hash
end
# @api private
def primitive
::Hash
end
end
# @api private
def self.build_type(definition)
Type.infer(definition.type)
end
# @api private
def self.merge_options!(type, options)
options[:key_type] ||= Attribute.build(type.key_type, :strict => options[:strict])
options[:value_type] ||= Attribute.build(type.value_type, :strict => options[:strict])
end
# Coerce members
#
# @see [Attribute#coerce]
#
# @api public
def coerce(*)
coerced = super
return coerced unless coerced.respond_to?(:each_with_object)
coerced.each_with_object({}) do |(key, value), hash|
hash[key_type.coerce(key)] = value_type.coerce(value)
end
end
# @api private
def finalize
return self if finalized?
@key_type = options[:key_type].finalize
@value_type = options[:value_type].finalize
super
end
# @api private
def finalized?
super && key_type.finalized? && value_type.finalized?
end
end # class Hash
end # class Attribute
end # module Virtus
© 2015 - 2025 Weber Informatics LLC | Privacy Policy