hashie 1.2.0 → 2.0.0

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.
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashie::Extensions::KeyConversion do
4
+ subject do
5
+ klass = Class.new(::Hash)
6
+ klass.send :include, Hashie::Extensions::KeyConversion
7
+ klass
8
+ end
9
+ let(:instance){ subject.new }
10
+
11
+ describe '#stringify_keys!' do
12
+ it 'should convert keys to strings' do
13
+ instance[:abc] = 'abc'
14
+ instance[123] = '123'
15
+ instance.stringify_keys!
16
+ (instance.keys & %w(abc 123)).size.should == 2
17
+ end
18
+
19
+ it 'should do deep conversion within nested hashes' do
20
+ instance[:ab] = subject.new
21
+ instance[:ab][:cd] = subject.new
22
+ instance[:ab][:cd][:ef] = 'abcdef'
23
+ instance.stringify_keys!
24
+ instance.should == {'ab' => {'cd' => {'ef' => 'abcdef'}}}
25
+ end
26
+
27
+ it 'should do deep conversion within nested arrays' do
28
+ instance[:ab] = []
29
+ instance[:ab] << subject.new
30
+ instance[:ab] << subject.new
31
+ instance[:ab][0][:cd] = 'abcd'
32
+ instance[:ab][1][:ef] = 'abef'
33
+ instance.stringify_keys!
34
+ instance.should == {'ab' => [{'cd' => 'abcd'}, {'ef' => 'abef'}]}
35
+ end
36
+
37
+ it 'should return itself' do
38
+ instance.stringify_keys!.should == instance
39
+ end
40
+ end
41
+
42
+ describe '#stringify_keys' do
43
+ it 'should convert keys to strings' do
44
+ instance[:abc] = 'def'
45
+ copy = instance.stringify_keys
46
+ copy['abc'].should == 'def'
47
+ end
48
+
49
+ it 'should not alter the original' do
50
+ instance[:abc] = 'def'
51
+ copy = instance.stringify_keys
52
+ instance.keys.should == [:abc]
53
+ copy.keys.should == %w(abc)
54
+ end
55
+ end
56
+
57
+ describe '#symbolize_keys!' do
58
+ it 'should convert keys to symbols' do
59
+ instance['abc'] = 'abc'
60
+ instance['def'] = 'def'
61
+ instance.symbolize_keys!
62
+ (instance.keys & [:abc, :def]).size.should == 2
63
+ end
64
+
65
+ it 'should do deep conversion within nested hashes' do
66
+ instance['ab'] = subject.new
67
+ instance['ab']['cd'] = subject.new
68
+ instance['ab']['cd']['ef'] = 'abcdef'
69
+ instance.symbolize_keys!
70
+ instance.should == {:ab => {:cd => {:ef => 'abcdef'}}}
71
+ end
72
+
73
+ it 'should do deep conversion within nested arrays' do
74
+ instance['ab'] = []
75
+ instance['ab'] << subject.new
76
+ instance['ab'] << subject.new
77
+ instance['ab'][0]['cd'] = 'abcd'
78
+ instance['ab'][1]['ef'] = 'abef'
79
+ instance.symbolize_keys!
80
+ instance.should == {:ab => [{:cd => 'abcd'}, {:ef => 'abef'}]}
81
+ end
82
+
83
+ it 'should return itself' do
84
+ instance.symbolize_keys!.should == instance
85
+ end
86
+ end
87
+
88
+ describe '#symbolize_keys' do
89
+ it 'should convert keys to symbols' do
90
+ instance['abc'] = 'def'
91
+ copy = instance.symbolize_keys
92
+ copy[:abc].should == 'def'
93
+ end
94
+
95
+ it 'should not alter the original' do
96
+ instance['abc'] = 'def'
97
+ copy = instance.symbolize_keys
98
+ instance.keys.should == ['abc']
99
+ copy.keys.should == [:abc]
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashie::Extensions::MergeInitializer do
4
+ class MergeInitializerHash < Hash; include Hashie::Extensions::MergeInitializer end
5
+ subject{ MergeInitializerHash }
6
+
7
+ it 'should initialize fine with no arguments' do
8
+ subject.new.should == {}
9
+ end
10
+
11
+ it 'should initialize with a hash' do
12
+ subject.new(:abc => 'def').should == {:abc => 'def'}
13
+ end
14
+
15
+ it 'should initialize with a hash and a default' do
16
+ h = subject.new({:abc => 'def'}, 'bar')
17
+ h[:foo].should == 'bar'
18
+ h[:abc].should == 'def'
19
+ end
20
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashie::Extensions::MethodReader do
4
+ class ReaderHash < Hash
5
+ def initialize(hash = {}); self.update(hash) end
6
+ include Hashie::Extensions::MethodReader
7
+ end
8
+ subject{ ReaderHash }
9
+
10
+ it 'should read string keys from the method' do
11
+ subject.new('awesome' => 'sauce').awesome.should == 'sauce'
12
+ end
13
+
14
+ it 'should read symbol keys from the method' do
15
+ subject.new(:awesome => 'sauce').awesome.should == 'sauce'
16
+ end
17
+
18
+ it 'should read nil and false values out properly' do
19
+ h = subject.new(:nil => nil, :false => false)
20
+ h.nil.should == nil
21
+ h.false.should == false
22
+ end
23
+
24
+ it 'should raise a NoMethodError for undefined keys' do
25
+ lambda{ subject.new.awesome }.should raise_error(NoMethodError)
26
+ end
27
+
28
+ describe '#respond_to?' do
29
+ it 'should be true for string keys' do
30
+ subject.new('awesome' => 'sauce').should be_respond_to(:awesome)
31
+ end
32
+
33
+ it 'should be true for symbol keys' do
34
+ subject.new(:awesome => 'sauce').should be_respond_to(:awesome)
35
+ end
36
+
37
+ it 'should be false for non-keys' do
38
+ subject.new.should_not be_respond_to(:awesome)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe Hashie::Extensions::MethodWriter do
44
+ class WriterHash < Hash
45
+ include Hashie::Extensions::MethodWriter
46
+ end
47
+ subject{ WriterHash.new }
48
+
49
+ it 'should write from a method call' do
50
+ subject.awesome = 'sauce'
51
+ subject['awesome'].should == 'sauce'
52
+ end
53
+
54
+ it 'should convert the key using the #convert_key method' do
55
+ subject.stub!(:convert_key).and_return(:awesome)
56
+ subject.awesome = 'sauce'
57
+ subject[:awesome].should == 'sauce'
58
+ end
59
+
60
+ it 'should still NoMethodError on non equals-ending methods' do
61
+ lambda{ subject.awesome }.should raise_error(NoMethodError)
62
+ end
63
+
64
+ it 'should #respond_to? properly' do
65
+ subject.should be_respond_to(:abc=)
66
+ subject.should_not be_respond_to(:abc)
67
+ end
68
+ end
69
+
70
+ describe Hashie::Extensions::MethodQuery do
71
+ class QueryHash < Hash
72
+ def initialize(hash = {}); self.update(hash) end
73
+ include Hashie::Extensions::MethodQuery
74
+ end
75
+ subject{ QueryHash }
76
+
77
+ it 'should be true for non-nil string key values' do
78
+ subject.new('abc' => 123).should be_abc
79
+ end
80
+
81
+ it 'should be true for non-nil symbol key values' do
82
+ subject.new(:abc => 123).should be_abc
83
+ end
84
+
85
+ it 'should be false for nil key values' do
86
+ subject.new(:abc => false).should_not be_abc
87
+ end
88
+
89
+ it 'should raise a NoMethodError for non-set keys' do
90
+ lambda{ subject.new.abc? }.should raise_error(NoMethodError)
91
+ end
92
+
93
+ it 'should respond_to? for existing string keys' do
94
+ subject.new('abc' => 'def').should be_respond_to('abc?')
95
+ end
96
+
97
+ it 'should respond_to? for existing symbol keys' do
98
+ subject.new(:abc => 'def').should be_respond_to(:abc?)
99
+ end
100
+
101
+ it 'should not respond_to? for non-existent keys' do
102
+ subject.new.should_not be_respond_to('abc?')
103
+ end
104
+ end
105
+
106
+ describe Hashie::Extensions::MethodAccess do
107
+ it 'should include all of the other method mixins' do
108
+ klass = Class.new(Hash)
109
+ klass.send :include, Hashie::Extensions::MethodAccess
110
+ (klass.ancestors & [Hashie::Extensions::MethodReader, Hashie::Extensions::MethodWriter, Hashie::Extensions::MethodQuery]).size.should == 3
111
+ end
112
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Hashie::Hash do
3
+ describe Hash do
4
4
  it "should be convertible to a Hashie::Mash" do
