attributor 2.6.0 → 2.6.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|