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 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