5
5
  mash = Hashie::Hash[:some => "hash"].to_mash
6
6
  mash.is_a?(Hashie::Mash).should be_true
@@ -19,14 +19,4 @@ describe Hashie::Hash do
19
19
  hash.should == Hashie::Hash[:a => "hey", 123 => "bob"]
20
20
  stringified_hash.should == Hashie::Hash["a" => "hey", "123" => "bob"]
21
21
  end
22
-
23
- describe '#to_hash' do
24
- it 'should convert it to a hash with string keys by default' do
25
- Hashie::Hash.new.merge(:a => 'hey', :b => 'foo').to_hash.should == {'a' => 'hey', 'b' => 'foo'}
26
- end
27
-
28
- it 'should convert to a hash with symbol keys if :symbolize_keys is passed in' do
29
- Hashie::Hash.new.merge('a' => 'hey', 'b' => 'doo').to_hash(:symbolize_keys => true).should == {:a => 'hey', :b => 'doo'}
30
- end
31
- end
32
- end
22
+ end
@@ -71,6 +71,15 @@ describe Hashie::Mash do
71
71
  @mash.name!.should == "Bob"
72
72
  end
73
73
 
74
+ it "should return a Hashie::Mash when passed an under bang method to a non-existenct key" do
75
+ @mash.abc_.is_a?(Hashie::Mash).should be_true
76
+ end
77
+
78
+ it "should return the existing value when passed an under bang method for an existing key" do
79
+ @mash.name = "Bob"
80
+ @mash.name_.should == "Bob"
81
+ end
82
+
74
83
  it "#initializing_reader should return a Hashie::Mash when passed a non-existent key" do
