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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b366557c378f786a7a4620793c05bdb06504ef3e
4
- data.tar.gz: 728171cc3c89f03c7f998fb24db74fada6c845a8
3
+ metadata.gz: ab74d0c797f7d246ba2e19564b9a215fcb1e8e73
4
+ data.tar.gz: 780a566c96c190af27f559aaf9052ccddcc5d237
5
5
  SHA512:
6
- metadata.gz: facbc89fd9ac8b18cd60611b3699bf3308854890becc782522959497d81c6c89abf59dbc29219c5c9e378a8ff317bfb67b511696c377bd6c024ad234ba3f5866
7
- data.tar.gz: 0abb9af1bde8c73bf374686aeb441ef6aba3426f9767970a072d2730d6e71b3e164c9458d1bcb42a48851f424bfdff302096a78300bf8758c0b5461743a13e8f
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'
@@ -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.class.attributes.each do |name, _|
10
- obj.define_singleton_method(name) do
11
- get(name)
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,17 @@
1
+ # Abstract type for the 'numeric' family
2
+
3
+ module Attributor
4
+
5
+ class Numeric
6
+ include Type
7
+
8
+ def self.native_type
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def self.family
13
+ 'numeric'
14
+ end
15
+
16
+ end
17
+ 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
@@ -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
@@ -2,8 +2,7 @@ require 'bigdecimal'
2
2
 
3
3
  module Attributor
4
4
 
5
- class BigDecimal
6
- include Type
5
+ class BigDecimal < Numeric
7
6
 
8
7
  def self.native_type
9
8
  return ::BigDecimal
@@ -23,6 +23,11 @@ module Attributor
23
23
  return true if [ true, 'true', 'TRUE', '1', 1, 't', 'T' ].include?(value)
24
24
  raise CoercionError, context: context, from: value.class, to: self
25
25
  end
26
+
27
+ def self.family
28
+ 'boolean'
29
+ end
30
+
26
31
  end
27
32
  end
28
33
 
@@ -25,6 +25,10 @@ module Attributor
25
25
  return ::Array
26
26
  end
27
27
 
28
+ def self.family
29
+ 'array'
30
+ end
31
+
28
32
  def self.member_type
29
33
  @member_type ||= Attributor::Object
30
34
  end
@@ -29,5 +29,9 @@ module Attributor
29
29
  return collection.join(',')
30
30
  end
31
31
 
32
+ def self.family
33
+ Collection.family
34
+ end
35
+
32
36
  end
33
37
  end
@@ -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
- def self.dump(value,**opts)
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
- @insensitive_map = self.keys.keys.each_with_object({}) do |k, map|
82
- map[k.downcase] = k
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
- def each_pair(&block)
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(@contents.merge(h.contents))
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,8 +2,7 @@
2
2
 
3
3
  module Attributor
4
4
 
5
- class Integer
6
- include Type
5
+ class Integer < Numeric
7
6
 
8
7
  EXAMPLE_RANGE = 1000.freeze
9
8
 
@@ -23,6 +23,11 @@ module Attributor
23
23
  return /\w+/.gen
24
24
  end
25
25
  end
26
+
27
+ def self.family
28
+ 'string'
29
+ end
30
+
26
31
  end
27
32
 
28
33
  end
@@ -16,6 +16,11 @@ module Attributor
16
16
  def self.example(context=nil, options:{})
17
17
  :example
18
18
  end
19
+
20
+ def self.family
21
+ String.family
22
+ end
23
+
19
24
  end
20
25
 
21
26
  end
@@ -31,6 +31,9 @@ module Attributor
31
31
  super
32
32
  end
33
33
 
34
+ def self.family
35
+ String.family
36
+ end
34
37
 
35
38
  end
36
39
  end
@@ -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
- def self.dump(value,**opts)
37
- value && value.iso8601
38
- end
39
-
40
-
36
+
41
37
  end
42
38
 
43
39
  end
@@ -1,3 +1,3 @@
1
1
  module Attributor
2
- VERSION = "2.6.0"
2
+ VERSION = "2.6.1"
3
3
  end
@@ -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) { AttributeType }
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.name, id: type.id}}
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) { IntegerAttributeType}
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
 
@@ -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
@@ -28,3 +28,9 @@ RSpec.configure do |config|
28
28
  end
29
29
 
30
30
  end
31
+
32
+ RSpec::Matchers.define :be_in_family do |expected|
33
+ match do |actual|
34
+ actual.family == expected
35
+ end
36
+ end
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) { AttributeType }
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('AttributeType')}
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, /AttributeType cannot load values of type Fixnum.*while loading top.sub/)
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
 
@@ -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 'with case_insensitive_load option for string keys' do
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
- it 'maps the incoming keys to defined keys, regardless of case' do
574
- output['downcase'].should eq(1)
575
- output['UPCASE'].should eq(2)
576
- output['CamelCase'].should eq(3)
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.0
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-03-17 00:00:00.000000000 Z
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
@@ -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