/
hash.rb
99 lines (82 loc) · 2.35 KB
/
hash.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
module Virtus
class Attribute
# Hash
#
# @example
# class Post
# include Virtus
#
# attribute :meta, Hash
# end
#
# Post.new(:meta => { :tags => %w(foo bar) })
#
class Hash < Attribute
primitive ::Hash
default primitive.new
# FIXME: remove this once axiom-types supports it
Type = Struct.new(:key_type, :value_type) do
def self.infer(type)
if type.is_a?(Class) && type < Axiom::Types::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
def self.determine_type(type)
if EmbeddedValue.handles?(type)
type
else
Axiom::Types.infer(type)
end
end
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 => type.keys.first, :value_type => type.values.first }
end
end
def coercion_method
:to_hash
end
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)
unless options.key?(:key_type)
options[:key_type] = Attribute.build(type.key_type)
end
unless options.key?(:value_type)
options[:value_type] = Attribute.build(type.value_type)
end
end
# @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 key_type
@options[:key_type]
end
# @api private
def value_type
@options[:value_type]
end
end # class Hash
end # class Attribute
end # module Virtus