75
84
  @mash.initializing_reader(:abc).is_a?(Hashie::Mash).should be_true
76
85
  end
@@ -82,9 +91,33 @@ describe Hashie::Mash do
82
91
  @mash.author.website.should == Hashie::Mash.new(:url => "http://www.mbleigh.com/")
83
92
  end
84
93
 
85
- # it "should call super if type is not a key" do
86
- # @mash.type.should == Hashie::Mash
87
- # end
94
+ it "should allow for multi-level under bang testing" do
95
+ @mash.author_.website_.url.should be_nil
96
+ @mash.author_.website_.url?.should == false
97
+ @mash.author.should be_nil
98
+ end
99
+
100
+ it "should not call super if object_id is not a key" do
101
+ @mash.object_id.should == nil
102
+ end
103
+
104
+ it "should return the value if object_id is a key" do
105
+ @mash.object_id = "Steve"
106
+ @mash.object_id.should == "Steve"
107
+ end
108
+
109
+ it "should not call super if id is not a key" do
110
+ @mash.id.should == nil
111
+ end
112
+
113
+ it "should return the value if id is a key" do
114
+ @mash.id = "Steve"
115
+ @mash.id.should == "Steve"
116
+ end
117
+
118
+ it "should not call super if type is not a key" do
119
+ @mash.type.should == nil
120
+ end
88
121
 
89
122
  it "should return the value if type is a key" do
90
123
  @mash.type = "Steve"
@@ -126,6 +159,12 @@ describe Hashie::Mash do
126
159
  duped.details.email.should == "michael@intridea.com"
127
160
  duped.details.address.should == "Nowhere road"
128
161
  end
162
+
163
+ # http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-update
164
+ it "accepts a block" do
165
+ duped = subject.merge(:details => {:address => "Pasadena CA"}) {|key, oldv, newv| [oldv, newv].join(', ')}
166
+ duped.details.address.should == 'Nowhere road, Pasadena CA'
167
+ end
129
168
  end
