virtus2 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +39 -0
- data/.rspec +2 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +18 -0
- data/Changelog.md +258 -0
- data/Gemfile +10 -0
- data/Guardfile +19 -0
- data/LICENSE +20 -0
- data/README.md +630 -0
- data/Rakefile +15 -0
- data/TODO.md +6 -0
- data/lib/virtus/attribute/accessor.rb +103 -0
- data/lib/virtus/attribute/boolean.rb +55 -0
- data/lib/virtus/attribute/builder.rb +182 -0
- data/lib/virtus/attribute/coercer.rb +45 -0
- data/lib/virtus/attribute/coercible.rb +20 -0
- data/lib/virtus/attribute/collection.rb +103 -0
- data/lib/virtus/attribute/default_value/from_callable.rb +35 -0
- data/lib/virtus/attribute/default_value/from_clonable.rb +35 -0
- data/lib/virtus/attribute/default_value/from_symbol.rb +35 -0
- data/lib/virtus/attribute/default_value.rb +51 -0
- data/lib/virtus/attribute/embedded_value.rb +67 -0
- data/lib/virtus/attribute/enum.rb +45 -0
- data/lib/virtus/attribute/hash.rb +130 -0
- data/lib/virtus/attribute/lazy_default.rb +18 -0
- data/lib/virtus/attribute/nullify_blank.rb +24 -0
- data/lib/virtus/attribute/strict.rb +26 -0
- data/lib/virtus/attribute.rb +245 -0
- data/lib/virtus/attribute_set.rb +240 -0
- data/lib/virtus/builder/hook_context.rb +51 -0
- data/lib/virtus/builder.rb +133 -0
- data/lib/virtus/class_inclusions.rb +48 -0
- data/lib/virtus/class_methods.rb +90 -0
- data/lib/virtus/coercer.rb +41 -0
- data/lib/virtus/configuration.rb +72 -0
- data/lib/virtus/const_missing_extensions.rb +18 -0
- data/lib/virtus/extensions.rb +105 -0
- data/lib/virtus/instance_methods.rb +218 -0
- data/lib/virtus/model.rb +68 -0
- data/lib/virtus/module_extensions.rb +88 -0
- data/lib/virtus/support/equalizer.rb +128 -0
- data/lib/virtus/support/options.rb +113 -0
- data/lib/virtus/support/type_lookup.rb +109 -0
- data/lib/virtus/value_object.rb +150 -0
- data/lib/virtus/version.rb +3 -0
- data/lib/virtus.rb +310 -0
- data/spec/integration/attributes_attribute_spec.rb +28 -0
- data/spec/integration/building_module_spec.rb +90 -0
- data/spec/integration/collection_member_coercion_spec.rb +96 -0
- data/spec/integration/custom_attributes_spec.rb +42 -0
- data/spec/integration/custom_collection_attributes_spec.rb +101 -0
- data/spec/integration/default_values_spec.rb +87 -0
- data/spec/integration/defining_attributes_spec.rb +86 -0
- data/spec/integration/embedded_value_spec.rb +50 -0
- data/spec/integration/extending_objects_spec.rb +35 -0
- data/spec/integration/hash_attributes_coercion_spec.rb +54 -0
- data/spec/integration/inheritance_spec.rb +42 -0
- data/spec/integration/injectible_coercers_spec.rb +48 -0
- data/spec/integration/mass_assignment_with_accessors_spec.rb +44 -0
- data/spec/integration/overriding_virtus_spec.rb +46 -0
- data/spec/integration/required_attributes_spec.rb +25 -0
- data/spec/integration/struct_as_embedded_value_spec.rb +28 -0
- data/spec/integration/using_modules_spec.rb +55 -0
- data/spec/integration/value_object_with_custom_constructor_spec.rb +42 -0
- data/spec/integration/virtus/instance_level_attributes_spec.rb +23 -0
- data/spec/integration/virtus/value_object_spec.rb +99 -0
- data/spec/shared/constants_helpers.rb +9 -0
- data/spec/shared/freeze_method_behavior.rb +40 -0
- data/spec/shared/idempotent_method_behaviour.rb +5 -0
- data/spec/shared/options_class_method.rb +19 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +43 -0
- data/spec/unit/virtus/attribute/boolean/value_coerced_predicate_spec.rb +25 -0
- data/spec/unit/virtus/attribute/class_methods/build_spec.rb +180 -0
- data/spec/unit/virtus/attribute/class_methods/coerce_spec.rb +32 -0
- data/spec/unit/virtus/attribute/coerce_spec.rb +129 -0
- data/spec/unit/virtus/attribute/coercible_predicate_spec.rb +20 -0
- data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +105 -0
- data/spec/unit/virtus/attribute/collection/coerce_spec.rb +74 -0
- data/spec/unit/virtus/attribute/collection/value_coerced_predicate_spec.rb +31 -0
- data/spec/unit/virtus/attribute/comparison_spec.rb +20 -0
- data/spec/unit/virtus/attribute/custom_collection_spec.rb +29 -0
- data/spec/unit/virtus/attribute/defined_spec.rb +20 -0
- data/spec/unit/virtus/attribute/embedded_value/class_methods/build_spec.rb +70 -0
- data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +91 -0
- data/spec/unit/virtus/attribute/get_spec.rb +32 -0
- data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +106 -0
- data/spec/unit/virtus/attribute/hash/coerce_spec.rb +92 -0
- data/spec/unit/virtus/attribute/lazy_predicate_spec.rb +20 -0
- data/spec/unit/virtus/attribute/rename_spec.rb +16 -0
- data/spec/unit/virtus/attribute/required_predicate_spec.rb +19 -0
- data/spec/unit/virtus/attribute/set_default_value_spec.rb +107 -0
- data/spec/unit/virtus/attribute/set_spec.rb +29 -0
- data/spec/unit/virtus/attribute/value_coerced_predicate_spec.rb +19 -0
- data/spec/unit/virtus/attribute_set/append_spec.rb +47 -0
- data/spec/unit/virtus/attribute_set/define_reader_method_spec.rb +36 -0
- data/spec/unit/virtus/attribute_set/define_writer_method_spec.rb +36 -0
- data/spec/unit/virtus/attribute_set/each_spec.rb +65 -0
- data/spec/unit/virtus/attribute_set/element_reference_spec.rb +17 -0
- data/spec/unit/virtus/attribute_set/element_set_spec.rb +64 -0
- data/spec/unit/virtus/attribute_set/merge_spec.rb +34 -0
- data/spec/unit/virtus/attribute_set/reset_spec.rb +71 -0
- data/spec/unit/virtus/attribute_spec.rb +229 -0
- data/spec/unit/virtus/attributes_reader_spec.rb +41 -0
- data/spec/unit/virtus/attributes_writer_spec.rb +51 -0
- data/spec/unit/virtus/class_methods/finalize_spec.rb +67 -0
- data/spec/unit/virtus/class_methods/new_spec.rb +39 -0
- data/spec/unit/virtus/config_spec.rb +13 -0
- data/spec/unit/virtus/element_reader_spec.rb +21 -0
- data/spec/unit/virtus/element_writer_spec.rb +19 -0
- data/spec/unit/virtus/freeze_spec.rb +41 -0
- data/spec/unit/virtus/model_spec.rb +197 -0
- data/spec/unit/virtus/module_spec.rb +174 -0
- data/spec/unit/virtus/set_default_attributes_spec.rb +32 -0
- data/spec/unit/virtus/value_object_spec.rb +138 -0
- data/virtus2.gemspec +26 -0
- metadata +225 -0
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rspec/core/rake_task"
|
2
|
+
|
3
|
+
RSpec::Core::RakeTask.new(:spec)
|
4
|
+
task default: [:spec]
|
5
|
+
|
6
|
+
begin
|
7
|
+
require "rubocop/rake_task"
|
8
|
+
|
9
|
+
Rake::Task[:default].enhance [:rubocop]
|
10
|
+
|
11
|
+
RuboCop::RakeTask.new do |task|
|
12
|
+
task.options << "--display-cop-names"
|
13
|
+
end
|
14
|
+
rescue LoadError
|
15
|
+
end
|
data/TODO.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
|
4
|
+
# Accessor extension provides methods to read and write attributes
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
#
|
8
|
+
# attribute = Virtus::Attribute.build(String, :name => :email)
|
9
|
+
# model = Class.new { attr_reader :email }
|
10
|
+
# object = model.new
|
11
|
+
#
|
12
|
+
# attribute.set(object, 'jane@doe.com')
|
13
|
+
# attribute.get(object) # => 'jane@doe.com'
|
14
|
+
#
|
15
|
+
module Accessor
|
16
|
+
|
17
|
+
# Return name of this accessor attribute
|
18
|
+
#
|
19
|
+
# @return [Symbol]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
attr_reader :name
|
23
|
+
|
24
|
+
# Return instance_variable_name used by this accessor
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
attr_reader :instance_variable_name
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
def self.extended(descendant)
|
31
|
+
super
|
32
|
+
name = descendant.options.fetch(:name).to_sym
|
33
|
+
descendant.instance_variable_set('@name', name)
|
34
|
+
descendant.instance_variable_set('@instance_variable_name', "@#{name}")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return if attribute value is defined
|
38
|
+
#
|
39
|
+
# @param [Object] instance
|
40
|
+
#
|
41
|
+
# @return [Boolean]
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def defined?(instance)
|
45
|
+
instance.instance_variable_defined?(instance_variable_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return value of the attribute
|
49
|
+
#
|
50
|
+
# @param [Object] instance
|
51
|
+
#
|
52
|
+
# @return [Object]
|
53
|
+
#
|
54
|
+
# @api public
|
55
|
+
def get(instance)
|
56
|
+
instance.instance_variable_get(instance_variable_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Set value of the attribute
|
60
|
+
#
|
61
|
+
# @param [Object] instance
|
62
|
+
# @param [Object] value
|
63
|
+
#
|
64
|
+
# @return [Object] value that was set
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def set(instance, value)
|
68
|
+
instance.instance_variable_set(instance_variable_name, value)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Set default value
|
72
|
+
#
|
73
|
+
# @param [Object] instance
|
74
|
+
#
|
75
|
+
# @return [Object] value that was set
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
def set_default_value(instance)
|
79
|
+
set(instance, default_value.call(instance, self))
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns a Boolean indicating whether the reader method is public
|
83
|
+
#
|
84
|
+
# @return [Boolean]
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
def public_reader?
|
88
|
+
options[:reader] == :public
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns a Boolean indicating whether the writer method is public
|
92
|
+
#
|
93
|
+
# @return [Boolean]
|
94
|
+
#
|
95
|
+
# @api private
|
96
|
+
def public_writer?
|
97
|
+
options[:writer] == :public
|
98
|
+
end
|
99
|
+
|
100
|
+
end # Accessor
|
101
|
+
|
102
|
+
end # Attribute
|
103
|
+
end # Virtus
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
|
4
|
+
# Boolean attribute allows true or false values to be set
|
5
|
+
# Additionally it adds boolean reader method, like "admin?"
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# class Post
|
9
|
+
# include Virtus
|
10
|
+
#
|
11
|
+
# attribute :published, Boolean
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# post = Post.new(:published => false)
|
15
|
+
# post.published? # => false
|
16
|
+
#
|
17
|
+
class Boolean < Attribute
|
18
|
+
primitive TrueClass
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
def self.build_type(*)
|
22
|
+
Axiom::Types::Boolean
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns if the given value is either true or false
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# boolean = Virtus::Attribute::Boolean.new(:bool)
|
29
|
+
# boolean.value_coerced?(true) # => true
|
30
|
+
# boolean.value_coerced?(false) # => true
|
31
|
+
# boolean.value_coerced?(1) # => false
|
32
|
+
# boolean.value_coerced?('true') # => false
|
33
|
+
#
|
34
|
+
# @return [Boolean]
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def value_coerced?(value)
|
38
|
+
value.equal?(true) || value.equal?(false)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Creates an attribute reader method as a query
|
42
|
+
#
|
43
|
+
# @param [Module] mod
|
44
|
+
#
|
45
|
+
# @return [undefined]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
def define_accessor_methods(attribute_set)
|
49
|
+
super
|
50
|
+
attribute_set.define_reader_method(self, "#{name}?", options[:reader])
|
51
|
+
end
|
52
|
+
|
53
|
+
end # class Boolean
|
54
|
+
end # class Attribute
|
55
|
+
end # module Virtus
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# Attribute placeholder used when type constant is passed as a string or symbol
|
4
|
+
#
|
5
|
+
# @private
|
6
|
+
class PendingAttribute
|
7
|
+
attr_reader :type, :options, :name
|
8
|
+
|
9
|
+
# @api private
|
10
|
+
def initialize(type, options)
|
11
|
+
@type, @options = type.to_s, options
|
12
|
+
@name = options[:name]
|
13
|
+
end
|
14
|
+
|
15
|
+
# @api private
|
16
|
+
def finalize
|
17
|
+
Attribute::Builder.call(determine_type, options).finalize
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
def finalized?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
def determine_type
|
27
|
+
if type.include?('::')
|
28
|
+
Virtus.constantize(type)
|
29
|
+
else
|
30
|
+
Object.const_get(type)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end # PendingAttribute
|
35
|
+
|
36
|
+
# Extracts the actual type primitive from input type
|
37
|
+
#
|
38
|
+
# @private
|
39
|
+
class TypeDefinition
|
40
|
+
attr_reader :type, :primitive
|
41
|
+
|
42
|
+
# @api private
|
43
|
+
def initialize(type)
|
44
|
+
@type = type
|
45
|
+
initialize_primitive
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
def pending?
|
50
|
+
@pending if defined?(@pending)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
def initialize_primitive
|
57
|
+
@primitive =
|
58
|
+
if type.instance_of?(String) || type.instance_of?(Symbol)
|
59
|
+
if !type.to_s.include?('::') && Object.const_defined?(type)
|
60
|
+
Object.const_get(type)
|
61
|
+
elsif not Attribute::Builder.determine_type(type)
|
62
|
+
@pending = true
|
63
|
+
type
|
64
|
+
else
|
65
|
+
type
|
66
|
+
end
|
67
|
+
elsif not type.is_a?(Class)
|
68
|
+
type.class
|
69
|
+
else
|
70
|
+
type
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Attribute
|
76
|
+
|
77
|
+
# Builder is used to set up an attribute instance based on input type and options
|
78
|
+
#
|
79
|
+
# @private
|
80
|
+
class Builder
|
81
|
+
attr_reader :attribute, :options, :type_definition, :klass, :type
|
82
|
+
|
83
|
+
# @api private
|
84
|
+
def self.call(type, options = {})
|
85
|
+
type_definition = TypeDefinition.new(type)
|
86
|
+
|
87
|
+
if type_definition.pending?
|
88
|
+
PendingAttribute.new(type, options)
|
89
|
+
else
|
90
|
+
new(type_definition, options).attribute
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# @api private
|
95
|
+
def self.determine_type(klass, default = nil)
|
96
|
+
type = Attribute.determine_type(klass)
|
97
|
+
|
98
|
+
if klass.is_a?(Class)
|
99
|
+
type ||=
|
100
|
+
if klass < Axiom::Types::Type
|
101
|
+
determine_type(klass.primitive)
|
102
|
+
elsif EmbeddedValue.handles?(klass)
|
103
|
+
EmbeddedValue
|
104
|
+
elsif klass < Enumerable && !(klass <= Range)
|
105
|
+
Collection
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
type || default
|
110
|
+
end
|
111
|
+
|
112
|
+
# @api private
|
113
|
+
def initialize(type_definition, options)
|
114
|
+
@type_definition = type_definition
|
115
|
+
|
116
|
+
initialize_class
|
117
|
+
initialize_type
|
118
|
+
initialize_options(options)
|
119
|
+
initialize_default_value
|
120
|
+
initialize_coercer
|
121
|
+
initialize_attribute
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
# @api private
|
127
|
+
def initialize_class
|
128
|
+
@klass = self.class.determine_type(type_definition.primitive, Attribute)
|
129
|
+
end
|
130
|
+
|
131
|
+
# @api private
|
132
|
+
def initialize_type
|
133
|
+
@type = klass.build_type(type_definition)
|
134
|
+
end
|
135
|
+
|
136
|
+
# @api private
|
137
|
+
def initialize_options(options)
|
138
|
+
@options = klass.options.merge(:coerce => Virtus.coerce).update(options)
|
139
|
+
klass.merge_options!(type, @options)
|
140
|
+
determine_visibility
|
141
|
+
end
|
142
|
+
|
143
|
+
# @api private
|
144
|
+
def initialize_default_value
|
145
|
+
options.update(:default_value => DefaultValue.build(options[:default]))
|
146
|
+
end
|
147
|
+
|
148
|
+
# @api private
|
149
|
+
def initialize_coercer
|
150
|
+
options.update(:coercer => determine_coercer)
|
151
|
+
end
|
152
|
+
|
153
|
+
# @api private
|
154
|
+
def initialize_attribute
|
155
|
+
@attribute = klass.new(type, options)
|
156
|
+
|
157
|
+
@attribute.extend(Accessor) if options[:name]
|
158
|
+
@attribute.extend(Coercible) if options[:coerce]
|
159
|
+
@attribute.extend(NullifyBlank) if options[:nullify_blank]
|
160
|
+
@attribute.extend(Strict) if options[:strict]
|
161
|
+
@attribute.extend(LazyDefault) if options[:lazy]
|
162
|
+
|
163
|
+
@attribute.finalize if options[:finalize]
|
164
|
+
end
|
165
|
+
|
166
|
+
# @api private
|
167
|
+
def determine_coercer
|
168
|
+
options.fetch(:coercer) { klass.build_coercer(type, options) }
|
169
|
+
end
|
170
|
+
|
171
|
+
# @api private
|
172
|
+
def determine_visibility
|
173
|
+
default_accessor = options.fetch(:accessor)
|
174
|
+
reader_visibility = options.fetch(:reader, default_accessor)
|
175
|
+
writer_visibility = options.fetch(:writer, default_accessor)
|
176
|
+
options.update(:reader => reader_visibility, :writer => writer_visibility)
|
177
|
+
end
|
178
|
+
|
179
|
+
end # class Builder
|
180
|
+
|
181
|
+
end # class Attribute
|
182
|
+
end # module Virtus
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
|
4
|
+
# Coercer accessor wrapper
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class Coercer < Virtus::Coercer
|
8
|
+
|
9
|
+
# @api private
|
10
|
+
attr_reader :method, :coercers
|
11
|
+
|
12
|
+
# Initialize a new coercer object
|
13
|
+
#
|
14
|
+
# @param [Object] coercers accessor
|
15
|
+
# @param [Symbol] coercion method
|
16
|
+
#
|
17
|
+
# @return [undefined]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
def initialize(type, coercers)
|
21
|
+
super(type)
|
22
|
+
@method = type.coercion_method
|
23
|
+
@coercers = coercers
|
24
|
+
end
|
25
|
+
|
26
|
+
# Coerce given value
|
27
|
+
#
|
28
|
+
# @return [Object]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
def call(value)
|
32
|
+
coercers[value.class].public_send(method, value)
|
33
|
+
rescue ::Coercible::UnsupportedCoercion
|
34
|
+
value
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api public
|
38
|
+
def success?(primitive, value)
|
39
|
+
coercers[primitive].coerced?(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
end # class Coercer
|
43
|
+
|
44
|
+
end # class Attribute
|
45
|
+
end # module Virtus
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
|
4
|
+
# Attribute extension providing coercion when setting an attribute value
|
5
|
+
#
|
6
|
+
module Coercible
|
7
|
+
|
8
|
+
# Coerce value before setting
|
9
|
+
#
|
10
|
+
# @see Accessor#set
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
def set(instance, value)
|
14
|
+
super(instance, coerce(value))
|
15
|
+
end
|
16
|
+
|
17
|
+
end # Coercible
|
18
|
+
|
19
|
+
end # Attribute
|
20
|
+
end # Virtus
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
|
4
|
+
# Collection attribute handles enumerable-like types
|
5
|
+
#
|
6
|
+
# Handles coercing members to the designated member type.
|
7
|
+
#
|
8
|
+
class Collection < Attribute
|
9
|
+
default Proc.new { |_, attribute| attribute.primitive.new }
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
attr_reader :member_type
|
13
|
+
|
14
|
+
# FIXME: temporary hack, remove when Axiom::Type works with EV as member_type
|
15
|
+
Type = Struct.new(:primitive, :member_type) do
|
16
|
+
def self.infer(type, primitive)
|
17
|
+
return type if axiom_type?(type)
|
18
|
+
|
19
|
+
klass = Axiom::Types.infer(type)
|
20
|
+
member = infer_member_type(type) || Object
|
21
|
+
|
22
|
+
if EmbeddedValue.handles?(member) || pending?(member)
|
23
|
+
Type.new(primitive, member)
|
24
|
+
else
|
25
|
+
klass.new {
|
26
|
+
primitive primitive
|
27
|
+
member_type Axiom::Types.infer(member)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.pending?(primitive)
|
33
|
+
primitive.is_a?(String) || primitive.is_a?(Symbol)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.axiom_type?(type)
|
37
|
+
type.is_a?(Class) && type < Axiom::Types::Type
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.infer_member_type(type)
|
41
|
+
return unless type.respond_to?(:count)
|
42
|
+
|
43
|
+
member_type =
|
44
|
+
if type.count > 1
|
45
|
+
raise NotImplementedError, "build SumType from list of types (#{type})"
|
46
|
+
else
|
47
|
+
type.first
|
48
|
+
end
|
49
|
+
|
50
|
+
if member_type.is_a?(Class) && member_type < Attribute && member_type.primitive
|
51
|
+
member_type.primitive
|
52
|
+
else
|
53
|
+
member_type
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def coercion_method
|
58
|
+
:to_array
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @api private
|
63
|
+
def self.build_type(definition)
|
64
|
+
Type.infer(definition.type, definition.primitive)
|
65
|
+
end
|
66
|
+
|
67
|
+
# @api private
|
68
|
+
def self.merge_options!(type, options)
|
69
|
+
options[:member_type] ||= Attribute.build(type.member_type, strict: options[:strict])
|
70
|
+
end
|
71
|
+
|
72
|
+
# @api public
|
73
|
+
def coerce(value)
|
74
|
+
coerced = super
|
75
|
+
|
76
|
+
return coerced unless coerced.respond_to?(:each_with_object)
|
77
|
+
|
78
|
+
coerced.each_with_object(primitive.new) do |entry, collection|
|
79
|
+
collection << member_type.coerce(entry)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @api public
|
84
|
+
def value_coerced?(value)
|
85
|
+
super && value.all? { |item| member_type.value_coerced? item }
|
86
|
+
end
|
87
|
+
|
88
|
+
# @api private
|
89
|
+
def finalize
|
90
|
+
return self if finalized?
|
91
|
+
@member_type = @options[:member_type].finalize
|
92
|
+
super
|
93
|
+
end
|
94
|
+
|
95
|
+
# @api private
|
96
|
+
def finalized?
|
97
|
+
super && member_type.finalized?
|
98
|
+
end
|
99
|
+
|
100
|
+
end # class Collection
|
101
|
+
|
102
|
+
end # class Attribute
|
103
|
+
end # module Virtus
|