hashie 1.2.0 → 2.0.0

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