130
169
 
131
170
  describe "shallow update" do
@@ -204,20 +243,35 @@ describe Hashie::Mash do
204
243
  son.non_existent!.should be_kind_of(SubMash)
205
244
  end
206
245
 
246
+ it "should respect the class when passed an under bang method for a non-existent key" do
247
+ record = Hashie::Mash.new
248
+ record.non_existent_.should be_kind_of(Hashie::Mash)
249
+
250
+ class SubMash < Hashie::Mash
251
+ end
252
+
253
+ son = SubMash.new
254
+ son.non_existent_.should be_kind_of(SubMash)
255
+ end
256
+
207
257
  it "should respect the class when converting the value" do
208
258
  record = Hashie::Mash.new
209
259
  record.details = Hashie::Mash.new({:email => "randy@asf.com"})
210
260
  record.details.should be_kind_of(Hashie::Mash)
261
+ end
262
+
263
+ it "should respect another subclass when converting the value" do
264
+ record = Hashie::Mash.new
211
265
 
212
266
  class SubMash < Hashie::Mash
213
267
  end
214
268
 
215
- son = SubMash.new
216
- son.details = Hashie::Mash.new({:email => "randyjr@asf.com"})
217
- son.details.should be_kind_of(SubMash)
269
+ son = SubMash.new({:email => "foo@bar.com"})
270
+ record.details = son
271
+ record.details.should be_kind_of(SubMash)
218
272
  end
219
273
 
220
- describe '#respond_to?' do
274
+ describe "#respond_to?" do
221
275
  it 'should respond to a normal method' do
222
276
  Hashie::Mash.new.should be_respond_to(:key?)
223
277
  end
@@ -251,11 +305,11 @@ describe Hashie::Mash do
251
305
  initial = Hashie::Mash.new(:name => 'randy', :address => {:state => 'TX'})
252
306
  copy = Hashie::Mash.new(initial)
253
307
  initial.name.should == copy.name
254
- initial.object_id.should_not == copy.object_id
308
+ initial.__id__.should_not == copy.__id__
255
309
  copy.address.state.should == 'TX'
256
310
  copy.address.state = 'MI'
257
311
  initial.address.state.should == 'TX'
258
- copy.address.object_id.should_not == initial.address.object_id
312
+ copy.address.__id__.should_not == initial.address.__id__
259
313
  end
260
314
 
261
315
  it "should accept a default block" do
@@ -266,15 +320,49 @@ describe Hashie::Mash do
266
320
  initial.test?.should be_true
267
321
  end
268
322
 
269
- describe '.subkey_class' do
270
- it 'should be able to define a subkey class different from itself' do
271
- class CustomSubkeyHash < Hashie::Mash
272
- def self.subkey_class; Hashie::Mash end
323
+ it "should convert Hashie::Mashes within Arrays back to Hashes" do
324
+ initial_hash = {"a" => [{"b" => 12, "c" =>["d" => 50, "e" => 51]}, 23]}
325
+ converted = Hashie::Mash.new(initial_hash)
326
+ converted.to_hash["a"].first.is_a?(Hashie::Mash).should be_false
327
+ converted.to_hash["a"].first.is_a?(Hash).should be_true
328
+ converted.to_hash["a"].first["c"].first.is_a?(Hashie::Mash).should be_false
329
+ end
330
+ end
331
+
332
+ describe "#fetch" do
333
+ let(:hash) { {:one => 1} }
334
+ let(:mash) { Hashie::Mash.new(hash) }
335
+
336
+ context "when key exists" do
337
+ it "returns the value" do
338
+ mash.fetch(:one).should eql(1)
339
+ end
340
+
341
+ context "when key has other than original but acceptable type" do
342
+ it "returns the value" do
343
+ mash.fetch('one').should eql(1)
344
+ end
345
+ end
346
+ end
347
+
348
+ context "when key does not exist" do
349
+ it "should raise KeyError" do
350
+ error = RUBY_VERSION =~ /1.8/ ? IndexError : KeyError
351
+ expect { mash.fetch(:two) }.to raise_error(error)
352
+ end
353
+
354
+ context "with default value given" do
355
+ it "returns default value" do
356
+ mash.fetch(:two, 8).should eql(8)
357
+ end
358
+ end
359
+
360
+ context "with block given" do
361
+ it "returns default value" do
362
+ mash.fetch(:two) {|key|
363
+ "block default value"
364
+ }.should eql("block default value")
273
365
  end
