hashie 2.0.5 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +36 -0
- data/.travis.yml +13 -6
- data/CHANGELOG.md +40 -21
- data/CONTRIBUTING.md +110 -19
- data/Gemfile +9 -0
- data/LICENSE +1 -1
- data/README.md +347 -0
- data/Rakefile +4 -2
- data/hashie.gemspec +4 -7
- data/lib/hashie.rb +3 -0
- data/lib/hashie/clash.rb +19 -19
- data/lib/hashie/dash.rb +47 -39
- data/lib/hashie/extensions/coercion.rb +10 -6
- data/lib/hashie/extensions/deep_fetch.rb +29 -0
- data/lib/hashie/extensions/deep_merge.rb +15 -6
- data/lib/hashie/extensions/ignore_undeclared.rb +41 -0
- data/lib/hashie/extensions/indifferent_access.rb +37 -10
- data/lib/hashie/extensions/key_conversion.rb +3 -3
- data/lib/hashie/extensions/method_access.rb +9 -9
- data/lib/hashie/hash.rb +7 -7
- data/lib/hashie/hash_extensions.rb +5 -7
- data/lib/hashie/mash.rb +38 -31
- data/lib/hashie/rash.rb +119 -0
- data/lib/hashie/trash.rb +31 -22
- data/lib/hashie/version.rb +1 -1
- data/spec/hashie/clash_spec.rb +43 -45
- data/spec/hashie/dash_spec.rb +115 -53
- data/spec/hashie/extensions/coercion_spec.rb +42 -37
- data/spec/hashie/extensions/deep_fetch_spec.rb +70 -0
- data/spec/hashie/extensions/deep_merge_spec.rb +11 -9
- data/spec/hashie/extensions/ignore_undeclared_spec.rb +23 -0
- data/spec/hashie/extensions/indifferent_access_spec.rb +117 -64
- data/spec/hashie/extensions/key_conversion_spec.rb +28 -27
- data/spec/hashie/extensions/merge_initializer_spec.rb +13 -10
- data/spec/hashie/extensions/method_access_spec.rb +49 -40
- data/spec/hashie/hash_spec.rb +25 -13
- data/spec/hashie/mash_spec.rb +243 -187
- data/spec/hashie/rash_spec.rb +44 -0
- data/spec/hashie/trash_spec.rb +81 -43
- data/spec/hashie/version_spec.rb +7 -0
- data/spec/spec_helper.rb +0 -4
- metadata +27 -78
- data/.document +0 -5
- data/README.markdown +0 -236
- data/lib/hashie/extensions/structure.rb +0 -47
@@ -1,20 +1,23 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Hashie::Extensions::MergeInitializer do
|
4
|
-
class MergeInitializerHash < Hash
|
5
|
-
|
4
|
+
class MergeInitializerHash < Hash
|
5
|
+
include Hashie::Extensions::MergeInitializer
|
6
|
+
end
|
7
|
+
|
8
|
+
subject { MergeInitializerHash }
|
6
9
|
|
7
|
-
it '
|
8
|
-
subject.new.should
|
10
|
+
it 'initializes with no arguments' do
|
11
|
+
subject.new.should eq({})
|
9
12
|
end
|
10
13
|
|
11
|
-
it '
|
12
|
-
subject.new(:
|
14
|
+
it 'initializes with a hash' do
|
15
|
+
subject.new(abc: 'def').should eq(abc: 'def')
|
13
16
|
end
|
14
17
|
|
15
|
-
it '
|
16
|
-
h = subject.new({:
|
17
|
-
h[:foo].should
|
18
|
-
h[:abc].should
|
18
|
+
it 'initializes with a hash and a default' do
|
19
|
+
h = subject.new({ abc: 'def' }, 'bar')
|
20
|
+
h[:foo].should eq 'bar'
|
21
|
+
h[:abc].should eq 'def'
|
19
22
|
end
|
20
23
|
end
|
@@ -2,39 +2,43 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Hashie::Extensions::MethodReader do
|
4
4
|
class ReaderHash < Hash
|
5
|
-
def initialize(hash = {}); self.update(hash) end
|
6
5
|
include Hashie::Extensions::MethodReader
|
6
|
+
|
7
|
+
def initialize(hash = {})
|
8
|
+
update(hash)
|
9
|
+
end
|
7
10
|
end
|
8
|
-
subject{ ReaderHash }
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
subject { ReaderHash }
|
13
|
+
|
14
|
+
it 'reads string keys from the method' do
|
15
|
+
subject.new('awesome' => 'sauce').awesome.should eq 'sauce'
|
12
16
|
end
|
13
17
|
|
14
|
-
it '
|
15
|
-
subject.new(:
|
18
|
+
it 'reads symbol keys from the method' do
|
19
|
+
subject.new(awesome: 'sauce').awesome.should eq 'sauce'
|
16
20
|
end
|
17
21
|
|
18
|
-
it '
|
19
|
-
h = subject.new(:
|
20
|
-
h.nil.should
|
21
|
-
h.false.should
|
22
|
+
it 'reads nil and false values out properly' do
|
23
|
+
h = subject.new(nil: nil, false: false)
|
24
|
+
h.nil.should eq nil
|
25
|
+
h.false.should eq false
|
22
26
|
end
|
23
27
|
|
24
|
-
it '
|
25
|
-
lambda{ subject.new.awesome }.should raise_error(NoMethodError)
|
28
|
+
it 'raises a NoMethodError for undefined keys' do
|
29
|
+
lambda { subject.new.awesome }.should raise_error(NoMethodError)
|
26
30
|
end
|
27
31
|
|
28
32
|
describe '#respond_to?' do
|
29
|
-
it '
|
33
|
+
it 'is true for string keys' do
|
30
34
|
subject.new('awesome' => 'sauce').should be_respond_to(:awesome)
|
31
35
|
end
|
32
36
|
|
33
|
-
it '
|
34
|
-
subject.new(:
|
37
|
+
it 'is true for symbol keys' do
|
38
|
+
subject.new(awesome: 'sauce').should be_respond_to(:awesome)
|
35
39
|
end
|
36
40
|
|
37
|
-
it '
|
41
|
+
it 'is false for non-keys' do
|
38
42
|
subject.new.should_not be_respond_to(:awesome)
|
39
43
|
end
|
40
44
|
end
|
@@ -44,24 +48,25 @@ describe Hashie::Extensions::MethodWriter do
|
|
44
48
|
class WriterHash < Hash
|
45
49
|
include Hashie::Extensions::MethodWriter
|
46
50
|
end
|
47
|
-
subject{ WriterHash.new }
|
48
51
|
|
49
|
-
|
52
|
+
subject { WriterHash.new }
|
53
|
+
|
54
|
+
it 'writes from a method call' do
|
50
55
|
subject.awesome = 'sauce'
|
51
|
-
subject['awesome'].should
|
56
|
+
subject['awesome'].should eq 'sauce'
|
52
57
|
end
|
53
58
|
|
54
|
-
it '
|
59
|
+
it 'converts the key using the #convert_key method' do
|
55
60
|
subject.stub!(:convert_key).and_return(:awesome)
|
56
61
|
subject.awesome = 'sauce'
|
57
|
-
subject[:awesome].should
|
62
|
+
subject[:awesome].should eq 'sauce'
|
58
63
|
end
|
59
64
|
|
60
|
-
it '
|
61
|
-
lambda{ subject.awesome }.should raise_error(NoMethodError)
|
65
|
+
it 'raises NoMethodError on non equals-ending methods' do
|
66
|
+
lambda { subject.awesome }.should raise_error(NoMethodError)
|
62
67
|
end
|
63
68
|
|
64
|
-
it '
|
69
|
+
it '#respond_to? correctly' do
|
65
70
|
subject.should be_respond_to(:abc=)
|
66
71
|
subject.should_not be_respond_to(:abc)
|
67
72
|
end
|
@@ -69,44 +74,48 @@ end
|
|
69
74
|
|
70
75
|
describe Hashie::Extensions::MethodQuery do
|
71
76
|
class QueryHash < Hash
|
72
|
-
def initialize(hash = {}); self.update(hash) end
|
73
77
|
include Hashie::Extensions::MethodQuery
|
78
|
+
|
79
|
+
def initialize(hash = {})
|
80
|
+
update(hash)
|
81
|
+
end
|
74
82
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
83
|
+
|
84
|
+
subject { QueryHash }
|
85
|
+
|
86
|
+
it 'is true for non-nil string key values' do
|
78
87
|
subject.new('abc' => 123).should be_abc
|
79
88
|
end
|
80
89
|
|
81
|
-
it '
|
82
|
-
subject.new(:
|
90
|
+
it 'is true for non-nil symbol key values' do
|
91
|
+
subject.new(abc: 123).should be_abc
|
83
92
|
end
|
84
93
|
|
85
|
-
it '
|
86
|
-
subject.new(:
|
94
|
+
it 'is false for nil key values' do
|
95
|
+
subject.new(abc: false).should_not be_abc
|
87
96
|
end
|
88
97
|
|
89
|
-
it '
|
90
|
-
lambda{ subject.new.abc? }.should raise_error(NoMethodError)
|
98
|
+
it 'raises a NoMethodError for non-set keys' do
|
99
|
+
lambda { subject.new.abc? }.should raise_error(NoMethodError)
|
91
100
|
end
|
92
101
|
|
93
|
-
it '
|
102
|
+
it '#respond_to? for existing string keys' do
|
94
103
|
subject.new('abc' => 'def').should be_respond_to('abc?')
|
95
104
|
end
|
96
105
|
|
97
|
-
it '
|
98
|
-
subject.new(:
|
106
|
+
it '#respond_to? for existing symbol keys' do
|
107
|
+
subject.new(abc: 'def').should be_respond_to(:abc?)
|
99
108
|
end
|
100
109
|
|
101
|
-
it '
|
110
|
+
it 'does not #respond_to? for non-existent keys' do
|
102
111
|
subject.new.should_not be_respond_to('abc?')
|
103
112
|
end
|
104
113
|
end
|
105
114
|
|
106
115
|
describe Hashie::Extensions::MethodAccess do
|
107
|
-
it '
|
116
|
+
it 'includes all of the other method mixins' do
|
108
117
|
klass = Class.new(Hash)
|
109
118
|
klass.send :include, Hashie::Extensions::MethodAccess
|
110
|
-
(klass.ancestors & [Hashie::Extensions::MethodReader, Hashie::Extensions::MethodWriter, Hashie::Extensions::MethodQuery]).size.should
|
119
|
+
(klass.ancestors & [Hashie::Extensions::MethodReader, Hashie::Extensions::MethodWriter, Hashie::Extensions::MethodQuery]).size.should eq 3
|
111
120
|
end
|
112
121
|
end
|
data/spec/hashie/hash_spec.rb
CHANGED
@@ -1,22 +1,34 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Hash do
|
4
|
-
it
|
5
|
-
mash = Hashie::Hash[:
|
4
|
+
it 'is convertible to a Hashie::Mash' do
|
5
|
+
mash = Hashie::Hash[some: 'hash'].to_mash
|
6
6
|
mash.is_a?(Hashie::Mash).should be_true
|
7
|
-
mash.some.should
|
7
|
+
mash.some.should eq 'hash'
|
8
8
|
end
|
9
|
-
|
10
|
-
it
|
11
|
-
hash = Hashie::Hash[:a =>
|
9
|
+
|
10
|
+
it '#stringify_keys! turns all keys into strings' do
|
11
|
+
hash = Hashie::Hash[:a => 'hey', 123 => 'bob']
|
12
12
|
hash.stringify_keys!
|
13
|
-
hash.should
|
13
|
+
hash.should eq Hashie::Hash['a' => 'hey', '123' => 'bob']
|
14
14
|
end
|
15
|
-
|
16
|
-
it
|
17
|
-
hash = Hashie::Hash[:a =>
|
15
|
+
|
16
|
+
it '#stringify_keys returns a hash with stringified keys' do
|
17
|
+
hash = Hashie::Hash[:a => 'hey', 123 => 'bob']
|
18
18
|
stringified_hash = hash.stringify_keys
|
19
|
-
hash.should
|
20
|
-
stringified_hash.should
|
19
|
+
hash.should eq Hashie::Hash[:a => 'hey', 123 => 'bob']
|
20
|
+
stringified_hash.should eq Hashie::Hash['a' => 'hey', '123' => 'bob']
|
21
|
+
end
|
22
|
+
|
23
|
+
it '#to_hash returns a hash with stringified keys' do
|
24
|
+
hash = Hashie::Hash['a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3]]
|
25
|
+
stringified_hash = hash.to_hash
|
26
|
+
stringified_hash.should eq('a' => 'hey', '123' => 'bob', 'array' => [1, 2, 3])
|
27
|
+
end
|
28
|
+
|
29
|
+
it '#to_hash with symbolize_keys set to true returns a hash with symbolized keys' do
|
30
|
+
hash = Hashie::Hash['a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3]]
|
31
|
+
symbolized_hash = hash.to_hash(symbolize_keys: true)
|
32
|
+
symbolized_hash.should eq(:a => 'hey', :"123" => 'bob', :array => [1, 2, 3])
|
21
33
|
end
|
22
|
-
end
|
34
|
+
end
|
data/spec/hashie/mash_spec.rb
CHANGED
@@ -2,207 +2,232 @@ require 'spec_helper'
|
|
2
2
|
require 'delegate'
|
3
3
|
|
4
4
|
describe Hashie::Mash do
|
5
|
-
|
6
|
-
@mash = Hashie::Mash.new
|
7
|
-
end
|
5
|
+
subject { Hashie::Mash.new }
|
8
6
|
|
9
|
-
it
|
10
|
-
|
7
|
+
it 'inherits from Hash' do
|
8
|
+
subject.is_a?(Hash).should be_true
|
11
9
|
end
|
12
10
|
|
13
|
-
it
|
14
|
-
|
15
|
-
|
11
|
+
it 'sets hash values through method= calls' do
|
12
|
+
subject.test = 'abc'
|
13
|
+
subject['test'].should eq 'abc'
|
16
14
|
end
|
17
15
|
|
18
|
-
it
|
19
|
-
|
20
|
-
|
16
|
+
it 'retrieves set values through method calls' do
|
17
|
+
subject['test'] = 'abc'
|
18
|
+
subject.test.should eq 'abc'
|
21
19
|
end
|
22
20
|
|
23
|
-
it
|
24
|
-
|
21
|
+
it 'retrieves set values through blocks' do
|
22
|
+
subject['test'] = 'abc'
|
25
23
|
value = nil
|
26
|
-
|
27
|
-
value.should
|
24
|
+
subject.[]('test') { |v| value = v }
|
25
|
+
value.should eq 'abc'
|
28
26
|
end
|
29
27
|
|
30
|
-
it
|
31
|
-
|
28
|
+
it 'retrieves set values through blocks with method calls' do
|
29
|
+
subject['test'] = 'abc'
|
32
30
|
value = nil
|
33
|
-
|
34
|
-
value.should
|
31
|
+
subject.test { |v| value = v }
|
32
|
+
value.should eq 'abc'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'tests for already set values when passed a ? method' do
|
36
|
+
subject.test?.should be_false
|
37
|
+
subject.test = 'abc'
|
38
|
+
subject.test?.should be_true
|
35
39
|
end
|
36
40
|
|
37
|
-
it
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
+
it 'returns false on a ? method if a value has been set to nil or false' do
|
42
|
+
subject.test = nil
|
43
|
+
subject.should_not be_test
|
44
|
+
subject.test = false
|
45
|
+
subject.should_not be_test
|
41
46
|
end
|
42
47
|
|
43
|
-
it
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@mash.should_not be_test
|
48
|
+
it 'makes all [] and []= into strings for consistency' do
|
49
|
+
subject['abc'] = 123
|
50
|
+
subject.key?('abc').should be_true
|
51
|
+
subject['abc'].should eq 123
|
48
52
|
end
|
49
53
|
|
50
|
-
it
|
51
|
-
|
52
|
-
|
53
|
-
@mash["abc"].should == 123
|
54
|
+
it 'has a to_s that is identical to its inspect' do
|
55
|
+
subject.abc = 123
|
56
|
+
subject.to_s.should eq subject.inspect
|
54
57
|
end
|
55
58
|
|
56
|
-
it
|
57
|
-
|
58
|
-
@mash.to_s.should == @mash.inspect
|
59
|
+
it 'returns nil instead of raising an error for attribute-esque method calls' do
|
60
|
+
subject.abc.should be_nil
|
59
61
|
end
|
60
62
|
|
61
|
-
it
|
62
|
-
|
63
|
+
it 'returns the default value if set like Hash' do
|
64
|
+
subject.default = 123
|
65
|
+
subject.abc.should eq 123
|
63
66
|
end
|
64
67
|
|
65
|
-
it
|
66
|
-
|
68
|
+
it 'gracefully handles being accessed with arguments' do
|
69
|
+
subject.abc('foobar').should eq nil
|
70
|
+
subject.abc = 123
|
71
|
+
subject.abc('foobar').should eq 123
|
67
72
|
end
|
68
73
|
|
69
|
-
it
|
70
|
-
|
71
|
-
@mash.name!.should == "Bob"
|
74
|
+
it 'returns a Hashie::Mash when passed a bang method to a non-existenct key' do
|
75
|
+
subject.abc!.is_a?(Hashie::Mash).should be_true
|
72
76
|
end
|
73
77
|
|
74
|
-
it
|
75
|
-
|
78
|
+
it 'returns the existing value when passed a bang method for an existing key' do
|
79
|
+
subject.name = 'Bob'
|
80
|
+
subject.name!.should eq 'Bob'
|
76
81
|
end
|
77
82
|
|
78
|
-
it
|
79
|
-
|
80
|
-
@mash.name_.should == "Bob"
|
83
|
+
it 'returns a Hashie::Mash when passed an under bang method to a non-existenct key' do
|
84
|
+
subject.abc_.is_a?(Hashie::Mash).should be_true
|
81
85
|
end
|
82
86
|
|
83
|
-
it
|
84
|
-
|
87
|
+
it 'returns the existing value when passed an under bang method for an existing key' do
|
88
|
+
subject.name = 'Bob'
|
89
|
+
subject.name_.should eq 'Bob'
|
85
90
|
end
|
86
91
|
|
87
|
-
it
|
88
|
-
|
89
|
-
@mash.author.should == Hashie::Mash.new(:name => "Michael Bleigh")
|
90
|
-
@mash.author!.website!.url = "http://www.mbleigh.com/"
|
91
|
-
@mash.author.website.should == Hashie::Mash.new(:url => "http://www.mbleigh.com/")
|
92
|
+
it '#initializing_reader returns a Hashie::Mash when passed a non-existent key' do
|
93
|
+
subject.initializing_reader(:abc).is_a?(Hashie::Mash).should be_true
|
92
94
|
end
|
93
95
|
|
94
|
-
it
|
95
|
-
|
96
|
-
|
97
|
-
|
96
|
+
it 'allows for multi-level assignment through bang methods' do
|
97
|
+
subject.author!.name = 'Michael Bleigh'
|
98
|
+
subject.author.should eq Hashie::Mash.new(name: 'Michael Bleigh')
|
99
|
+
subject.author!.website!.url = 'http://www.mbleigh.com/'
|
100
|
+
subject.author.website.should eq Hashie::Mash.new(url: 'http://www.mbleigh.com/')
|
98
101
|
end
|
99
102
|
|
100
|
-
it
|
101
|
-
|
103
|
+
it 'allows for multi-level under bang testing' do
|
104
|
+
subject.author_.website_.url.should be_nil
|
105
|
+
subject.author_.website_.url?.should eq false
|
106
|
+
subject.author.should be_nil
|
102
107
|
end
|
103
108
|
|
104
|
-
it
|
105
|
-
|
106
|
-
@mash.id.should == "Steve"
|
109
|
+
it 'does not call super if id is not a key' do
|
110
|
+
subject.id.should eq nil
|
107
111
|
end
|
108
112
|
|
109
|
-
it
|
110
|
-
|
113
|
+
it 'returns the value if id is a key' do
|
114
|
+
subject.id = 'Steve'
|
115
|
+
subject.id.should eq 'Steve'
|
111
116
|
end
|
112
117
|
|
113
|
-
it
|
114
|
-
|
115
|
-
@mash.type.should == "Steve"
|
118
|
+
it 'does not call super if type is not a key' do
|
119
|
+
subject.type.should eq nil
|
116
120
|
end
|
117
121
|
|
118
|
-
|
119
|
-
subject
|
120
|
-
|
121
|
-
|
122
|
-
}
|
122
|
+
it 'returns the value if type is a key' do
|
123
|
+
subject.type = 'Steve'
|
124
|
+
subject.type.should eq 'Steve'
|
125
|
+
end
|
123
126
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
127
|
+
context 'updating' do
|
128
|
+
subject do
|
129
|
+
described_class.new(
|
130
|
+
first_name: 'Michael',
|
131
|
+
last_name: 'Bleigh',
|
132
|
+
details: {
|
133
|
+
email: 'michael@asf.com',
|
134
|
+
address: 'Nowhere road'
|
135
|
+
})
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#deep_update' do
|
139
|
+
it 'recursively Hashie::Mash Hashie::Mashes and hashes together' do
|
140
|
+
subject.deep_update(details: { email: 'michael@intridea.com', city: 'Imagineton' })
|
141
|
+
subject.first_name.should eq 'Michael'
|
142
|
+
subject.details.email.should eq 'michael@intridea.com'
|
143
|
+
subject.details.address.should eq 'Nowhere road'
|
144
|
+
subject.details.city.should eq 'Imagineton'
|
131
145
|
end
|
132
146
|
|
133
|
-
it
|
134
|
-
|
135
|
-
|
136
|
-
|
147
|
+
it 'converts values only once' do
|
148
|
+
class ConvertedMash < Hashie::Mash
|
149
|
+
end
|
150
|
+
|
151
|
+
rhs = ConvertedMash.new(email: 'foo@bar.com')
|
152
|
+
subject.should_receive(:convert_value).exactly(1).times
|
153
|
+
subject.deep_update(rhs)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'makes #update deep by default' do
|
157
|
+
subject.update(details: { address: 'Fake street' }).should eql(subject)
|
158
|
+
subject.details.address.should eq 'Fake street'
|
159
|
+
subject.details.email.should eq 'michael@asf.com'
|
137
160
|
end
|
138
161
|
|
139
|
-
it
|
140
|
-
duped = subject.deep_merge(:
|
162
|
+
it 'clones before a #deep_merge' do
|
163
|
+
duped = subject.deep_merge(details: { address: 'Fake street' })
|
141
164
|
duped.should_not eql(subject)
|
142
|
-
duped.details.address.should
|
143
|
-
subject.details.address.should
|
144
|
-
duped.details.email.should
|
165
|
+
duped.details.address.should eq 'Fake street'
|
166
|
+
subject.details.address.should eq 'Nowhere road'
|
167
|
+
duped.details.email.should eq 'michael@asf.com'
|
145
168
|
end
|
146
169
|
|
147
|
-
it
|
148
|
-
duped = subject.merge(:
|
170
|
+
it 'default #merge is deep' do
|
171
|
+
duped = subject.merge(details: { email: 'michael@intridea.com' })
|
149
172
|
duped.should_not eql(subject)
|
150
|
-
duped.details.email.should
|
151
|
-
duped.details.address.should
|
173
|
+
duped.details.email.should eq 'michael@intridea.com'
|
174
|
+
duped.details.address.should eq 'Nowhere road'
|
152
175
|
end
|
153
176
|
|
154
177
|
# http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-update
|
155
|
-
it
|
156
|
-
duped = subject.merge(:
|
157
|
-
duped.details.address.should
|
178
|
+
it 'accepts a block' do
|
179
|
+
duped = subject.merge(details: { address: 'Pasadena CA' }) { |key, oldv, newv| [oldv, newv].join(', ') }
|
180
|
+
duped.details.address.should eq 'Nowhere road, Pasadena CA'
|
158
181
|
end
|
159
182
|
end
|
160
183
|
|
161
|
-
describe
|
162
|
-
it
|
163
|
-
subject.shallow_update(:
|
164
|
-
|
184
|
+
describe 'shallow update' do
|
185
|
+
it 'shallowly Hashie::Mash Hashie::Mashes and hashes together' do
|
186
|
+
subject.shallow_update(details: {
|
187
|
+
email: 'michael@intridea.com', city: 'Imagineton'
|
165
188
|
}).should eql(subject)
|
166
189
|
|
167
|
-
subject.first_name.should
|
168
|
-
subject.details.email.should
|
190
|
+
subject.first_name.should eq 'Michael'
|
191
|
+
subject.details.email.should eq 'michael@intridea.com'
|
169
192
|
subject.details.address.should be_nil
|
170
|
-
subject.details.city.should
|
193
|
+
subject.details.city.should eq 'Imagineton'
|
171
194
|
end
|
172
195
|
|
173
|
-
it
|
174
|
-
duped = subject.shallow_merge(:
|
196
|
+
it 'clones before a #regular_merge' do
|
197
|
+
duped = subject.shallow_merge(details: { address: 'Fake street' })
|
175
198
|
duped.should_not eql(subject)
|
176
199
|
end
|
177
200
|
|
178
|
-
it
|
179
|
-
duped = subject.shallow_merge(:
|
180
|
-
duped.details.address.should
|
181
|
-
subject.details.address.should
|
201
|
+
it 'default #merge is shallow' do
|
202
|
+
duped = subject.shallow_merge(details: { address: 'Fake street' })
|
203
|
+
duped.details.address.should eq 'Fake street'
|
204
|
+
subject.details.address.should eq 'Nowhere road'
|
182
205
|
duped.details.email.should be_nil
|
183
206
|
end
|
184
207
|
end
|
185
208
|
|
186
209
|
describe '#replace' do
|
187
210
|
before do
|
188
|
-
subject.replace(
|
189
|
-
:
|
211
|
+
subject.replace(
|
212
|
+
middle_name: 'Cain',
|
213
|
+
details: { city: 'Imagination' }
|
214
|
+
)
|
190
215
|
end
|
191
216
|
|
192
|
-
it '
|
193
|
-
subject.replace(:
|
217
|
+
it 'returns self' do
|
218
|
+
subject.replace(foo: 'bar').to_hash.should eq('foo' => 'bar')
|
194
219
|
end
|
195
220
|
|
196
221
|
it 'sets all specified keys to their corresponding values' do
|
197
222
|
subject.middle_name?.should be_true
|
198
223
|
subject.details?.should be_true
|
199
|
-
subject.middle_name.should
|
224
|
+
subject.middle_name.should eq 'Cain'
|
200
225
|
subject.details.city?.should be_true
|
201
|
-
subject.details.city.should
|
226
|
+
subject.details.city.should eq 'Imagination'
|
202
227
|
end
|
203
228
|
|
204
229
|
it 'leaves only specified keys' do
|
205
|
-
subject.keys.sort.should
|
230
|
+
subject.keys.sort.should eq %w(details middle_name)
|
206
231
|
subject.first_name?.should be_false
|
207
232
|
subject.should_not respond_to(:first_name)
|
208
233
|
subject.last_name?.should be_false
|
@@ -211,13 +236,13 @@ describe Hashie::Mash do
|
|
211
236
|
end
|
212
237
|
|
213
238
|
describe 'delete' do
|
214
|
-
it '
|
239
|
+
it 'deletes with String key' do
|
215
240
|
subject.delete('details')
|
216
241
|
subject.details.should be_nil
|
217
242
|
subject.should_not be_respond_to :details
|
218
243
|
end
|
219
244
|
|
220
|
-
it '
|
245
|
+
it 'deletes with Symbol key' do
|
221
246
|
subject.delete(:details)
|
222
247
|
subject.details.should be_nil
|
223
248
|
subject.should_not be_respond_to :details
|
@@ -225,22 +250,22 @@ describe Hashie::Mash do
|
|
225
250
|
end
|
226
251
|
end
|
227
252
|
|
228
|
-
it
|
229
|
-
|
230
|
-
|
231
|
-
|
253
|
+
it 'converts hash assignments into Hashie::Mashes' do
|
254
|
+
subject.details = { email: 'randy@asf.com', address: { state: 'TX' } }
|
255
|
+
subject.details.email.should eq 'randy@asf.com'
|
256
|
+
subject.details.address.state.should eq 'TX'
|
232
257
|
end
|
233
258
|
|
234
|
-
it
|
259
|
+
it 'does not convert the type of Hashie::Mashes childs to Hashie::Mash' do
|
235
260
|
class MyMash < Hashie::Mash
|
236
261
|
end
|
237
262
|
|
238
263
|
record = MyMash.new
|
239
264
|
record.son = MyMash.new
|
240
|
-
record.son.class.should
|
265
|
+
record.son.class.should eq MyMash
|
241
266
|
end
|
242
267
|
|
243
|
-
it
|
268
|
+
it 'does not change the class of Mashes when converted' do
|
244
269
|
class SubMash < Hashie::Mash
|
245
270
|
end
|
246
271
|
|
@@ -250,7 +275,7 @@ describe Hashie::Mash do
|
|
250
275
|
record['submash'].should be_kind_of(SubMash)
|
251
276
|
end
|
252
277
|
|
253
|
-
it
|
278
|
+
it 'respects the class when passed a bang method for a non-existent key' do
|
254
279
|
record = Hashie::Mash.new
|
255
280
|
record.non_existent!.should be_kind_of(Hashie::Mash)
|
256
281
|
|
@@ -261,7 +286,7 @@ describe Hashie::Mash do
|
|
261
286
|
son.non_existent!.should be_kind_of(SubMash)
|
262
287
|
end
|
263
288
|
|
264
|
-
it
|
289
|
+
it 'respects the class when passed an under bang method for a non-existent key' do
|
265
290
|
record = Hashie::Mash.new
|
266
291
|
record.non_existent_.should be_kind_of(Hashie::Mash)
|
267
292
|
|
@@ -272,141 +297,172 @@ describe Hashie::Mash do
|
|
272
297
|
son.non_existent_.should be_kind_of(SubMash)
|
273
298
|
end
|
274
299
|
|
275
|
-
it
|
300
|
+
it 'respects the class when converting the value' do
|
276
301
|
record = Hashie::Mash.new
|
277
|
-
record.details = Hashie::Mash.new(
|
302
|
+
record.details = Hashie::Mash.new(email: 'randy@asf.com')
|
278
303
|
record.details.should be_kind_of(Hashie::Mash)
|
279
304
|
end
|
280
305
|
|
281
|
-
it
|
306
|
+
it 'respects another subclass when converting the value' do
|
282
307
|
record = Hashie::Mash.new
|
283
308
|
|
284
309
|
class SubMash < Hashie::Mash
|
285
310
|
end
|
286
311
|
|
287
|
-
son = SubMash.new(
|
312
|
+
son = SubMash.new(email: 'foo@bar.com')
|
288
313
|
record.details = son
|
289
314
|
record.details.should be_kind_of(SubMash)
|
290
315
|
end
|
291
316
|
|
292
|
-
describe
|
293
|
-
it '
|
317
|
+
describe '#respond_to?' do
|
318
|
+
it 'responds to a normal method' do
|
294
319
|
Hashie::Mash.new.should be_respond_to(:key?)
|
295
320
|
end
|
296
321
|
|
297
|
-
it '
|
298
|
-
Hashie::Mash.new(:
|
322
|
+
it 'responds to a set key' do
|
323
|
+
Hashie::Mash.new(abc: 'def').should be_respond_to(:abc)
|
299
324
|
end
|
300
325
|
|
301
|
-
it '
|
326
|
+
it 'responds to a set key with a suffix' do
|
302
327
|
%w(= ? ! _).each do |suffix|
|
303
|
-
Hashie::Mash.new(:
|
328
|
+
Hashie::Mash.new(abc: 'def').should be_respond_to(:"abc#{suffix}")
|
304
329
|
end
|
305
330
|
end
|
306
331
|
|
307
|
-
it '
|
332
|
+
it 'does not respond to an unknown key with a suffix' do
|
308
333
|
%w(= ? ! _).each do |suffix|
|
309
|
-
Hashie::Mash.new(:
|
334
|
+
Hashie::Mash.new(abc: 'def').should_not be_respond_to(:"xyz#{suffix}")
|
310
335
|
end
|
311
336
|
end
|
312
337
|
|
313
|
-
it
|
314
|
-
Hashie::Mash.new(:
|
338
|
+
it 'does not respond to an unknown key without a suffix' do
|
339
|
+
Hashie::Mash.new(abc: 'def').should_not be_respond_to(:xyz)
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'does not respond to permitted?' do
|
343
|
+
Hashie::Mash.new.should_not be_respond_to(:permitted?)
|
315
344
|
end
|
316
345
|
end
|
317
346
|
|
318
|
-
context
|
319
|
-
it
|
320
|
-
converted = Hashie::Mash.new(
|
321
|
-
converted.abc.should
|
322
|
-
converted.name.should
|
347
|
+
context '#initialize' do
|
348
|
+
it 'converts an existing hash to a Hashie::Mash' do
|
349
|
+
converted = Hashie::Mash.new(abc: 123, name: 'Bob')
|
350
|
+
converted.abc.should eq 123
|
351
|
+
converted.name.should eq 'Bob'
|
323
352
|
end
|
324
353
|
|
325
|
-
it
|
326
|
-
converted = Hashie::Mash.new(
|
354
|
+
it 'converts hashes recursively into Hashie::Mashes' do
|
355
|
+
converted = Hashie::Mash.new(a: { b: 1, c: { d: 23 } })
|
327
356
|
converted.a.is_a?(Hashie::Mash).should be_true
|
328
|
-
converted.a.b.should
|
329
|
-
converted.a.c.d.should
|
357
|
+
converted.a.b.should eq 1
|
358
|
+
converted.a.c.d.should eq 23
|
330
359
|
end
|
331
360
|
|
332
|
-
it
|
333
|
-
converted = Hashie::Mash.new(
|
334
|
-
converted.a.first.b.should
|
335
|
-
converted.a.last.should
|
361
|
+
it 'converts hashes in arrays into Hashie::Mashes' do
|
362
|
+
converted = Hashie::Mash.new(a: [{ b: 12 }, 23])
|
363
|
+
converted.a.first.b.should eq 12
|
364
|
+
converted.a.last.should eq 23
|
336
365
|
end
|
337
366
|
|
338
|
-
it
|
339
|
-
initial = Hashie::Mash.new(:
|
367
|
+
it 'converts an existing Hashie::Mash into a Hashie::Mash' do
|
368
|
+
initial = Hashie::Mash.new(name: 'randy', address: { state: 'TX' })
|
340
369
|
copy = Hashie::Mash.new(initial)
|
341
|
-
initial.name.should
|
342
|
-
initial.__id__.should_not
|
343
|
-
copy.address.state.should
|
370
|
+
initial.name.should eq copy.name
|
371
|
+
initial.__id__.should_not eq copy.__id__
|
372
|
+
copy.address.state.should eq 'TX'
|
344
373
|
copy.address.state = 'MI'
|
345
|
-
initial.address.state.should
|
346
|
-
copy.address.__id__.should_not
|
374
|
+
initial.address.state.should eq 'TX'
|
375
|
+
copy.address.__id__.should_not eq initial.address.__id__
|
347
376
|
end
|
348
377
|
|
349
|
-
it
|
350
|
-
initial = Hashie::Mash.new { |h,i| h[i] = []}
|
378
|
+
it 'accepts a default block' do
|
379
|
+
initial = Hashie::Mash.new { |h, i| h[i] = [] }
|
351
380
|
initial.default_proc.should_not be_nil
|
352
381
|
initial.default.should be_nil
|
353
|
-
initial.test.should
|
382
|
+
initial.test.should eq []
|
354
383
|
initial.test?.should be_true
|
355
384
|
end
|
356
385
|
|
357
|
-
it
|
358
|
-
initial_hash = {
|
386
|
+
it 'converts Hashie::Mashes within Arrays back to Hashes' do
|
387
|
+
initial_hash = { 'a' => [{ 'b' => 12, 'c' => ['d' => 50, 'e' => 51] }, 23] }
|
359
388
|
converted = Hashie::Mash.new(initial_hash)
|
360
|
-
converted.to_hash[
|
361
|
-
converted.to_hash[
|
362
|
-
converted.to_hash[
|
363
|
-
converted.to_hash({:symbolize_keys => true}).keys[0].should == :a
|
389
|
+
converted.to_hash['a'].first.is_a?(Hashie::Mash).should be_false
|
390
|
+
converted.to_hash['a'].first.is_a?(Hash).should be_true
|
391
|
+
converted.to_hash['a'].first['c'].first.is_a?(Hashie::Mash).should be_false
|
364
392
|
end
|
365
393
|
end
|
366
394
|
|
367
|
-
describe
|
368
|
-
let(:hash) { {:
|
395
|
+
describe '#fetch' do
|
396
|
+
let(:hash) { { one: 1, other: false } }
|
369
397
|
let(:mash) { Hashie::Mash.new(hash) }
|
370
398
|
|
371
|
-
context
|
372
|
-
it
|
399
|
+
context 'when key exists' do
|
400
|
+
it 'returns the value' do
|
373
401
|
mash.fetch(:one).should eql(1)
|
374
402
|
end
|
375
403
|
|
376
|
-
it
|
404
|
+
it 'returns the value even if the value is falsy' do
|
377
405
|
mash.fetch(:other).should eql(false)
|
378
406
|
end
|
379
407
|
|
380
|
-
context
|
381
|
-
it
|
408
|
+
context 'when key has other than original but acceptable type' do
|
409
|
+
it 'returns the value' do
|
382
410
|
mash.fetch('one').should eql(1)
|
383
411
|
end
|
384
412
|
end
|
385
413
|
end
|
386
414
|
|
387
|
-
context
|
388
|
-
it
|
415
|
+
context 'when key does not exist' do
|
416
|
+
it 'raises KeyError' do
|
389
417
|
error = RUBY_VERSION =~ /1.8/ ? IndexError : KeyError
|
390
418
|
expect { mash.fetch(:two) }.to raise_error(error)
|
391
419
|
end
|
392
420
|
|
393
|
-
context
|
394
|
-
it
|
421
|
+
context 'with default value given' do
|
422
|
+
it 'returns default value' do
|
395
423
|
mash.fetch(:two, 8).should eql(8)
|
396
424
|
end
|
397
425
|
|
398
|
-
it
|
426
|
+
it 'returns default value even if it is falsy' do
|
399
427
|
mash.fetch(:two, false).should eql(false)
|
400
428
|
end
|
401
429
|
end
|
402
430
|
|
403
|
-
context
|
404
|
-
it
|
405
|
-
mash.fetch(:two)
|
406
|
-
|
407
|
-
|
431
|
+
context 'with block given' do
|
432
|
+
it 'returns default value' do
|
433
|
+
mash.fetch(:two) do |key|
|
434
|
+
'block default value'
|
435
|
+
end.should eql('block default value')
|
408
436
|
end
|
409
437
|
end
|
410
438
|
end
|
411
439
|
end
|
440
|
+
|
441
|
+
describe '#to_hash' do
|
442
|
+
let(:hash) { { 'outer' => { 'inner' => 42 }, 'testing' => [1, 2, 3] } }
|
443
|
+
let(:mash) { Hashie::Mash.new(hash) }
|
444
|
+
|
445
|
+
it 'returns a standard Hash' do
|
446
|
+
mash.to_hash.should be_a(::Hash)
|
447
|
+
end
|
448
|
+
|
449
|
+
it 'includes all keys' do
|
450
|
+
mash.to_hash.keys.should eql(%w(outer testing))
|
451
|
+
end
|
452
|
+
|
453
|
+
it 'converts keys to symbols when symbolize_keys option is true' do
|
454
|
+
mash.to_hash(symbolize_keys: true).keys.should include(:outer)
|
455
|
+
mash.to_hash(symbolize_keys: true).keys.should_not include('outer')
|
456
|
+
end
|
457
|
+
|
458
|
+
it 'leaves keys as strings when symbolize_keys option is false' do
|
459
|
+
mash.to_hash(symbolize_keys: false).keys.should include('outer')
|
460
|
+
mash.to_hash(symbolize_keys: false).keys.should_not include(:outer)
|
461
|
+
end
|
462
|
+
|
463
|
+
it 'symbolizes keys recursively' do
|
464
|
+
mash.to_hash(symbolize_keys: true)[:outer].keys.should include(:inner)
|
465
|
+
mash.to_hash(symbolize_keys: true)[:outer].keys.should_not include('inner')
|
466
|
+
end
|
467
|
+
end
|
412
468
|
end
|