attributor 2.6.0 → 2.6.1
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/CHANGELOG.md +13 -0
- data/lib/attributor.rb +5 -4
- data/lib/attributor/attribute.rb +3 -1
- data/lib/attributor/example_mixin.rb +38 -5
- data/lib/attributor/families/numeric.rb +17 -0
- data/lib/attributor/families/temporal.rb +22 -0
- data/lib/attributor/type.rb +5 -0
- data/lib/attributor/types/bigdecimal.rb +1 -2
- data/lib/attributor/types/boolean.rb +5 -0
- data/lib/attributor/types/collection.rb +4 -0
- data/lib/attributor/types/csv.rb +4 -0
- data/lib/attributor/types/date.rb +1 -6
- data/lib/attributor/types/date_time.rb +3 -5
- data/lib/attributor/types/float.rb +5 -1
- data/lib/attributor/types/hash.rb +11 -8
- data/lib/attributor/types/integer.rb +1 -2
- data/lib/attributor/types/string.rb +5 -0
- data/lib/attributor/types/symbol.rb +5 -0
- data/lib/attributor/types/tempfile.rb +3 -0
- data/lib/attributor/types/time.rb +2 -6
- data/lib/attributor/version.rb +1 -1
- data/spec/attribute_spec.rb +27 -3
- data/spec/attributor_spec.rb +17 -0
- data/spec/families_spec.rb +14 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/type_spec.rb +18 -4
- data/spec/types/hash_spec.rb +88 -16
- metadata +6 -4
- data/spec/support/types.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab74d0c797f7d246ba2e19564b9a215fcb1e8e73
|
4
|
+
data.tar.gz: 780a566c96c190af27f559aaf9052ccddcc5d237
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df140c8719de7cbc33cf48c2b92d3b5afb3fc15119a84bacc1e414757e2a54288e72fc46ddce64485ac87ed927cca93ce7a387398064d5dfac235af4eccfae94
|
7
|
+
data.tar.gz: 5128f01b9a28a1b7e2e112d23bd82037600af17b127f6f4c9ad8911d00d04ffe829a10c5bc0288dbcd9517d379052b0680db0b4892cbf7ec4c0976fbf48335f3
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
Attributor Changelog
|
2
2
|
============================
|
3
3
|
|
4
|
+
next
|
5
|
+
----
|
6
|
+
|
7
|
+
2.6.1
|
8
|
+
-----
|
9
|
+
|
10
|
+
* 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
|
+
* Added `Type.family` which returns a more-generic "family name". It's defined for all built-in types, and is included in `Type.describe`.
|
12
|
+
* Cleanup and bug fixes around example generation for `Model`, `Struct` and `Hash`.
|
13
|
+
* Avoid creating method accessors for true `Hash` types (only `[]` accessors)
|
14
|
+
* Fix common hash methods created for example instances (to play well with lazy attributes)
|
15
|
+
* Avoid storing the `Hash#insensitive_map` unless insensitivity enabled
|
16
|
+
|
4
17
|
2.6.0
|
5
18
|
-----
|
6
19
|
|
data/lib/attributor.rb
CHANGED
@@ -18,9 +18,6 @@ module Attributor
|
|
18
18
|
require_relative 'attributor/extensions/randexp'
|
19
19
|
|
20
20
|
|
21
|
-
|
22
|
-
# List of all basic types (i.e. not collections, structs or models)
|
23
|
-
|
24
21
|
# hierarchical separator string for composing human readable attributes
|
25
22
|
SEPARATOR = '.'.freeze
|
26
23
|
DEFAULT_ROOT_CONTEXT = ['$'].freeze
|
@@ -74,16 +71,20 @@ module Attributor
|
|
74
71
|
MODULE_PREFIX = "Attributor\:\:".freeze
|
75
72
|
MODULE_PREFIX_REGEX = Regexp.new(MODULE_PREFIX)
|
76
73
|
|
74
|
+
require_relative 'attributor/families/numeric'
|
75
|
+
require_relative 'attributor/families/temporal'
|
76
|
+
|
77
77
|
require_relative 'attributor/types/container'
|
78
78
|
require_relative 'attributor/types/object'
|
79
|
+
|
79
80
|
require_relative 'attributor/types/bigdecimal'
|
80
81
|
require_relative 'attributor/types/integer'
|
81
82
|
require_relative 'attributor/types/string'
|
82
83
|
require_relative 'attributor/types/symbol'
|
83
84
|
require_relative 'attributor/types/boolean'
|
85
|
+
require_relative 'attributor/types/time'
|
84
86
|
require_relative 'attributor/types/date'
|
85
87
|
require_relative 'attributor/types/date_time'
|
86
|
-
require_relative 'attributor/types/time'
|
87
88
|
require_relative 'attributor/types/float'
|
88
89
|
require_relative 'attributor/types/collection'
|
89
90
|
require_relative 'attributor/types/hash'
|
data/lib/attributor/attribute.rb
CHANGED
@@ -96,7 +96,7 @@ module Attributor
|
|
96
96
|
end
|
97
97
|
|
98
98
|
|
99
|
-
TOP_LEVEL_OPTIONS = [ :description, :values, :default, :example, :required, :required_if ]
|
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
101
|
def describe(shallow=true)
|
102
102
|
description = { }
|
@@ -284,6 +284,8 @@ module Attributor
|
|
284
284
|
unless definition.is_a?(::Regexp) || definition.is_a?(::String) || definition.is_a?(::Array) || definition.is_a?(::Proc) || definition.nil? || self.type.valid_type?(definition)
|
285
285
|
raise AttributorException.new("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)")
|
286
286
|
end
|
287
|
+
when :custom_data
|
288
|
+
raise AttributorException.new("custom_data must be a Hash. Got (#{definition})") unless definition.is_a?(::Hash)
|
287
289
|
else
|
288
290
|
return :unknown # unknown option
|
289
291
|
end
|
@@ -6,9 +6,11 @@ module Attributor
|
|
6
6
|
module ExampleMixin
|
7
7
|
|
8
8
|
def self.extended(obj)
|
9
|
-
obj.
|
10
|
-
obj.
|
11
|
-
|
9
|
+
if obj.kind_of? Attributor::Model
|
10
|
+
obj.class.attributes.each do |name, _|
|
11
|
+
obj.define_singleton_method(name) do
|
12
|
+
get(name)
|
13
|
+
end
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -21,6 +23,37 @@ module Attributor
|
|
21
23
|
@lazy_attributes = val
|
22
24
|
end
|
23
25
|
|
26
|
+
def [](k)
|
27
|
+
unless @contents.key?(k)
|
28
|
+
proc = lazy_attributes.delete k
|
29
|
+
@contents[k] = proc.call
|
30
|
+
end
|
31
|
+
@contents[k]
|
32
|
+
end
|
33
|
+
|
34
|
+
def []=(k,v)
|
35
|
+
lazy_attributes.delete k
|
36
|
+
@contents[k] = v
|
37
|
+
end
|
38
|
+
|
39
|
+
def each(&block)
|
40
|
+
contents.each(&block)
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_method :each_pair, :each
|
44
|
+
|
45
|
+
def values
|
46
|
+
contents.values
|
47
|
+
end
|
48
|
+
|
49
|
+
def empty?
|
50
|
+
contents.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
def size
|
54
|
+
keys.size
|
55
|
+
end
|
56
|
+
|
24
57
|
def keys
|
25
58
|
@contents.keys | lazy_attributes.keys
|
26
59
|
end
|
@@ -53,9 +86,9 @@ module Attributor
|
|
53
86
|
def contents
|
54
87
|
lazy_attributes.keys.each do |key|
|
55
88
|
proc = lazy_attributes.delete(key)
|
56
|
-
@contents[key] = proc.call(self)
|
89
|
+
@contents[key] = proc.call(self) unless @contents.key?(key)
|
57
90
|
end
|
58
|
-
|
91
|
+
|
59
92
|
super
|
60
93
|
end
|
61
94
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Abstract type for the 'temporal' family
|
2
|
+
|
3
|
+
module Attributor
|
4
|
+
|
5
|
+
class Temporal
|
6
|
+
include Type
|
7
|
+
|
8
|
+
def self.native_type
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.family
|
13
|
+
'temporal'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.dump(value,**opts)
|
17
|
+
value && value.iso8601
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/attributor/type.rb
CHANGED
@@ -109,6 +109,7 @@ module Attributor
|
|
109
109
|
type_name = self.ancestors.find { |k| k.name && !k.name.empty? }.name
|
110
110
|
{
|
111
111
|
name: type_name.gsub(Attributor::MODULE_PREFIX_REGEX, ''),
|
112
|
+
family: self.family,
|
112
113
|
id: self.id
|
113
114
|
}
|
114
115
|
end
|
@@ -118,6 +119,10 @@ module Attributor
|
|
118
119
|
self.name.gsub('::'.freeze,'-'.freeze)
|
119
120
|
end
|
120
121
|
|
122
|
+
def family
|
123
|
+
'any'
|
124
|
+
end
|
125
|
+
|
121
126
|
end
|
122
127
|
end
|
123
128
|
end
|
data/lib/attributor/types/csv.rb
CHANGED
@@ -2,8 +2,7 @@ require 'date'
|
|
2
2
|
|
3
3
|
module Attributor
|
4
4
|
|
5
|
-
class Date
|
6
|
-
include Type
|
5
|
+
class Date < Temporal
|
7
6
|
|
8
7
|
def self.native_type
|
9
8
|
return ::Date
|
@@ -31,10 +30,6 @@ module Attributor
|
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
34
|
-
def self.dump(value,**opts)
|
35
|
-
value && value.iso8601
|
36
|
-
end
|
37
|
-
|
38
33
|
end
|
39
34
|
|
40
35
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# Represents a plain old boolean type. TBD: can be nil?
|
2
2
|
#
|
3
3
|
require_relative '../exceptions'
|
4
|
+
|
4
5
|
require 'date'
|
5
6
|
|
6
7
|
module Attributor
|
7
8
|
|
8
|
-
class DateTime
|
9
|
-
include Type
|
9
|
+
class DateTime < Temporal
|
10
10
|
|
11
11
|
def self.native_type
|
12
12
|
return ::DateTime
|
@@ -30,9 +30,7 @@ module Attributor
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
value && value.iso8601
|
35
|
-
end
|
33
|
+
|
36
34
|
|
37
35
|
|
38
36
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Float objects represent inexact real numbers using the native architecture's double-precision floating point representation.
|
2
2
|
# See: http://ruby-doc.org/core-2.1.0/Float.html
|
3
|
-
|
3
|
+
|
4
4
|
module Attributor
|
5
5
|
|
6
6
|
class Float
|
@@ -23,5 +23,9 @@ module Attributor
|
|
23
23
|
super
|
24
24
|
end
|
25
25
|
|
26
|
+
def self.family
|
27
|
+
'numeric'
|
28
|
+
end
|
29
|
+
|
26
30
|
end
|
27
31
|
end
|
@@ -34,7 +34,10 @@ module Attributor
|
|
34
34
|
@concrete=true
|
35
35
|
end
|
36
36
|
|
37
|
-
|
37
|
+
def self.family
|
38
|
+
'hash'
|
39
|
+
end
|
40
|
+
|
38
41
|
@saved_blocks = []
|
39
42
|
@options = {allow_extra: false}
|
40
43
|
@keys = {}
|
@@ -78,10 +81,12 @@ module Attributor
|
|
78
81
|
compiler = dsl_class.new(self, opts)
|
79
82
|
compiler.parse(*blocks)
|
80
83
|
|
81
|
-
|
82
|
-
|
84
|
+
if opts[:case_insensitive_load] == true
|
85
|
+
@insensitive_map = self.keys.keys.each_with_object({}) do |k, map|
|
86
|
+
map[k.downcase] = k
|
87
|
+
end
|
83
88
|
end
|
84
|
-
|
89
|
+
|
85
90
|
compiler
|
86
91
|
end
|
87
92
|
|
@@ -397,9 +402,7 @@ module Attributor
|
|
397
402
|
@contents.each(&block)
|
398
403
|
end
|
399
404
|
|
400
|
-
|
401
|
-
@contents.each_pair(&block)
|
402
|
-
end
|
405
|
+
alias_method :each_pair, :each
|
403
406
|
|
404
407
|
def size
|
405
408
|
@contents.size
|
@@ -425,7 +428,7 @@ module Attributor
|
|
425
428
|
def merge(h)
|
426
429
|
case h
|
427
430
|
when self.class
|
428
|
-
self.class.new(
|
431
|
+
self.class.new(contents.merge(h.contents))
|
429
432
|
when Attributor::Hash
|
430
433
|
raise ArgumentError, "cannot merge Attributor::Hash instances of different types" unless h.is_a?(self.class)
|
431
434
|
else
|
@@ -2,7 +2,7 @@ require 'date'
|
|
2
2
|
|
3
3
|
module Attributor
|
4
4
|
|
5
|
-
class Time
|
5
|
+
class Time < Temporal
|
6
6
|
include Type
|
7
7
|
|
8
8
|
def self.native_type
|
@@ -33,11 +33,7 @@ module Attributor
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
value && value.iso8601
|
38
|
-
end
|
39
|
-
|
40
|
-
|
36
|
+
|
41
37
|
end
|
42
38
|
|
43
39
|
end
|
data/lib/attributor/version.rb
CHANGED
data/spec/attribute_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
|
4
4
|
describe Attributor::Attribute do
|
5
5
|
|
6
6
|
let(:attribute_options) { Hash.new }
|
7
|
-
let(:type) {
|
7
|
+
let(:type) { Attributor::String }
|
8
8
|
|
9
9
|
subject(:attribute) { Attributor::Attribute.new(type, attribute_options) }
|
10
10
|
|
@@ -43,7 +43,7 @@ describe Attributor::Attribute do
|
|
43
43
|
context 'describe' do
|
44
44
|
let(:attribute_options) { {:required => true, :values => ["one"], :description => "something", :min => 0} }
|
45
45
|
let(:expected) do
|
46
|
-
h = {type: {name: type.
|
46
|
+
h = {type: {name: 'String', id: type.id, family: type.family}}
|
47
47
|
common = attribute_options.select{|k,v| Attributor::Attribute::TOP_LEVEL_OPTIONS.include? k }
|
48
48
|
h.merge!( common )
|
49
49
|
h[:options] = {:min => 0 }
|
@@ -62,6 +62,16 @@ describe Attributor::Attribute do
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
context 'with custom_data' do
|
66
|
+
let(:custom_data) { {loggable: true, visible_in_ui: false} }
|
67
|
+
let(:attribute_options) { {custom_data: custom_data} }
|
68
|
+
its(:describe) { should have_key(:custom_data) }
|
69
|
+
|
70
|
+
it 'keep the custom data attribute' do
|
71
|
+
subject.describe[:custom_data].should == custom_data
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
65
75
|
context 'for an anonymous type (aka: Struct)' do
|
66
76
|
let(:attribute_options) { Hash.new }
|
67
77
|
let(:attribute) do
|
@@ -110,6 +120,19 @@ describe Attributor::Attribute do
|
|
110
120
|
}.to raise_error(/Default value doesn't have the correct attribute type/)
|
111
121
|
end
|
112
122
|
|
123
|
+
context 'custom_data' do
|
124
|
+
it 'raises when not a hash' do
|
125
|
+
expect {
|
126
|
+
Attributor::Attribute.new(Integer, custom_data: 1)
|
127
|
+
}.to raise_error(/custom_data must be a Hash/)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'does not raise for hashes' do
|
131
|
+
expect {
|
132
|
+
Attributor::Attribute.new(Integer, custom_data: {loggable: true})
|
133
|
+
}.not_to raise_error
|
134
|
+
end
|
135
|
+
end
|
113
136
|
end
|
114
137
|
|
115
138
|
|
@@ -145,7 +168,7 @@ describe Attributor::Attribute do
|
|
145
168
|
end
|
146
169
|
|
147
170
|
context 'for a type with a non-String native_type' do
|
148
|
-
let(:type) {
|
171
|
+
let(:type) { Attributor::Integer }
|
149
172
|
context 'using a regexp' do
|
150
173
|
let(:example) { /\d{5}/ }
|
151
174
|
it 'coerces the example value properly' do
|
@@ -228,6 +251,7 @@ describe Attributor::Attribute do
|
|
228
251
|
example_1.should_not eq example_2
|
229
252
|
end
|
230
253
|
|
254
|
+
|
231
255
|
end
|
232
256
|
end
|
233
257
|
|
data/spec/attributor_spec.rb
CHANGED
@@ -22,4 +22,21 @@ describe Attributor do
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
context '.humanize_context' do
|
27
|
+
let(:context) { [] }
|
28
|
+
|
29
|
+
subject(:humanized) { Attributor.humanize_context(context) }
|
30
|
+
|
31
|
+
context 'with string value' do
|
32
|
+
let(:context) { 'some-context'}
|
33
|
+
it { should eq('some-context')}
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with array value' do
|
37
|
+
let(:context) { ['a', 'b'] }
|
38
|
+
it { should eq('a.b') }
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
25
42
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe 'Type families' do
|
4
|
+
|
5
|
+
let(:types) { ObjectSpace.each_object(Class).select { |k| k < Attributor::Type } }
|
6
|
+
|
7
|
+
it 'are set on all types' do
|
8
|
+
types.each do |type|
|
9
|
+
next if type == Attributor::Object # object has no set family
|
10
|
+
type.should_not be_in_family('attributor')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/type_spec.rb
CHANGED
@@ -3,7 +3,22 @@ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
|
3
3
|
|
4
4
|
describe Attributor::Type do
|
5
5
|
|
6
|
-
subject(:test_type)
|
6
|
+
subject(:test_type) do
|
7
|
+
Class.new do
|
8
|
+
include Attributor::Type
|
9
|
+
def self.native_type
|
10
|
+
::String
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.name
|
14
|
+
'Testing'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.family
|
18
|
+
'testing'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
7
22
|
|
8
23
|
let(:attribute_options) { Hash.new }
|
9
24
|
let(:attribute_attributes) { Hash.new }
|
@@ -16,8 +31,7 @@ describe Attributor::Type do
|
|
16
31
|
|
17
32
|
|
18
33
|
its(:native_type) { should be(::String) }
|
19
|
-
its(:id) { should eq('
|
20
|
-
|
34
|
+
its(:id) { should eq('Testing')}
|
21
35
|
|
22
36
|
context 'load' do
|
23
37
|
let(:value) { nil }
|
@@ -41,7 +55,7 @@ describe Attributor::Type do
|
|
41
55
|
let(:context) { ['top','sub'] }
|
42
56
|
|
43
57
|
it 'raises an exception' do
|
44
|
-
expect { test_type.load(value,context) }.to raise_error( Attributor::IncompatibleTypeError, /
|
58
|
+
expect { test_type.load(value,context) }.to raise_error( Attributor::IncompatibleTypeError, /cannot load values of type Fixnum.*while loading top.sub/)
|
45
59
|
end
|
46
60
|
|
47
61
|
|
data/spec/types/hash_spec.rb
CHANGED
@@ -35,6 +35,64 @@ describe Attributor::Hash do
|
|
35
35
|
example.values.all? {|v| v.kind_of? Integer}.should be(true)
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
context 'for a Hash with defined keys' do
|
40
|
+
let(:name) { 'bob' }
|
41
|
+
let(:something) { 'else' }
|
42
|
+
|
43
|
+
subject(:example) { HashWithStrings.example(name: name, something: something) }
|
44
|
+
|
45
|
+
context 'resolves a lazy attributes on demand' do
|
46
|
+
before { example.lazy_attributes.keys.should eq [:name, :something] }
|
47
|
+
after { example.lazy_attributes.keys.should eq [:something] }
|
48
|
+
|
49
|
+
it 'using get' do
|
50
|
+
example.get(:name).should be name
|
51
|
+
end
|
52
|
+
it 'using []' do
|
53
|
+
example[:name].should be name
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'using set' do
|
57
|
+
example.set :name, 'not bob'
|
58
|
+
example.get(:name).should == 'not bob'
|
59
|
+
end
|
60
|
+
it 'using []=' do
|
61
|
+
example[:name] = 'not bob'
|
62
|
+
example[:name].should == 'not bob'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
its(:size) { should eq 2 }
|
67
|
+
its(:values) { should =~ [name, something] }
|
68
|
+
its(:keys) { should =~ [:name, :something] }
|
69
|
+
it do
|
70
|
+
should_not be_empty
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'responds to key? correctly' do
|
74
|
+
example.key?(:name).should be(true)
|
75
|
+
example.key?(:something).should be(true)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'enumerates the contents' do
|
79
|
+
example.collect {|k,v| k }.should eq [:name, :something ]
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'enumerates the contents using each_pair' do
|
83
|
+
pairs = []
|
84
|
+
example.each_pair {|pair| pairs << pair }
|
85
|
+
pairs.should =~ [ [:name, name], [:something, something] ]
|
86
|
+
end
|
87
|
+
|
88
|
+
its(:contents){ should eq ({name: name, something: something}) }
|
89
|
+
it 'does not create methods for the keys' do
|
90
|
+
example.should_not respond_to(:name)
|
91
|
+
example.should_not respond_to(:something)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
38
96
|
context 'using a non array context' do
|
39
97
|
it 'should work for hashes with key/value types' do
|
40
98
|
expect{ Attributor::Hash.of(key:String,value:String).example("Not an Array") }.to_not raise_error
|
@@ -431,8 +489,8 @@ describe Attributor::Hash do
|
|
431
489
|
context 'for hashes with key and value types' do
|
432
490
|
it 'describes the type correctly' do
|
433
491
|
description[:name].should eq('Hash')
|
434
|
-
description[:key].should eq(type:{name: 'Object', id: 'Attributor-Object'})
|
435
|
-
description[:value].should eq(type:{name: 'Object', id: 'Attributor-Object'})
|
492
|
+
description[:key].should eq(type:{name: 'Object', id: 'Attributor-Object', family: 'any'})
|
493
|
+
description[:value].should eq(type:{name: 'Object', id: 'Attributor-Object', family: 'any'})
|
436
494
|
end
|
437
495
|
end
|
438
496
|
|
@@ -450,15 +508,15 @@ describe Attributor::Hash do
|
|
450
508
|
|
451
509
|
it 'describes the type correctly' do
|
452
510
|
description[:name].should eq('Hash')
|
453
|
-
description[:key].should eq(type:{name: 'String', id: 'Attributor-String'})
|
511
|
+
description[:key].should eq(type:{name: 'String', id: 'Attributor-String', family: 'string'})
|
454
512
|
description.should_not have_key(:value)
|
455
513
|
|
456
514
|
keys = description[:keys]
|
457
515
|
|
458
|
-
keys['a string'].should eq(type: {name: 'String', id: 'Attributor-String'} )
|
459
|
-
keys['1'].should eq(type: {name: 'Integer', id: 'Attributor-Integer'}, options: {min: 1, max: 20} )
|
460
|
-
keys['some_date'].should eq(type: {name: 'DateTime', id: 'Attributor-DateTime'})
|
461
|
-
keys['defaulted'].should eq(type: {name: 'String', id: 'Attributor-String'}, default: 'default value')
|
516
|
+
keys['a string'].should eq(type: {name: 'String', id: 'Attributor-String', family: 'string'} )
|
517
|
+
keys['1'].should eq(type: {name: 'Integer', id: 'Attributor-Integer', family: 'numeric'}, options: {min: 1, max: 20} )
|
518
|
+
keys['some_date'].should eq(type: {name: 'DateTime', id: 'Attributor-DateTime', family: 'temporal'})
|
519
|
+
keys['defaulted'].should eq(type: {name: 'String', id: 'Attributor-String', family: 'string'}, default: 'default value')
|
462
520
|
end
|
463
521
|
end
|
464
522
|
end
|
@@ -555,7 +613,9 @@ describe Attributor::Hash do
|
|
555
613
|
end
|
556
614
|
end
|
557
615
|
|
558
|
-
context '
|
616
|
+
context 'case_insensitive_load option' do
|
617
|
+
let(:case_insensitive) { true }
|
618
|
+
let(:type) { Attributor::Hash.of(key: String).construct(block, case_insensitive_load: case_insensitive) }
|
559
619
|
let(:block) do
|
560
620
|
proc do
|
561
621
|
key 'downcase', Integer
|
@@ -563,19 +623,31 @@ describe Attributor::Hash do
|
|
563
623
|
key 'CamelCase', Integer
|
564
624
|
end
|
565
625
|
end
|
566
|
-
|
567
|
-
let(:type) { Attributor::Hash.of(key: String).construct(block, case_insensitive_load: true) }
|
568
|
-
|
569
626
|
let(:input) { {'DOWNCASE' => 1, 'upcase' => 2, 'CamelCase' => 3} }
|
570
|
-
|
571
627
|
subject(:output) { type.load(input) }
|
572
628
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
629
|
+
context 'when defined' do
|
630
|
+
|
631
|
+
it 'maps the incoming keys to defined keys, regardless of case' do
|
632
|
+
output['downcase'].should eq(1)
|
633
|
+
output['UPCASE'].should eq(2)
|
634
|
+
output['CamelCase'].should eq(3)
|
635
|
+
end
|
636
|
+
it 'has loaded the (internal) insensitive_map upon building the definition' do
|
637
|
+
type.definition
|
638
|
+
type.insensitive_map.should be_kind_of(::Hash)
|
639
|
+
type.insensitive_map.keys.should =~ ["downcase","upcase","camelcase"]
|
640
|
+
end
|
577
641
|
end
|
642
|
+
|
643
|
+
context 'when not defined' do
|
644
|
+
let(:case_insensitive) { false }
|
578
645
|
|
646
|
+
it 'skips the loading of the (internal) insensitive_map' do
|
647
|
+
type.definition
|
648
|
+
type.insensitive_map.should be_nil
|
649
|
+
end
|
650
|
+
end
|
579
651
|
end
|
580
652
|
|
581
653
|
context 'with allow_extra keys option' 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: 2.6.
|
4
|
+
version: 2.6.1
|
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: 2015-
|
12
|
+
date: 2015-04-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: hashie
|
@@ -260,6 +260,8 @@ files:
|
|
260
260
|
- lib/attributor/example_mixin.rb
|
261
261
|
- lib/attributor/exceptions.rb
|
262
262
|
- lib/attributor/extensions/randexp.rb
|
263
|
+
- lib/attributor/families/numeric.rb
|
264
|
+
- lib/attributor/families/temporal.rb
|
263
265
|
- lib/attributor/type.rb
|
264
266
|
- lib/attributor/types/bigdecimal.rb
|
265
267
|
- lib/attributor/types/boolean.rb
|
@@ -285,10 +287,10 @@ files:
|
|
285
287
|
- spec/attribute_spec.rb
|
286
288
|
- spec/attributor_spec.rb
|
287
289
|
- spec/dsl_compiler_spec.rb
|
290
|
+
- spec/families_spec.rb
|
288
291
|
- spec/spec_helper.rb
|
289
292
|
- spec/support/hashes.rb
|
290
293
|
- spec/support/models.rb
|
291
|
-
- spec/support/types.rb
|
292
294
|
- spec/type_spec.rb
|
293
295
|
- spec/types/bigdecimal_spec.rb
|
294
296
|
- spec/types/boolean_spec.rb
|
@@ -336,10 +338,10 @@ test_files:
|
|
336
338
|
- spec/attribute_spec.rb
|
337
339
|
- spec/attributor_spec.rb
|
338
340
|
- spec/dsl_compiler_spec.rb
|
341
|
+
- spec/families_spec.rb
|
339
342
|
- spec/spec_helper.rb
|
340
343
|
- spec/support/hashes.rb
|
341
344
|
- spec/support/models.rb
|
342
|
-
- spec/support/types.rb
|
343
345
|
- spec/type_spec.rb
|
344
346
|
- spec/types/bigdecimal_spec.rb
|
345
347
|
- spec/types/boolean_spec.rb
|
data/spec/support/types.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
class AttributeType
|
2
|
-
include Attributor::Type
|
3
|
-
def self.native_type
|
4
|
-
::String
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
|
9
|
-
class IntegerAttributeType
|
10
|
-
include Attributor::Type
|
11
|
-
def self.native_type
|
12
|
-
::Integer
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
|
16
|
-
value.to_i
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|