274
-
275
- subhash = CustomSubkeyHash.new(:subhash => {:abc => :def})[:subhash]
276
- subhash.should_not be_kind_of(CustomSubkeyHash)
277
- subhash.should be_kind_of(Hashie::Mash)
278
366
  end
279
367
  end
280
368
  end
@@ -63,8 +63,83 @@ describe Hashie::Trash do
63
63
  TrashTest.new(:first_name => 'Michael').first_name.should == 'Michael'
64
64
  end
65
65
 
66
+ context "with both the translated property and the property" do
67
+ it 'sets the desired properties' do
68
+ TrashTest.new(:first_name => 'Michael', :firstName=>'Maeve').first_name.should == 'Michael'
69
+ end
70
+ end
71
+
66
72
  it 'sets the translated properties' do
67
73
  TrashTest.new(:firstName => 'Michael').first_name.should == 'Michael'
68
74
  end
69
75
  end
76
+
77
+ describe 'translating properties using a proc' do
78
+ class TrashLambdaTest < Hashie::Trash
79
+ property :first_name, :from => :firstName, :with => lambda { |value| value.reverse }
80
+ end
81
+
82
+ let(:lambda_trash) { TrashLambdaTest.new }
83
+
84
+ it 'should translate the value given on initialization with the given lambda' do
85
+ TrashLambdaTest.new(:firstName => 'Michael').first_name.should == 'Michael'.reverse
86
+ end
87
+
88
+ it 'should not translate the value if given with the right property' do
89
+ TrashTest.new(:first_name => 'Michael').first_name.should == 'Michael'
90
+ end
91
+
92
+ it 'should translate the value given as property with the given lambda' do
93
+ lambda_trash.firstName = 'Michael'
94
+ lambda_trash.first_name.should == 'Michael'.reverse
95
+ end
96
+
97
+ it 'should not translate the value given as right property' do
98
+ lambda_trash.first_name = 'Michael'
99
+ lambda_trash.first_name.should == 'Michael'
100
+ end
101
+ end
102
+
103
+ describe 'translating properties without from option using a proc' do
104
+
105
+ class TrashLambdaTest2 < Hashie::Trash
106
+ property :first_name, :transform_with => lambda { |value| value.reverse }
107
+ end
108
+
109
+ let(:lambda_trash) { TrashLambdaTest2.new }
110
+
111
+ it 'should translate the value given as property with the given lambda' do
112
+ lambda_trash.first_name = 'Michael'
113
+ lambda_trash.first_name.should == 'Michael'.reverse
114
+ end
115
+
116
+ it 'should transform the value when given in constructor' do
117
+ TrashLambdaTest2.new(:first_name => 'Michael').first_name.should == 'Michael'.reverse
118
+ end
119
+
120
+ context "when :from option is given" do
121
+ class TrashLambdaTest3 < Hashie::Trash
122
+ property :first_name, :from => :firstName, :transform_with => lambda { |value| value.reverse }
123
+ end
124
+
125
+ it 'should not override the :from option in the constructor' do
126
+ TrashLambdaTest3.new(:first_name => 'Michael').first_name.should == 'Michael'
127
+ end
128
+
129
+ it 'should not override the :from option when given as property' do
130
+ t = TrashLambdaTest3.new
131
+ t.first_name = 'Michael'
132
+ t.first_name.should == 'Michael'
133
+ end
134
+
135
+ end
136
+ end
137
+
138
+ it "should raise an error when :from have the same value as property" do
139
+ expect {
140
+ class WrongTrash < Hashie::Trash
141
+ property :first_name, :from => :first_name
142
+ end
143
+ }.to raise_error(ArgumentError)
144
+ end
70
145
  end