attributor 2.6.1 → 3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/CHANGELOG.md +38 -26
- data/lib/attributor.rb +8 -7
- data/lib/attributor/attribute.rb +41 -25
- data/lib/attributor/dsl_compiler.rb +7 -1
- data/lib/attributor/type.rb +10 -8
- data/lib/attributor/types/collection.rb +49 -10
- data/lib/attributor/types/csv.rb +8 -1
- data/lib/attributor/types/hash.rb +45 -11
- data/lib/attributor/types/model.rb +13 -10
- data/lib/attributor/types/string.rb +4 -4
- data/lib/attributor/types/uri.rb +57 -0
- data/lib/attributor/version.rb +1 -1
- data/spec/attribute_spec.rb +136 -64
- data/spec/support/models.rb +7 -2
- data/spec/type_spec.rb +13 -3
- data/spec/types/collection_spec.rb +66 -19
- data/spec/types/csv_spec.rb +14 -3
- data/spec/types/hash_spec.rb +134 -10
- data/spec/types/ids_spec.rb +1 -1
- data/spec/types/model_spec.rb +78 -18
- data/spec/types/string_spec.rb +6 -8
- data/spec/types/uri_spec.rb +12 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9b60ce0b5ac69797dca8bc43ce9164e8a666eeb
|
4
|
+
data.tar.gz: d87a417b41105b3ed4ae10098bd14542d88d7f96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 660346014295ff874c70f0cc79931b98c109c0034e1b235f516108aa5857fa71688a2a2fcd7aab8c9636c91b87a64d1b82d09ae1a7d247b0bf7a23cf232a80d2
|
7
|
+
data.tar.gz: 174c4d2859d2ad0611c55b3c16c70eaa80440fcdee5308fe84a917192d624383e65bfae19c5bd593732ac59ba0d13d908e4c018406f0256fe0f1c30864911fc1
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,26 @@
|
|
1
|
-
Attributor Changelog
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
# Attributor Changelog
|
2
|
+
|
3
|
+
## 3.0.0
|
4
|
+
|
5
|
+
* Small enhancements on `describe` for types
|
6
|
+
* avoid creating empty `:attributes` key for `Model`
|
7
|
+
* ensure embedding `key_type` in `Hash` using `shallow` mode
|
8
|
+
* Added `Hash#delete`.
|
9
|
+
* Changed the schema for describing `Hash` to use `attributes` instead of `keys`
|
10
|
+
* It makes more sense, and it is compatible with Model and Structs too.
|
11
|
+
* Undefine JRuby package helper methods in `Model` (org, java...)
|
12
|
+
* Added support to `Collection.load` for any value that responds to `to_a`
|
13
|
+
* Fixed `Collection.validate` to complain when value object is not a valida type
|
14
|
+
* Fixed bug where defining an attribute that references a `Collection` would not properly support defining sub-attributes in a provided block.
|
15
|
+
* Enhanced the type/attribute `describe` methods of types so that they generate an example if an `example` argument is passed in.
|
16
|
+
* Complex (sub-structured) types will not output examples, only 'leaf' ones.
|
17
|
+
* Improved handling of exceptions during attribute definitions for `Hash`/`Model` that would previously leave the set of attributes in an undefined state. Now, any attempts to use the type will throw an `InvalidDefinition` exception and include the original exception. (#127)
|
18
|
+
* Removed `undef :empty?` from `Model`
|
19
|
+
* Made `Collection` a subclass of Array, and `load` create new instances of it.
|
20
|
+
* Built in proper loading and validation of any `Attribute#example` when the `:example` option is used.
|
21
|
+
|
22
|
+
|
23
|
+
## 2.6.1
|
9
24
|
|
10
25
|
* Add the `:custom_data` option for attributes. This is a hash that is passed through to `describe` - Attributor does no processing or handling of this option.
|
11
26
|
* Added `Type.family` which returns a more-generic "family name". It's defined for all built-in types, and is included in `Type.describe`.
|
@@ -14,8 +29,7 @@ next
|
|
14
29
|
* Fix common hash methods created for example instances (to play well with lazy attributes)
|
15
30
|
* Avoid storing the `Hash#insensitive_map` unless insensitivity enabled
|
16
31
|
|
17
|
-
2.6.0
|
18
|
-
-----
|
32
|
+
## 2.6.0
|
19
33
|
|
20
34
|
* Fixed bug in `example_mixin` where lazy_attributes were not evaluated.
|
21
35
|
* Fixed bug in `Hash` where the class would refuse to load from another `Attributor::Hash` when there were no keys defined and they were seemingly compatible.
|
@@ -27,8 +41,8 @@ next
|
|
27
41
|
* Added `Hash#merge` that works with two identically-typed hashes
|
28
42
|
* Added `Hash#each_pair` for better duck-type compatibility with ::Hash.
|
29
43
|
|
30
|
-
|
31
|
-
|
44
|
+
|
45
|
+
## 2.5.0
|
32
46
|
|
33
47
|
* Partial support for defining `:default` values through Procs.
|
34
48
|
* Note: this is only "partially" supported the `parent` argument of the Proc will NOT contain the correct attribute parent yet. It will contain a fake class, that will loudly complain about any attempt to use any of its methods.
|
@@ -38,17 +52,16 @@ next
|
|
38
52
|
* `Time`, `DateTime`, and `Date` now all return ISO 8601 formatted values from `.dump` (via calling `iso8601` on the value).
|
39
53
|
* Added `Type.id`, a unique value based on the type's class name.
|
40
54
|
|
41
|
-
2.4.0
|
42
|
-
------
|
43
55
|
|
44
|
-
|
56
|
+
## 2.4.0
|
57
|
+
|
58
|
+
* `Model` is now a subclass of `Hash`.
|
45
59
|
* The interface for `Model` instances is almost entirely unchanged, except for the addition of `Hash`-like methods (i.e., you can now do `some_model[:key]` to access attributes).
|
46
60
|
* This fixes numerous incompatabilities between models and hashes, as well as confusing differences between the behavior when loading a model vs a hash.
|
47
61
|
* `String.load` now raises `IncompatibleTypeError` for `Enumerable` values.
|
48
|
-
* Added `Symbol` type, use with caution as it will automatically call `#to_sym` on anything loaded.
|
62
|
+
* Added `Symbol` type, use with caution as it will automatically call `#to_sym` on anything loaded.
|
49
63
|
|
50
|
-
2.3.0
|
51
|
-
------
|
64
|
+
## 2.3.0
|
52
65
|
|
53
66
|
* Added `recurse` option to `Type.load` that is used by `Model` and `Hash` to force the loading of values (specifically, so that default values are assigned) even if the loaded value is `nil`.
|
54
67
|
* Fix `Attributor::CSV` to dump `String` values and generate `String` examples.
|
@@ -59,16 +72,15 @@ next
|
|
59
72
|
* Added `Hash#get`, for retrieving keys using the same logic the `case_insensitive_load` and `allow_extra` with defined `extra` key.
|
60
73
|
|
61
74
|
|
62
|
-
2.2.1
|
63
|
-
------
|
75
|
+
## 2.2.1
|
64
76
|
|
65
77
|
* Dumping attributes will now load the values if they're not in the native type.
|
66
78
|
* `Model.valid_type?` now accepts hashes.
|
67
79
|
* `Hash`:
|
68
80
|
* Added `:has_key?` to delegation
|
69
81
|
|
70
|
-
|
71
|
-
|
82
|
+
|
83
|
+
## 2.2.0
|
72
84
|
|
73
85
|
* Fix example generation for Hash and Collection to handle a non-Array context parameter.
|
74
86
|
* Hash:
|
@@ -78,8 +90,8 @@ next
|
|
78
90
|
* Added `Hash#set` to encapsulate the above options and attribute loading.
|
79
91
|
* Added `extra` command in the `keys` DSL, which lets you define a key (whose value should be a Hash), to group any unspecified keys during load.
|
80
92
|
|
81
|
-
|
82
|
-
|
93
|
+
|
94
|
+
## 2.1.0
|
83
95
|
|
84
96
|
* Structs now inherit type-level options from their reference.
|
85
97
|
* Add Collection subclasses for CSVs and Ids
|
@@ -103,8 +115,8 @@ next
|
|
103
115
|
* Introduced a new FileUpload type. This can be easily used in Web servers to map incoming multipart file uploads.
|
104
116
|
* Introduced a new Tempfile type.
|
105
117
|
|
106
|
-
|
107
|
-
|
118
|
+
|
119
|
+
## 2.0.0
|
108
120
|
|
109
121
|
* Added new exception subtypes (load methods return more precise errors now)
|
110
122
|
* Changed ```Attributor::Model``` to be a class instead of module.
|
data/lib/attributor.rb
CHANGED
@@ -14,7 +14,7 @@ module Attributor
|
|
14
14
|
require_relative 'attributor/attribute_resolver'
|
15
15
|
|
16
16
|
require_relative 'attributor/example_mixin'
|
17
|
-
|
17
|
+
|
18
18
|
require_relative 'attributor/extensions/randexp'
|
19
19
|
|
20
20
|
|
@@ -45,16 +45,16 @@ module Attributor
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def self.humanize_context( context )
|
48
|
-
|
48
|
+
return "" unless context
|
49
49
|
|
50
50
|
if context.kind_of? ::String
|
51
51
|
context = Array(context)
|
52
52
|
end
|
53
53
|
|
54
54
|
unless context.is_a? Enumerable
|
55
|
-
raise "INVALID CONTEXT!!! (got: #{context.inspect})"
|
55
|
+
raise "INVALID CONTEXT!!! (got: #{context.inspect})"
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
begin
|
59
59
|
return context.join('.')
|
60
60
|
rescue Exception => e
|
@@ -73,10 +73,10 @@ module Attributor
|
|
73
73
|
|
74
74
|
require_relative 'attributor/families/numeric'
|
75
75
|
require_relative 'attributor/families/temporal'
|
76
|
-
|
76
|
+
|
77
77
|
require_relative 'attributor/types/container'
|
78
78
|
require_relative 'attributor/types/object'
|
79
|
-
|
79
|
+
|
80
80
|
require_relative 'attributor/types/bigdecimal'
|
81
81
|
require_relative 'attributor/types/integer'
|
82
82
|
require_relative 'attributor/types/string'
|
@@ -90,7 +90,7 @@ module Attributor
|
|
90
90
|
require_relative 'attributor/types/hash'
|
91
91
|
require_relative 'attributor/types/model'
|
92
92
|
require_relative 'attributor/types/struct'
|
93
|
-
|
93
|
+
|
94
94
|
|
95
95
|
require_relative 'attributor/types/csv'
|
96
96
|
require_relative 'attributor/types/ids'
|
@@ -98,5 +98,6 @@ module Attributor
|
|
98
98
|
# TODO: move these to 'optional types' or 'extra types'... location
|
99
99
|
require_relative 'attributor/types/tempfile'
|
100
100
|
require_relative 'attributor/types/file_upload'
|
101
|
+
require_relative 'attributor/types/uri'
|
101
102
|
|
102
103
|
end
|
data/lib/attributor/attribute.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
module Attributor
|
4
4
|
|
5
5
|
class FakeParent < ::BasicObject
|
6
|
-
|
6
|
+
|
7
7
|
def method_missing(name, *args)
|
8
8
|
::Kernel.warn "Warning, you have tried to access the '#{name}' method of the 'parent' argument of a Proc-defined :default values." +
|
9
9
|
"Those Procs should completely ignore the 'parent' attribute for the moment as it will be set to an " +
|
10
10
|
"instance of a useless class (until the framework can provide such functionality)"
|
11
11
|
nil
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def class
|
15
15
|
FakeParent
|
16
16
|
end
|
@@ -80,7 +80,7 @@ module Attributor
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def dump(value, **opts)
|
83
|
-
type.dump(value, opts)
|
83
|
+
type.dump(value, **opts)
|
84
84
|
end
|
85
85
|
|
86
86
|
|
@@ -98,7 +98,7 @@ module Attributor
|
|
98
98
|
|
99
99
|
TOP_LEVEL_OPTIONS = [ :description, :values, :default, :example, :required, :required_if, :custom_data ]
|
100
100
|
INTERNAL_OPTIONS = [:dsl_compiler,:dsl_compiler_options] # Options we don't want to expose when describing attributes
|
101
|
-
def describe(shallow=true)
|
101
|
+
def describe(shallow=true, example: nil )
|
102
102
|
description = { }
|
103
103
|
# Clone the common options
|
104
104
|
TOP_LEVEL_OPTIONS.each do |option_name|
|
@@ -109,6 +109,7 @@ module Attributor
|
|
109
109
|
if ( ex_def = description.delete(:example) )
|
110
110
|
description[:example_definition] = ex_def
|
111
111
|
end
|
112
|
+
|
112
113
|
special_options = self.options.keys - TOP_LEVEL_OPTIONS - INTERNAL_OPTIONS
|
113
114
|
description[:options] = {} unless special_options.empty?
|
114
115
|
special_options.each do |opt_name|
|
@@ -119,40 +120,55 @@ module Attributor
|
|
119
120
|
description[:options][:reference] = reference.name
|
120
121
|
end
|
121
122
|
|
122
|
-
description[:type] = self.type.describe(shallow)
|
123
|
+
description[:type] = self.type.describe(shallow, example: example )
|
124
|
+
# Move over any example from the type, into the attribute itself
|
125
|
+
if ( ex = description[:type].delete(:example) )
|
126
|
+
description[:example] = self.dump( ex ).to_s
|
127
|
+
end
|
128
|
+
|
123
129
|
description
|
124
130
|
end
|
125
131
|
|
126
132
|
|
133
|
+
def example_from_options(parent, context)
|
134
|
+
val = self.options[:example]
|
135
|
+
generated = case val
|
136
|
+
when ::Regexp
|
137
|
+
val.gen
|
138
|
+
when ::Array
|
139
|
+
# TODO: handle arrays of non native types, i.e. arrays of regexps.... ?
|
140
|
+
val.pick
|
141
|
+
when ::Proc
|
142
|
+
if val.arity == 2
|
143
|
+
val.call(parent, context)
|
144
|
+
elsif val.arity == 1
|
145
|
+
val.call(parent)
|
146
|
+
else
|
147
|
+
val.call
|
148
|
+
end
|
149
|
+
when nil
|
150
|
+
nil
|
151
|
+
else
|
152
|
+
val
|
153
|
+
end
|
154
|
+
self.load( generated, context )
|
155
|
+
end
|
156
|
+
|
127
157
|
def example(context=nil, parent: nil, values:{})
|
128
158
|
raise ArgumentError, "attribute example cannot take a context of type String" if (context.is_a? ::String )
|
129
159
|
if context
|
130
160
|
ctx = Attributor.humanize_context(context)
|
131
161
|
seed, _ = Digest::SHA1.digest(ctx).unpack("QQ")
|
132
162
|
Random.srand(seed)
|
163
|
+
else
|
164
|
+
context = Attributor::DEFAULT_ROOT_CONTEXT
|
133
165
|
end
|
134
166
|
|
135
167
|
if self.options.has_key? :example
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
when ::Array
|
141
|
-
# TODO: handle arrays of non native types, i.e. arrays of regexps.... ?
|
142
|
-
val.pick
|
143
|
-
when ::Proc
|
144
|
-
if val.arity == 2
|
145
|
-
val.call(parent, context)
|
146
|
-
elsif val.arity == 1
|
147
|
-
val.call(parent)
|
148
|
-
else
|
149
|
-
val.call
|
150
|
-
end
|
151
|
-
when nil
|
152
|
-
nil
|
153
|
-
else
|
154
|
-
self.load(val)
|
155
|
-
end
|
168
|
+
loaded = example_from_options(parent, context)
|
169
|
+
errors = self.validate(loaded, context)
|
170
|
+
raise AttributorException, "Error generating example for #{Attributor.humanize_context(context)}. Errors: #{errors.inspect}" if errors.any?
|
171
|
+
loaded
|
156
172
|
else
|
157
173
|
if (option_values = self.options[:values])
|
158
174
|
option_values.pick
|
@@ -100,7 +100,13 @@ module Attributor
|
|
100
100
|
# determine attribute type to use
|
101
101
|
if attr_type.nil?
|
102
102
|
if block_given?
|
103
|
-
attr_type = Attributor::
|
103
|
+
attr_type = if inherited_attribute && inherited_attribute.type < Attributor::Collection
|
104
|
+
# override the reference to be the member_attribute's type for collections
|
105
|
+
opts[:reference] = inherited_attribute.type.member_attribute.type
|
106
|
+
Attributor::Collection.of(Struct)
|
107
|
+
else
|
108
|
+
Attributor::Struct
|
109
|
+
end
|
104
110
|
elsif inherited_attribute
|
105
111
|
attr_type = inherited_attribute.type
|
106
112
|
else
|
data/lib/attributor/type.rb
CHANGED
@@ -10,9 +10,9 @@ module Attributor
|
|
10
10
|
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
-
|
14
|
-
# Does this type support the generation of subtypes?
|
15
|
-
def constructable?
|
13
|
+
|
14
|
+
# Does this type support the generation of subtypes?
|
15
|
+
def constructable?
|
16
16
|
false
|
17
17
|
end
|
18
18
|
|
@@ -20,7 +20,7 @@ module Attributor
|
|
20
20
|
def load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
|
21
21
|
return nil if value.nil?
|
22
22
|
unless value.is_a?(self.native_type)
|
23
|
-
raise Attributor::IncompatibleTypeError, context: context, value_type: value.class, type: self
|
23
|
+
raise Attributor::IncompatibleTypeError, context: context, value_type: value.class, type: self
|
24
24
|
end
|
25
25
|
|
26
26
|
value
|
@@ -89,7 +89,7 @@ module Attributor
|
|
89
89
|
|
90
90
|
|
91
91
|
def generate_subcontext(context, subname)
|
92
|
-
context + [subname]
|
92
|
+
context + [subname]
|
93
93
|
end
|
94
94
|
|
95
95
|
def dsl_compiler
|
@@ -105,15 +105,17 @@ module Attributor
|
|
105
105
|
end
|
106
106
|
|
107
107
|
# Default describe for simple types...only their name (stripping the base attributor module)
|
108
|
-
def describe(root=false)
|
108
|
+
def describe(root=false, example: nil)
|
109
109
|
type_name = self.ancestors.find { |k| k.name && !k.name.empty? }.name
|
110
|
-
{
|
110
|
+
hash = {
|
111
111
|
name: type_name.gsub(Attributor::MODULE_PREFIX_REGEX, ''),
|
112
112
|
family: self.family,
|
113
113
|
id: self.id
|
114
114
|
}
|
115
|
+
hash[:example] = example if example
|
116
|
+
hash
|
115
117
|
end
|
116
|
-
|
118
|
+
|
117
119
|
def id
|
118
120
|
return nil if self.name.nil?
|
119
121
|
self.name.gsub('::'.freeze,'-'.freeze)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
module Attributor
|
5
5
|
|
6
|
-
class Collection
|
6
|
+
class Collection < Array
|
7
7
|
include Container
|
8
8
|
|
9
9
|
# @param type [Attributor::Type] optional, defines the type of all collection members
|
@@ -21,14 +21,31 @@ module Attributor
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
@options = {}
|
25
|
+
|
26
|
+
def self.inherited(klass)
|
27
|
+
klass.instance_eval do
|
28
|
+
@options = {}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.options
|
33
|
+
@options
|
34
|
+
end
|
35
|
+
|
36
|
+
|
24
37
|
def self.native_type
|
25
|
-
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.valid_type?(type)
|
42
|
+
type.kind_of?(self) || type.kind_of?(::Enumerable)
|
26
43
|
end
|
27
44
|
|
28
45
|
def self.family
|
29
46
|
'array'
|
30
47
|
end
|
31
|
-
|
48
|
+
|
32
49
|
def self.member_type
|
33
50
|
@member_type ||= Attributor::Object
|
34
51
|
end
|
@@ -51,16 +68,20 @@ module Attributor
|
|
51
68
|
context ||= ["Collection-#{result.object_id}"]
|
52
69
|
context = Array(context)
|
53
70
|
|
71
|
+
# avoid infinite recursion in example generation
|
72
|
+
example_depth = context.size
|
73
|
+
size = 0 if example_depth > Hash::MAX_EXAMPLE_DEPTH
|
74
|
+
|
54
75
|
size.times do |i|
|
55
76
|
subcontext = context + ["at(#{i})"]
|
56
77
|
result << self.member_attribute.example(subcontext)
|
57
78
|
end
|
58
79
|
|
59
|
-
result
|
80
|
+
self.new(result)
|
60
81
|
end
|
61
82
|
|
62
83
|
|
63
|
-
# The incoming value should be
|
84
|
+
# The incoming value should be array-like here, so the only decoding that we need to do
|
64
85
|
# is from the members (if there's an :member_type defined option).
|
65
86
|
def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
|
66
87
|
if value.nil?
|
@@ -69,11 +90,13 @@ module Attributor
|
|
69
90
|
loaded_value = value
|
70
91
|
elsif value.is_a?(::String)
|
71
92
|
loaded_value = decode_string(value,context)
|
93
|
+
elsif value.respond_to?(:to_a)
|
94
|
+
loaded_value = value.to_a
|
72
95
|
else
|
73
96
|
raise Attributor::IncompatibleTypeError, context: context, value_type: value.class, type: self
|
74
97
|
end
|
75
98
|
|
76
|
-
|
99
|
+
self.new(loaded_value.collect { |member| self.member_attribute.load(member,context) })
|
77
100
|
end
|
78
101
|
|
79
102
|
|
@@ -87,20 +110,21 @@ module Attributor
|
|
87
110
|
values.collect { |value| member_attribute.dump(value,opts) }
|
88
111
|
end
|
89
112
|
|
90
|
-
def self.describe(shallow=false)
|
113
|
+
def self.describe(shallow=false, example: nil)
|
91
114
|
hash = super(shallow)
|
92
115
|
hash[:options] = {} unless hash[:options]
|
93
|
-
|
116
|
+
member_example = example.first if example
|
117
|
+
hash[:member_attribute] = self.member_attribute.describe(true, example: member_example )
|
94
118
|
hash
|
95
119
|
end
|
96
120
|
|
97
121
|
|
98
|
-
def self.constructable?
|
122
|
+
def self.constructable?
|
99
123
|
true
|
100
124
|
end
|
101
125
|
|
102
|
-
def self.construct(constructor_block, options)
|
103
126
|
|
127
|
+
def self.construct(constructor_block, options)
|
104
128
|
member_options = (options[:member_options] || {} ).clone
|
105
129
|
if options.has_key?(:reference) && !member_options.has_key?(:reference)
|
106
130
|
member_options[:reference] = options[:reference]
|
@@ -131,6 +155,16 @@ module Attributor
|
|
131
155
|
|
132
156
|
# @param values [Array] Array of values to validate
|
133
157
|
def self.validate(values, context=Attributor::DEFAULT_ROOT_CONTEXT, attribute=nil)
|
158
|
+
|
159
|
+
unless self.valid_type?(values)
|
160
|
+
descriptive_type =if self.member_type != Object
|
161
|
+
"Collection.of(#{self.member_type})"
|
162
|
+
else
|
163
|
+
self
|
164
|
+
end
|
165
|
+
raise Attributor::IncompatibleTypeError, context: context, value_type: values.class, type: descriptive_type
|
166
|
+
end
|
167
|
+
|
134
168
|
values.each_with_index.collect do |value, i|
|
135
169
|
subcontext = context + ["at(#{i})"]
|
136
170
|
self.member_attribute.validate(value, subcontext)
|
@@ -142,5 +176,10 @@ module Attributor
|
|
142
176
|
errors
|
143
177
|
end
|
144
178
|
|
179
|
+
|
180
|
+
def dump(**opts)
|
181
|
+
self.collect { |value| self.class.member_attribute.dump(value,opts) }
|
182
|
+
end
|
183
|
+
|
145
184
|
end
|
146
185
|
end
|