attributor 7.0 → 8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -3
- data/CHANGELOG.md +11 -2
- data/attributor.gemspec +3 -2
- data/lib/attributor/attribute.rb +23 -11
- data/lib/attributor/extras/field_selector.rb +1 -1
- data/lib/attributor/types/bigdecimal.rb +1 -1
- data/lib/attributor/types/date.rb +2 -2
- data/lib/attributor/types/date_time.rb +3 -2
- data/lib/attributor/types/float.rb +1 -1
- data/lib/attributor/types/polymorphic.rb +1 -1
- data/lib/attributor/types/string.rb +1 -10
- data/lib/attributor/types/tempfile.rb +1 -1
- data/lib/attributor/types/time.rb +3 -3
- data/lib/attributor/types/uri.rb +1 -1
- data/lib/attributor/version.rb +1 -1
- data/lib/attributor.rb +4 -5
- data/spec/attribute_spec.rb +11 -25
- data/spec/support/hashes.rb +1 -1
- data/spec/support/models.rb +16 -16
- data/spec/types/csv_spec.rb +1 -1
- data/spec/types/date_spec.rb +6 -6
- data/spec/types/float_spec.rb +4 -2
- data/spec/types/model_spec.rb +1 -1
- data/spec/types/string_spec.rb +0 -8
- metadata +9 -10
- data/lib/attributor/extensions/randexp.rb +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1bec1eaa11a8711121f24b64eb86dcedc3779012f3ccc2239882bfa38527a0db
|
|
4
|
+
data.tar.gz: 15e6274f319f78d5c7801015ed5a97fd3858727ce98956a2e3b0229c89bdf25c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1055363cc837a07b3c319f0cfec62293797361a713354c146e3c1bead0b2fa0cbb2313dfcf092400abc57f5101a6fc76b782ba51bb9876c09720c2a382bb09de
|
|
7
|
+
data.tar.gz: 0aea79491d61b6ab866aec643c93d18bd372b8afc63428ba69d4a9723d0fd55a24589ea182a4ed2a0029e30a55f522981010f5637a54ea98af68ec0f41b48ec7
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Attributor Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 8.0
|
|
4
|
+
|
|
5
|
+
- Add support for Ruby 3.2.
|
|
6
|
+
- Remove usage of Randexp for examples and replace with Faker.
|
|
7
|
+
- The above means this also removes support for generating examples using regular expressions.
|
|
8
|
+
|
|
9
|
+
## 7.1 (6/9/2023)
|
|
10
|
+
- Enhance custom_option handling. The values provided for custom options are now loaded (and definition will fail if they aren't compatible). This was specially
|
|
11
|
+
important for custom options that were collections, etc.
|
|
12
|
+
- Fix Float type .load
|
|
4
13
|
|
|
5
14
|
## 7.0 (5/23/2023)
|
|
6
15
|
- Support for loading "<digits>." strings in BigDecimal/Float types (These are formats supported by JS, Java ..)
|
|
@@ -32,7 +41,7 @@
|
|
|
32
41
|
- added support for enum's out of values in json_schema generation
|
|
33
42
|
|
|
34
43
|
## 6.0 (22/11/2021)
|
|
35
|
-
- removed `required_if` support and all of the necessary code.
|
|
44
|
+
- removed `required_if` support and all of the necessary code.
|
|
36
45
|
- changed the semantics of the `required:` option in attributes, to really mean if the "key" is required to be passed in or not (i.e., check if the key is null, not if its value is null)
|
|
37
46
|
- Introduced a new option`null: true|false` to allow for the value of an attribute to be nullable or not when the attribute is passed in.
|
|
38
47
|
* The default behavior for an attribute nullability currently `null: false` (but it can be easily changed by overriding the `Attributor::Attribute.default_for_null` function to return `true`)
|
data/attributor.gemspec
CHANGED
|
@@ -13,14 +13,14 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
|
|
14
14
|
spec.homepage = 'https://github.com/rightscale/attributor'
|
|
15
15
|
spec.license = 'MIT'
|
|
16
|
-
spec.required_ruby_version = '>=2.
|
|
16
|
+
spec.required_ruby_version = '>=2.7'
|
|
17
17
|
|
|
18
18
|
spec.require_paths = ['lib']
|
|
19
19
|
spec.files = `git ls-files -z`.split("\x0")
|
|
20
20
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
21
21
|
|
|
22
22
|
spec.add_runtime_dependency('hashie', ['~> 3'])
|
|
23
|
-
spec.add_runtime_dependency('
|
|
23
|
+
spec.add_runtime_dependency('faker', [' >= 3.2'])
|
|
24
24
|
spec.add_runtime_dependency('activesupport', ['>= 3'])
|
|
25
25
|
|
|
26
26
|
spec.add_development_dependency 'rspec', '~> 3'
|
|
@@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
|
|
|
34
34
|
spec.add_development_dependency('guard', ['~> 2'])
|
|
35
35
|
spec.add_development_dependency('guard-rspec', ['~> 4'])
|
|
36
36
|
spec.add_development_dependency('pry')
|
|
37
|
+
|
|
37
38
|
if RUBY_PLATFORM !~ /java/
|
|
38
39
|
spec.add_development_dependency('pry-byebug')
|
|
39
40
|
spec.add_development_dependency('pry-stack_explorer')
|
data/lib/attributor/attribute.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Attributor
|
|
|
27
27
|
|
|
28
28
|
class << self
|
|
29
29
|
attr_accessor :custom_options
|
|
30
|
-
end
|
|
30
|
+
end
|
|
31
31
|
|
|
32
32
|
def self.custom_option(name, attr_type, options = {}, &block)
|
|
33
33
|
if TOP_LEVEL_OPTIONS.include?(name) || INTERNAL_OPTIONS.include?(name)
|
|
@@ -42,6 +42,17 @@ module Attributor
|
|
|
42
42
|
@type = Attributor.resolve_type(type, options, block)
|
|
43
43
|
@options = @type.respond_to?(:options) ? @type.options.merge(options) : options
|
|
44
44
|
|
|
45
|
+
# We will give the values passed for options, a chance to be 'loaded' by its type, so we store native loaded values in the options
|
|
46
|
+
begin
|
|
47
|
+
current_option_name = nil # Use this to avoid having to wrap each loop with a begin/rescue block
|
|
48
|
+
(self.class.custom_options.keys & @options.keys).each do |custom_key|
|
|
49
|
+
current_option_name = custom_key
|
|
50
|
+
@options[custom_key] = self.class.custom_options[custom_key].load(@options[custom_key])
|
|
51
|
+
end
|
|
52
|
+
rescue => e
|
|
53
|
+
raise AttributorException, "Error while loading value #{@options[current_option_name]} for custom option '#{current_option_name}': #{e.message}"
|
|
54
|
+
end
|
|
55
|
+
|
|
45
56
|
check_options!
|
|
46
57
|
end
|
|
47
58
|
|
|
@@ -145,8 +156,6 @@ module Attributor
|
|
|
145
156
|
def example_from_options(parent, context)
|
|
146
157
|
val = options[:example]
|
|
147
158
|
generated = case val
|
|
148
|
-
when ::Regexp
|
|
149
|
-
val.gen
|
|
150
159
|
when ::Proc
|
|
151
160
|
if val.arity == 2
|
|
152
161
|
val.call(parent, context)
|
|
@@ -200,8 +209,10 @@ module Attributor
|
|
|
200
209
|
description
|
|
201
210
|
end
|
|
202
211
|
|
|
203
|
-
def example(context=nil, parent: nil, values:{})
|
|
204
|
-
|
|
212
|
+
def example(context = nil, parent: nil, values: {})
|
|
213
|
+
require 'faker'
|
|
214
|
+
raise ArgumentError, 'attribute example cannot take a context of type String' if context.is_a? ::String
|
|
215
|
+
|
|
205
216
|
if context
|
|
206
217
|
ctx = Attributor.humanize_context(context)
|
|
207
218
|
seed, = Digest::SHA1.digest(ctx).unpack('QQ')
|
|
@@ -215,10 +226,11 @@ module Attributor
|
|
|
215
226
|
# Only validate the type, if the proc-generated example is "complex" (has attributes)
|
|
216
227
|
errors = loaded.class.respond_to?(:attributes) ? validate_type(loaded, context) : validate(loaded, context)
|
|
217
228
|
raise AttributorException, "Error generating example for #{Attributor.humanize_context(context)}. Errors: #{errors.inspect}" if errors.any?
|
|
229
|
+
|
|
218
230
|
return loaded
|
|
219
231
|
end
|
|
220
232
|
|
|
221
|
-
return options[:values].
|
|
233
|
+
return options[:values].sample if options.key? :values
|
|
222
234
|
|
|
223
235
|
if type.respond_to?(:attributes)
|
|
224
236
|
type.example(context, **values)
|
|
@@ -231,7 +243,7 @@ module Attributor
|
|
|
231
243
|
type.attributes if @type_has_attributes ||= type.respond_to?(:attributes)
|
|
232
244
|
end
|
|
233
245
|
|
|
234
|
-
# Default value for a non-specified null: option
|
|
246
|
+
# Default value for a non-specified null: option
|
|
235
247
|
def self.default_for_null
|
|
236
248
|
false
|
|
237
249
|
end
|
|
@@ -261,7 +273,7 @@ module Attributor
|
|
|
261
273
|
end
|
|
262
274
|
|
|
263
275
|
return errors if errors.any?
|
|
264
|
-
|
|
276
|
+
|
|
265
277
|
object.nil? ? errors : errors + type.validate(object, context, self)
|
|
266
278
|
end
|
|
267
279
|
|
|
@@ -292,7 +304,7 @@ module Attributor
|
|
|
292
304
|
raise AttributorException, 'Required must be a boolean' unless definition == true || definition == false
|
|
293
305
|
raise AttributorException, 'Required cannot be enabled in combination with :default' if definition == true && options.key?(:default)
|
|
294
306
|
when :null
|
|
295
|
-
raise AttributorException, 'Null must be a boolean' unless definition == true || definition == false
|
|
307
|
+
raise AttributorException, 'Null must be a boolean' unless definition == true || definition == false
|
|
296
308
|
when :example
|
|
297
309
|
unless definition.is_a?(::Regexp) || definition.is_a?(::String) || definition.is_a?(::Array) || definition.is_a?(::Proc) || definition.nil? || type.valid_type?(definition)
|
|
298
310
|
raise AttributorException, "Invalid example type (got: #{definition.class.name}). It must always match the type of the attribute (except if passing Regex that is allowed for some types)"
|
|
@@ -306,8 +318,8 @@ module Attributor
|
|
|
306
318
|
:ok # passes
|
|
307
319
|
end
|
|
308
320
|
|
|
309
|
-
def check_custom_option(name, definition)
|
|
310
|
-
attribute = self.class.custom_options.fetch(name)
|
|
321
|
+
def check_custom_option(name, definition)
|
|
322
|
+
attribute = self.class.custom_options.fetch(name)
|
|
311
323
|
|
|
312
324
|
errors = attribute.validate(definition)
|
|
313
325
|
raise AttributorException, "Custom option #{name.inspect} is invalid: #{errors.inspect}" if errors.any?
|
|
@@ -9,7 +9,7 @@ module Attributor
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def self.example(_context = nil, options: {})
|
|
12
|
-
BigDecimal("#{
|
|
12
|
+
BigDecimal("#{Faker::Number.decimal(l_digits: 3, r_digits: 3)}")
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.load(value, _context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
|
|
@@ -9,7 +9,7 @@ module Attributor
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def self.example(context = nil, options: {})
|
|
12
|
-
load(
|
|
12
|
+
load(Faker::Date.in_date_period, context)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
|
|
@@ -21,7 +21,7 @@ module Attributor
|
|
|
21
21
|
case value
|
|
22
22
|
when ::String
|
|
23
23
|
begin
|
|
24
|
-
|
|
24
|
+
::Date.parse(value)
|
|
25
25
|
rescue ArgumentError
|
|
26
26
|
raise Attributor::DeserializationError.new(context: context, from: value.class, encoding: 'Date', value: value)
|
|
27
27
|
end
|
|
@@ -13,7 +13,7 @@ module Attributor
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.example(context = nil, options: {})
|
|
16
|
-
load(
|
|
16
|
+
load(Faker::Date.in_date_period, context)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
|
|
@@ -21,10 +21,11 @@ module Attributor
|
|
|
21
21
|
return value if value.is_a?(native_type)
|
|
22
22
|
return value.to_datetime if value.respond_to?(:to_datetime)
|
|
23
23
|
return nil unless value.is_a?(::String)
|
|
24
|
+
|
|
24
25
|
# TODO: we should be able to convert not only from String but Time...etc
|
|
25
26
|
# Else, we'll decode it from String.
|
|
26
27
|
begin
|
|
27
|
-
|
|
28
|
+
::DateTime.parse(value)
|
|
28
29
|
rescue ArgumentError
|
|
29
30
|
raise Attributor::DeserializationError.new(context: context, from: value.class, encoding: 'DateTime', value: value)
|
|
30
31
|
end
|
|
@@ -18,7 +18,7 @@ module Attributor
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **options)
|
|
21
|
-
return
|
|
21
|
+
return Float(value + '0') if value.is_a?(::String) && value.end_with?('.')
|
|
22
22
|
Float(value)
|
|
23
23
|
rescue TypeError
|
|
24
24
|
super
|
|
@@ -17,16 +17,7 @@ module Attributor
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def self.example(_context = nil, options: {})
|
|
20
|
-
|
|
21
|
-
begin
|
|
22
|
-
# It may fail to generate an example, see bug #72.
|
|
23
|
-
options[:regexp].gen
|
|
24
|
-
rescue => e
|
|
25
|
-
format('Failed to generate example for %s : %s', options[:regexp].inspect, e.message)
|
|
26
|
-
end
|
|
27
|
-
else
|
|
28
|
-
/\w+/.gen
|
|
29
|
-
end
|
|
20
|
+
Faker::Lorem.word
|
|
30
21
|
end
|
|
31
22
|
|
|
32
23
|
def self.family
|
|
@@ -10,7 +10,7 @@ module Attributor
|
|
|
10
10
|
|
|
11
11
|
def self.example(context = Attributor::DEFAULT_ROOT_CONTEXT, options: {})
|
|
12
12
|
file = ::Tempfile.new(Attributor.humanize_context(context))
|
|
13
|
-
file.write
|
|
13
|
+
file.write Faker::Lorem.paragraph
|
|
14
14
|
file.write '.'
|
|
15
15
|
file.rewind
|
|
16
16
|
file
|
|
@@ -9,7 +9,7 @@ module Attributor
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def self.example(context = nil, options: {})
|
|
12
|
-
load(
|
|
12
|
+
load(Faker::Time.between(from: ::DateTime.now - 1, to: ::DateTime.now), context)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
|
|
@@ -24,10 +24,10 @@ module Attributor
|
|
|
24
24
|
def self.parse(value, context)
|
|
25
25
|
case value
|
|
26
26
|
when ::Integer
|
|
27
|
-
|
|
27
|
+
::Time.at(value)
|
|
28
28
|
when ::String
|
|
29
29
|
begin
|
|
30
|
-
|
|
30
|
+
::Time.parse(value)
|
|
31
31
|
rescue ArgumentError
|
|
32
32
|
raise Attributor::DeserializationError.new(context: context, from: value.class, encoding: 'Time', value: value)
|
|
33
33
|
end
|
data/lib/attributor/types/uri.rb
CHANGED
data/lib/attributor/version.rb
CHANGED
data/lib/attributor.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'json'
|
|
2
|
-
require 'randexp'
|
|
3
2
|
|
|
4
3
|
require 'hashie'
|
|
5
4
|
require 'active_support/concern'
|
|
@@ -17,8 +16,6 @@ module Attributor
|
|
|
17
16
|
|
|
18
17
|
require_relative 'attributor/example_mixin'
|
|
19
18
|
|
|
20
|
-
require_relative 'attributor/extensions/randexp'
|
|
21
|
-
|
|
22
19
|
# hierarchical separator string for composing human readable attributes
|
|
23
20
|
SEPARATOR = '.'.freeze
|
|
24
21
|
ROOT_PREFIX = '$'.freeze
|
|
@@ -37,11 +34,13 @@ module Attributor
|
|
|
37
34
|
|
|
38
35
|
def self.find_type(attr_type)
|
|
39
36
|
return attr_type if attr_type < Attributor::Type
|
|
37
|
+
|
|
40
38
|
name = attr_type.name.split('::').last # TOO EXPENSIVE?
|
|
41
39
|
|
|
42
40
|
klass = const_get(name) if const_defined?(name)
|
|
43
41
|
raise AttributorException, "Could not find class with name #{name}" unless klass
|
|
44
42
|
raise AttributorException, "Could not find attribute type for: #{name} [klass: #{klass.name}]" unless klass < Attributor::Type
|
|
43
|
+
|
|
45
44
|
klass
|
|
46
45
|
end
|
|
47
46
|
|
|
@@ -57,7 +56,7 @@ module Attributor
|
|
|
57
56
|
context = Array(context) if context.is_a? ::String
|
|
58
57
|
|
|
59
58
|
begin
|
|
60
|
-
|
|
59
|
+
context.join('.')
|
|
61
60
|
rescue e
|
|
62
61
|
raise "Error creating context string: #{e.message}"
|
|
63
62
|
end
|
|
@@ -81,7 +80,7 @@ module Attributor
|
|
|
81
80
|
else
|
|
82
81
|
val
|
|
83
82
|
end
|
|
84
|
-
end
|
|
83
|
+
end
|
|
85
84
|
|
|
86
85
|
MODULE_PREFIX = 'Attributor::'.freeze
|
|
87
86
|
MODULE_PREFIX_REGEX = ::Regexp.new(MODULE_PREFIX)
|
data/spec/attribute_spec.rb
CHANGED
|
@@ -232,6 +232,16 @@ describe Attributor::Attribute do
|
|
|
232
232
|
end
|
|
233
233
|
end
|
|
234
234
|
|
|
235
|
+
context 'validates the type of values' do
|
|
236
|
+
let(:custom_option_args) { [option_name, String ] }
|
|
237
|
+
|
|
238
|
+
it 'raises with an invalid option value' do
|
|
239
|
+
expect do
|
|
240
|
+
Attributor::Attribute.new(Integer, foo: {a:1})
|
|
241
|
+
end.to raise_error(Attributor::AttributorException,/Error while loading value {:a=>1} for custom option 'foo': Type Attributor::String cannot load values of type Hash/)
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
235
245
|
it 'appear in as_json_schema' do
|
|
236
246
|
attribute = Attributor::Attribute.new(Integer, foo: 'valid')
|
|
237
247
|
json_schema = attribute.as_json_schema
|
|
@@ -263,7 +273,7 @@ describe Attributor::Attribute do
|
|
|
263
273
|
end
|
|
264
274
|
|
|
265
275
|
context 'deterministic examples' do
|
|
266
|
-
let(:example) {
|
|
276
|
+
let(:example) { proc { Faker::Lorem.word } }
|
|
267
277
|
let(:attribute_options) { { example: example } }
|
|
268
278
|
|
|
269
279
|
it 'can take a context to pre-seed the random number generator' do
|
|
@@ -322,30 +332,6 @@ describe Attributor::Attribute do
|
|
|
322
332
|
it { should be example }
|
|
323
333
|
end
|
|
324
334
|
|
|
325
|
-
context 'with a regexp' do
|
|
326
|
-
let(:example) { Regexp.new(/\w+/) }
|
|
327
|
-
let(:generated_example) { /\w+/.gen }
|
|
328
|
-
|
|
329
|
-
it 'calls #gen on the regexp' do
|
|
330
|
-
expect(example).to receive(:gen).and_return(generated_example)
|
|
331
|
-
|
|
332
|
-
expect(example_result).to match example
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
context 'for a type with a non-String native_type' do
|
|
336
|
-
let(:type) { Attributor::Integer }
|
|
337
|
-
let(:example) { Regexp.new(/\d{5}/) }
|
|
338
|
-
let(:generated_example) { /\d{5}/.gen }
|
|
339
|
-
|
|
340
|
-
it 'coerces the example value properly' do
|
|
341
|
-
expect(example).to receive(:gen).and_return(generated_example)
|
|
342
|
-
expect(type).to receive(:load).and_call_original
|
|
343
|
-
|
|
344
|
-
expect(example_result).to be_kind_of(type.native_type)
|
|
345
|
-
end
|
|
346
|
-
end
|
|
347
|
-
end
|
|
348
|
-
|
|
349
335
|
context 'with a proc' do
|
|
350
336
|
let(:parent) { Object.new }
|
|
351
337
|
|
data/spec/support/hashes.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class HashWithModel < Attributor::Hash
|
|
2
2
|
keys do
|
|
3
|
-
key :name, String, default: 'Turkey McDucken', description: 'Turducken name', example:
|
|
3
|
+
key :name, String, default: 'Turkey McDucken', description: 'Turducken name', example: proc { Faker::Name.first_name }
|
|
4
4
|
key :chicken, Chicken
|
|
5
5
|
end
|
|
6
6
|
end
|
data/spec/support/models.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
class Chicken < Attributor::Model
|
|
2
2
|
attributes(identity: :email) do
|
|
3
|
-
attribute :name, Attributor::String, example:
|
|
3
|
+
attribute :name, Attributor::String, example: proc { Faker::Name.first_name }
|
|
4
4
|
attribute :age, Attributor::Integer, default: 1, min: 0, max: 120, description: 'The age of the chicken'
|
|
5
|
-
attribute :email, Attributor::String, example:
|
|
5
|
+
attribute :email, Attributor::String, example: proc { "#{Faker::Name.first_name.downcase}@#{Faker::Lorem.word.downcase}.example.org" }, regexp: /@/, description: 'The email address of the chicken'
|
|
6
6
|
attribute :angry, Attributor::Boolean, example: 'true', description: 'Angry bird?'
|
|
7
|
-
attribute :weight, Attributor::Float, example:
|
|
7
|
+
attribute :weight, Attributor::Float, example: proc { Faker::Number.number(digits: 3) }, description: 'The weight of the chicken'
|
|
8
8
|
attribute :type, Attributor::Symbol, values: [:chicken]
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -14,8 +14,8 @@ class Duck < Attributor::Model
|
|
|
14
14
|
attribute :age, Attributor::Integer
|
|
15
15
|
attribute :name, Attributor::String
|
|
16
16
|
attribute :email, Attributor::String
|
|
17
|
-
attribute :angry, Attributor::Boolean, default: true, example:
|
|
18
|
-
attribute :weight, Attributor::Float, example:
|
|
17
|
+
attribute :angry, Attributor::Boolean, default: true, example: proc { [true, false].sample }, description: 'Angry bird?'
|
|
18
|
+
attribute :weight, Attributor::Float, example: proc { Faker::Number.number(digits: 3).to_f }, description: 'The weight of the duck'
|
|
19
19
|
attribute :type, Attributor::Symbol, values: [:duck]
|
|
20
20
|
end
|
|
21
21
|
end
|
|
@@ -23,16 +23,16 @@ end
|
|
|
23
23
|
class Turkey < Attributor::Model
|
|
24
24
|
attributes do
|
|
25
25
|
attribute :age, Integer, default: 1, min: 0, max: 120, description: 'The age of the turkey'
|
|
26
|
-
attribute :name, String, description: 'name of the turkey', example:
|
|
27
|
-
attribute :email, String, example:
|
|
28
|
-
attribute :weight, Attributor::Float, example:
|
|
26
|
+
attribute :name, String, description: 'name of the turkey', example: proc { Faker::Name.name } # , :default => "Providencia Zboncak"
|
|
27
|
+
attribute :email, String, example: proc { "#{Faker::Name.first_name.downcase}@example.org" }, regexp: /@/, description: 'The email address of the turkey'
|
|
28
|
+
attribute :weight, Attributor::Float, example: proc { Faker::Number.number(digits: 2).to_f }, max: 100.0, description: 'The weight of the turkey'
|
|
29
29
|
attribute :type, Attributor::Symbol, values: [:turkey]
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
class Turducken < Attributor::Model
|
|
34
34
|
attributes do
|
|
35
|
-
attribute :name, String, default: 'Turkey McDucken', description: 'Turducken name', example:
|
|
35
|
+
attribute :name, String, default: 'Turkey McDucken', description: 'Turducken name', example: proc { Faker::Name.first_name }
|
|
36
36
|
attribute :chicken, Chicken
|
|
37
37
|
attribute :duck, Duck
|
|
38
38
|
attribute :turkey, Turkey, description: 'The turkey'
|
|
@@ -43,7 +43,7 @@ end
|
|
|
43
43
|
|
|
44
44
|
class Cormorant < Attributor::Model
|
|
45
45
|
attributes do
|
|
46
|
-
attribute :name, String, description: 'Name of the Cormorant', example:
|
|
46
|
+
attribute :name, String, description: 'Name of the Cormorant', example: proc { Faker::Name.name }
|
|
47
47
|
attribute :timestamps do
|
|
48
48
|
attribute :born_at, DateTime
|
|
49
49
|
attribute :died_at, DateTime, example: proc { |timestamps| timestamps.born_at + 10 }
|
|
@@ -53,22 +53,22 @@ class Cormorant < Attributor::Model
|
|
|
53
53
|
attribute :all_the_fish, Attributor::Collection, description: 'All kinds of fish for feeding the babies'
|
|
54
54
|
|
|
55
55
|
# This will be a collection of Cormorants (note, this relationship is circular)
|
|
56
|
-
attribute :neighbors, Attributor::Collection.of(Cormorant), member_options: {null: false}, description: 'Neighbor cormorants', null: false
|
|
56
|
+
attribute :neighbors, Attributor::Collection.of(Cormorant), member_options: { null: false }, description: 'Neighbor cormorants', null: false
|
|
57
57
|
|
|
58
58
|
# This will be a collection of instances of an anonymous Struct class, each having two well-defined attributes
|
|
59
59
|
|
|
60
60
|
attribute :babies, Attributor::Collection.of(Attributor::Struct), description: 'All the babies', member_options: { identity: :name } do
|
|
61
|
-
attribute :name, Attributor::String, example:
|
|
61
|
+
attribute :name, Attributor::String, example: proc { Faker::Name.name }, description: 'The name of the baby cormorant', required: true
|
|
62
62
|
attribute :months, Attributor::Integer, default: 0, min: 0, description: 'The age in months of the baby cormorant'
|
|
63
|
-
attribute :weight, Attributor::Float, example:
|
|
63
|
+
attribute :weight, Attributor::Float, example: proc { Faker::Number.number(digits: 2) }, description: 'The weight in kg of the baby cormorant'
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
class Person < Attributor::Model
|
|
69
69
|
attributes do
|
|
70
|
-
attribute :name, String, example:
|
|
71
|
-
attribute :title, String, values: %w
|
|
70
|
+
attribute :name, String, example: proc { Faker::Name.first_name }
|
|
71
|
+
attribute :title, String, values: %w[Mr Mrs Ms Dr]
|
|
72
72
|
attribute :okay, Attributor::Boolean, values: [true]
|
|
73
73
|
attribute :address, Address, example: proc { |person, context| Address.example(context, person: person) }
|
|
74
74
|
end
|
|
@@ -77,7 +77,7 @@ end
|
|
|
77
77
|
class Address < Attributor::Model
|
|
78
78
|
attributes do
|
|
79
79
|
attribute :name, String, example: /\w+/, null: true
|
|
80
|
-
attribute :state, String, values: %w
|
|
80
|
+
attribute :state, String, values: %w[OR CA], null: false
|
|
81
81
|
attribute :person, Person, example: proc { |address, context| Person.example(context, address: address) }
|
|
82
82
|
attribute :substruct, reference: Address, null: false do
|
|
83
83
|
attribute :state, Struct do # redefine state as a Struct
|
data/spec/types/csv_spec.rb
CHANGED
|
@@ -29,7 +29,7 @@ describe Attributor::CSV do
|
|
|
29
29
|
|
|
30
30
|
context '.dump' do
|
|
31
31
|
let!(:int_vals) { [1, 2, 3] }
|
|
32
|
-
let!(:str_vals) { (0..2).collect {
|
|
32
|
+
let!(:str_vals) { (0..2).collect { Faker::Lorem.word } }
|
|
33
33
|
|
|
34
34
|
it 'dumps a String value' do
|
|
35
35
|
expect(csv.dump(int_vals)).to be_a(String)
|
data/spec/types/date_spec.rb
CHANGED
|
@@ -19,7 +19,7 @@ describe Attributor::Date do
|
|
|
19
19
|
let(:example) { type.example }
|
|
20
20
|
subject(:value) { type.dump(example) }
|
|
21
21
|
it 'is formatted correctly' do
|
|
22
|
-
expect(value).to match(/\d{4}-\d{2}-\d{2}
|
|
22
|
+
expect(value).to match(/\d{4}-\d{2}-\d{2}/)
|
|
23
23
|
end
|
|
24
24
|
context 'nil values' do
|
|
25
25
|
it 'should be nil' do
|
|
@@ -59,7 +59,7 @@ describe Attributor::Date do
|
|
|
59
59
|
'Sat, 3 Feb 2001 04:05:06 +0700',
|
|
60
60
|
'2013/08/23 00:39:55 +0000',
|
|
61
61
|
'2007-10-19T04:11:33Z',
|
|
62
|
-
'2001-02-03T04:05:06+07:00.123456'
|
|
62
|
+
'2001-02-03T04:05:06+07:00.123456' # custom format with microseconds
|
|
63
63
|
].each do |value|
|
|
64
64
|
it "returns correct Date for #{value.inspect}" do
|
|
65
65
|
expect(type.load(value)).to eq Date.parse(value)
|
|
@@ -70,7 +70,7 @@ describe Attributor::Date do
|
|
|
70
70
|
'Sat, 30 Feb 2001 04:05:06 GMT', # No such date exists
|
|
71
71
|
'2013/08/33 00:39:55 +0000',
|
|
72
72
|
'2007-10-33T04:11:33Z',
|
|
73
|
-
'2001-02-33T04:05:06+07:00.123456'
|
|
73
|
+
'2001-02-33T04:05:06+07:00.123456' # custom format with microseconds
|
|
74
74
|
].each do |value|
|
|
75
75
|
it "raises Attributor::AttributorException for #{value.inspect}" do
|
|
76
76
|
expect do
|
|
@@ -82,7 +82,7 @@ describe Attributor::Date do
|
|
|
82
82
|
[
|
|
83
83
|
'',
|
|
84
84
|
'foobar',
|
|
85
|
-
'Sat, 30 Feb 2001 04:05:06 FOOBAR'
|
|
85
|
+
'Sat, 30 Feb 2001 04:05:06 FOOBAR' # No such date format exists
|
|
86
86
|
].each do |value|
|
|
87
87
|
it "raises Attributor::AttributorException for #{value.inspect}" do
|
|
88
88
|
expect do
|
|
@@ -93,11 +93,11 @@ describe Attributor::Date do
|
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
context '.as_json_schema' do
|
|
96
|
-
subject(:js){ type.as_json_schema }
|
|
96
|
+
subject(:js) { type.as_json_schema }
|
|
97
97
|
it 'adds the right attributes' do
|
|
98
98
|
expect(js.keys).to include(:type, :'x-type_name')
|
|
99
99
|
expect(js[:type]).to eq(:string)
|
|
100
|
-
expect(js[:format]).to eq(:
|
|
100
|
+
expect(js[:format]).to eq(:date)
|
|
101
101
|
expect(js[:'x-type_name']).to eq('Date')
|
|
102
102
|
end
|
|
103
103
|
end
|
data/spec/types/float_spec.rb
CHANGED
|
@@ -61,8 +61,10 @@ describe Attributor::Float do
|
|
|
61
61
|
expect(type.load('0.')).to eq Float('0.0')
|
|
62
62
|
end
|
|
63
63
|
it 'decodes it if the String represents a negative Float and ends in .' do
|
|
64
|
-
expect(type.load('-10.')).to eq
|
|
65
|
-
expect(type.load('-
|
|
64
|
+
expect(type.load('-10.')).to eq -10.0
|
|
65
|
+
expect(type.load('-10.').class).to eq Float
|
|
66
|
+
expect(type.load('-0.')).to eq -0.0
|
|
67
|
+
expect(type.load('-0.').class).to eq Float
|
|
66
68
|
end
|
|
67
69
|
end
|
|
68
70
|
|
data/spec/types/model_spec.rb
CHANGED
|
@@ -80,7 +80,7 @@ describe Attributor::Model do
|
|
|
80
80
|
subject(:chicken) { Chicken.example }
|
|
81
81
|
|
|
82
82
|
let(:age_opts) { { options: Chicken.attributes[:age].options } }
|
|
83
|
-
let(:age) {
|
|
83
|
+
let(:age) { Faker::Number.number(digits: 2) }
|
|
84
84
|
|
|
85
85
|
context 'for a simple model' do
|
|
86
86
|
it { should be_kind_of(Chicken) }
|
data/spec/types/string_spec.rb
CHANGED
|
@@ -22,14 +22,6 @@ describe Attributor::String do
|
|
|
22
22
|
expect(type.example).to be_a(::String)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
it 'handles regexps that Randexp can not (#72)' do
|
|
26
|
-
regex = /\w+(,\w+)*/
|
|
27
|
-
expect do
|
|
28
|
-
val = Attributor::String.example(options: { regexp: regex })
|
|
29
|
-
expect(val).to be_a(::String)
|
|
30
|
-
expect(val).to match(/Failed to generate.+is too vague/)
|
|
31
|
-
end.to_not raise_error
|
|
32
|
-
end
|
|
33
25
|
end
|
|
34
26
|
|
|
35
27
|
context '.load' do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: attributor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '
|
|
4
|
+
version: '8.0'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Josep M. Blanquer
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2023-
|
|
12
|
+
date: 2023-08-30 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: hashie
|
|
@@ -26,19 +26,19 @@ dependencies:
|
|
|
26
26
|
- !ruby/object:Gem::Version
|
|
27
27
|
version: '3'
|
|
28
28
|
- !ruby/object:Gem::Dependency
|
|
29
|
-
name:
|
|
29
|
+
name: faker
|
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
|
31
31
|
requirements:
|
|
32
|
-
- - "
|
|
32
|
+
- - ">="
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: '
|
|
34
|
+
version: '3.2'
|
|
35
35
|
type: :runtime
|
|
36
36
|
prerelease: false
|
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
|
38
38
|
requirements:
|
|
39
|
-
- - "
|
|
39
|
+
- - ">="
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: '
|
|
41
|
+
version: '3.2'
|
|
42
42
|
- !ruby/object:Gem::Dependency
|
|
43
43
|
name: activesupport
|
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -316,7 +316,6 @@ files:
|
|
|
316
316
|
- lib/attributor/dumpable.rb
|
|
317
317
|
- lib/attributor/example_mixin.rb
|
|
318
318
|
- lib/attributor/exceptions.rb
|
|
319
|
-
- lib/attributor/extensions/randexp.rb
|
|
320
319
|
- lib/attributor/extras/field_selector.rb
|
|
321
320
|
- lib/attributor/extras/field_selector/parser.rb
|
|
322
321
|
- lib/attributor/extras/field_selector/transformer.rb
|
|
@@ -400,14 +399,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
400
399
|
requirements:
|
|
401
400
|
- - ">="
|
|
402
401
|
- !ruby/object:Gem::Version
|
|
403
|
-
version: '2.
|
|
402
|
+
version: '2.7'
|
|
404
403
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
405
404
|
requirements:
|
|
406
405
|
- - ">="
|
|
407
406
|
- !ruby/object:Gem::Version
|
|
408
407
|
version: '0'
|
|
409
408
|
requirements: []
|
|
410
|
-
rubygems_version: 3.
|
|
409
|
+
rubygems_version: 3.4.10
|
|
411
410
|
signing_key:
|
|
412
411
|
specification_version: 4
|
|
413
412
|
summary: A powerful attribute and type management library for Ruby
|