motion-map 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module MotionMap
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/motion-map/version.rb', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = 'motion-map'
6
+ gem.version = MotionMap::VERSION
7
+ gem.authors = ['Fernand Galiana']
8
+ gem.email = ['fernand.galiana@gmail.com']
9
+ gem.summary = %{Port of the most excellent Map gem to RubyMotion}
10
+ gem.description = %{Ported Ara Howard's Map gem to RubyMotion}
11
+ gem.homepage = 'https://github.com/derailed/motion-map'
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.test_files = gem.files.grep(%r{^spec/})
14
+ gem.require_paths = ['lib']
15
+
16
+ gem.add_development_dependency 'rspec'
17
+ end
@@ -0,0 +1,13 @@
1
+ module MapHelper
2
+ def new_int_map(n = 1024)
3
+ Map.new.tap do |m|
4
+ n.times{|i| m[i.to_s] = i}
5
+ end
6
+ end
7
+
8
+ def new_int_hash(n = 1024)
9
+ hash = Hash.new
10
+ n.times{|i| hash[i.to_s] = i}
11
+ hash
12
+ end
13
+ end
@@ -0,0 +1,521 @@
1
+ describe Map do
2
+ extend MapHelper
3
+
4
+ describe 'constructor' do
5
+ it 'supports bare constructor' do
6
+ map = Map.new
7
+ map .count.should == 0
8
+ end
9
+
10
+ it 'can take a hash' do
11
+ map = Map.new( {} )
12
+ map.count.should == 0
13
+ end
14
+
15
+ it 'can take an empty array' do
16
+ array = []
17
+ map = Map.new( array )
18
+ map.count.should == 0
19
+ map = Map.new( *array )
20
+ map.count.should == 0
21
+ end
22
+
23
+ it 'deals with nil correctly' do
24
+ map = Map.new( nil )
25
+ map.count.should == 0
26
+ map = Map.new( false )
27
+ map.count.should == 0
28
+ end
29
+
30
+ it 'accepts an even sized array' do
31
+ arrays = [
32
+ [ %w( k v ), %w( key val ) ],
33
+ [ %w( k v ), %w( key val ), %w( a b ) ],
34
+ [ %w( k v ), %w( key val ), %w( a b ), %w( x y ) ]
35
+ ]
36
+ expectations = [1, 2, 2]
37
+ i = 0
38
+ arrays.each do |array|
39
+ map = Map.new(array)
40
+ map.count.should == expectations[i]
41
+ map = Map.new(*array)
42
+ map.count.should == expectations[i]
43
+ i += 1
44
+ end
45
+ end
46
+
47
+ it 'can deal with [] as new' do
48
+ list = [
49
+ [],
50
+ [{}],
51
+ [[:key, :val]],
52
+ [:key, :val]
53
+ ]
54
+ list.each do |args|
55
+ map = Map[*args]
56
+ map.class.should == Map
57
+ Map.new(*args).should == map
58
+ end
59
+ end
60
+ end
61
+
62
+ describe 'iterators' do
63
+ it 'yields pairs in order' do
64
+ map = new_int_map
65
+ i = 0
66
+ map.each do |key, val|
67
+ key.should == i.to_s
68
+ val.should == i
69
+ i += 1
70
+ end
71
+ end
72
+
73
+ it 'preserves ordering' do
74
+ map = new_int_map(n=2048)
75
+ values = Array.new(n) {|i| i}
76
+ keys = values.map{|value| value.to_s}
77
+ map.keys.size.should == n
78
+ map.keys.should == keys
79
+ map.values.should == values
80
+ end
81
+ end
82
+
83
+ describe 'accessors' do
84
+ it 'Maps string/symbol indifferent for simple look-ups' do
85
+ map = Map.new
86
+ map[:k] = :v
87
+ map['a'] = 'b'
88
+ map[:k].should == :v
89
+ map[:k.to_s].should == :v
90
+ map[:a].should == 'b'
91
+ map[:a.to_s].should == 'b'
92
+ end
93
+
94
+ it 'Maps string/symbol indifferent for recursive look-ups' do
95
+ map = Map(:a => {:b => {:c => 42}})
96
+
97
+ map[:a].should == {:b => {:c => 42}}
98
+ map[:a][:b][:c].should == 42
99
+ map['a'][:b][:c].should == 42
100
+ map['a']['b'][:c].should == 42
101
+ map['a']['b']['c'].should == 42
102
+ map[:a]['b'][:c].should == 42
103
+ map[:a]['b']['c'].should == 42
104
+ map[:a][:b]['c'].should == 42
105
+ map['a'][:b]['c'].should == 42
106
+
107
+ map = Map[:a => [{:b => 42}]]
108
+ map['a'].class.should == Array
109
+ map['a'][0].class.should == Map
110
+ map['a'][0]['b'].should == 42
111
+
112
+ map = Map(:a => [ {:b => 42}, [{:c => 'forty-two'}] ])
113
+ map['a'].class.should == Array
114
+ map['a'][0].class.should == Map
115
+ map['a'][1].class.should == Array
116
+ map['a'][0]['b'].should == 42
117
+ map['a'][1][0]['c'].should == 'forty-two'
118
+ end
119
+ end
120
+
121
+ describe 'shift' do
122
+ it 'supports shift like a good ordered container' do
123
+ map = Map.new
124
+ 10.times do |i|
125
+ key, val = i.to_s, i
126
+ map.unshift(key, val)
127
+ map[key].should == val
128
+ map.keys.first.to_s.should == key.to_s
129
+ map.values.first.to_s.should == val.to_s
130
+ end
131
+
132
+ map = Map.new
133
+ args = []
134
+ 10.times do |i|
135
+ key, val = i.to_s, i
136
+ args.unshift([key, val])
137
+ end
138
+ map.unshift(*args)
139
+ 10.times do |i|
140
+ key, val = i.to_s, i
141
+ map[key].should == val
142
+ map.keys[i].to_s.should == key.to_s
143
+ map.values[i].to_s.should == val.to_s
144
+ end
145
+ end
146
+ end
147
+
148
+ describe 'comparator' do
149
+ it 'supports match operator, which can make testing hash equality simpler!' do
150
+ map = new_int_map
151
+ hash = new_int_hash
152
+ map.should =~ hash
153
+ end
154
+
155
+ it 'supports inheritence without cycles' do
156
+ c = Class.new(Map){}
157
+ o = c.new
158
+ Map.should === o
159
+ end
160
+
161
+ it 'captures equality correctly' do
162
+ a = Map.new
163
+ b = Map.new
164
+ a.should == b
165
+ a.should != 42
166
+ b[:k] = :v
167
+ a.should != b
168
+ end
169
+ end
170
+
171
+ describe 'keys as methods' do
172
+ it 'works with simple usage' do
173
+ a = Map.new( k: :v)
174
+ a.k.should == :v
175
+ end
176
+
177
+ it 'works with complexusage' do
178
+ a = Map.new( k: {a: {b: 10}} )
179
+ a.k.a.b.should == 10
180
+ end
181
+ end
182
+
183
+ describe 'subclassing' do
184
+ it 'insures subclassing and clobbering initialize does not kill nested coersion' do
185
+ c = Class.new(Map){ def initialize(arg) end }
186
+ o = c.new(42)
187
+ o.class.should == c
188
+ o.update(:k => {:a => :b})
189
+ o.size.should == 1
190
+ end
191
+
192
+ it 'ensures that subclassing does not kill class level coersion' do
193
+ c = Class.new(Map){ }
194
+ o = c.for(Map.new)
195
+ o.class.should == c
196
+
197
+ d = Class.new(c)
198
+ o = d.for(Map.new)
199
+ o.class.should == d
200
+ end
201
+ end
202
+
203
+ describe '#to_list' do
204
+ it 'insures Maps can be converted to lists with numeric indexes' do
205
+ m = Map[0, :a, 1, :b, 2, :c]
206
+ m.to_list.should == [:a, :b, :c]
207
+ end
208
+ end
209
+
210
+ describe 'method_missing' do
211
+ it 'ensures method_missing hacks allow setting values, but not getting them until they are set' do
212
+ m = Map.new
213
+ (m.missing rescue $!).class.should == NoMethodError
214
+ m.missing = :val
215
+ m[:missing].should == :val
216
+ m.missing.should == :val
217
+ end
218
+
219
+ it 'ensures method_missing hacks have sane respond_to? semantics' do
220
+ m = Map.new
221
+ m.respond_to?(:missing).should == false
222
+ m.respond_to?(:missing=).should == true
223
+ m.missing = :val
224
+ m.respond_to?(:missing).should == true
225
+ m.respond_to?(:missing=).should == true
226
+ end
227
+
228
+ it 'ensures method missing with a block delegatets to fetch' do
229
+ m = Map.new
230
+ m.missing{ :val }.should == :val
231
+ m.has_key?(:key).should == false
232
+ end
233
+ end
234
+
235
+ describe 'compound keys' do
236
+ it 'Maps support compound key/val setting' do
237
+ m = Map.new
238
+ m.set(:a, :b, :c, 42)
239
+ m.get(:a, :b, :c).should == 42
240
+
241
+ m = Map.new
242
+ m.set([:a, :b, :c], 42)
243
+ m.get(:a, :b, :c) == 42
244
+
245
+ m = Map.new
246
+ m.set([:a, :b, :c] => 42)
247
+ m.get(:a, :b, :c) == 42
248
+
249
+ m = Map.new
250
+ m.set([:x, :y, :z] => 42.0, [:A, 2] => 'forty-two')
251
+ m[:A].class.should == Array
252
+ m[:A].size.should == 3
253
+ m[:A][2].should == 'forty-two'
254
+ m[:x][:y].class.should == Map
255
+ m[:x][:y][:z].should == 42.0
256
+
257
+ Map.new.tap{|m| m.set}.should =~ {}
258
+ Map.new.tap{|m| m.set({})} =~ {}
259
+ end
260
+ end
261
+
262
+ describe '#get' do
263
+ it 'supports providing a default value in a block' do
264
+ m = Map.new
265
+ m.set(:a, :b, :c, 42)
266
+ m.set(:z, 1)
267
+
268
+ m.get(:x){1}.should == 1
269
+ m.get(:z){2}.should == 1
270
+ m.get(:a, :b, :d){1}.should == 1
271
+ m.get(:a, :b, :c){1}.should == 42
272
+ m.get(:a, :b){1}.should == Map({:c => 42})
273
+ m.get(:a, :aa){1}.should == 1
274
+ end
275
+
276
+ it 'ensures setting a sub-container does not eff up the container values' do
277
+ m = Map.new
278
+ m.set(:array => [0,1,2])
279
+ m.get(:array, 0).should == 0
280
+ m.get(:array, 1).should == 1
281
+ m.get(:array, 2).should == 2
282
+
283
+ m.set(:array, 2, 42)
284
+ m.get(:array, 0).should == 0
285
+ m.get(:array, 1).should == 1
286
+ m.get(:array, 2).should == 42
287
+ end
288
+ end
289
+
290
+ describe 'merging' do
291
+ it 'ensures #apply selectively merges non-nil values' do
292
+ m = Map.new(:array => [0, 1], :hash => {:a => false, :b => nil, :c => 42})
293
+ defaults = Map.new(:array => [nil, nil, 2], :hash => {:b => true})
294
+
295
+ m.apply(defaults)
296
+ m[:array].should == [0,1,2]
297
+ m[:hash].should =~ {:a => false, :b => true, :c => 42}
298
+
299
+ m = Map.new
300
+ m.apply :key => [{:key => :val}]
301
+ m[:key].class.should == Array
302
+ m[:key][0].class.should == Map
303
+ end
304
+
305
+ it 'ensures #add overlays the leaves of one hash onto another without nuking branches' do
306
+ m = Map.new
307
+
308
+ m.add(
309
+ :comments => [
310
+ { :body => 'a' },
311
+ { :body => 'b' },
312
+ ],
313
+
314
+ [:comments, 0] => {'title' => 'teh title', 'description' => 'description'},
315
+ [:comments, 1] => {'description' => 'description'},
316
+ )
317
+ m.should =~
318
+ {"comments"=>
319
+ [{"body"=>"a", "title"=>"teh title", "description"=>"description"},
320
+ {"body"=>"b", "description"=>"description"}]}
321
+
322
+ m = Map.new
323
+ m.add(
324
+ [:a, :b, :c] => 42,
325
+ [:a, :b] => {:d => 42.0}
326
+ )
327
+ m.should =~ {"a"=>{"b"=>{"c"=>42, "d"=>42.0}}}
328
+
329
+ Map.new.tap{|m| m.add}.should =~ {}
330
+ Map.new.tap{|m| m.add({})}.should =~ {}
331
+ end
332
+
333
+ it 'ensures that Map.combine is teh sweet' do
334
+ {
335
+ [{:a => {:b => 42}}, {:a => {:c => 42.0}}] => {"a"=>{"b"=>42, "c"=>42.0}},
336
+ [{:a => {:b => 42}}, {:a => {:c => 42.0, :d => [1]}}] => {"a"=>{"b"=>42, "d"=>[1], "c"=>42.0}},
337
+ [{:a => {:b => 42}}, {:a => {:c => 42.0, :d => {0=>1}}}] => {"a"=>{"b"=>42, "d"=>{0=>1}, "c"=>42.0}}
338
+ }.each do |args, expected|
339
+ Map.combine(*args).should =~ expected
340
+ end
341
+ end
342
+ end
343
+
344
+ describe 'traversal' do
345
+ it 'supports depth_first_each' do
346
+ m = Map.new
347
+ prefix = %w[ a b c ]
348
+ keys = []
349
+ n = 0.42
350
+
351
+ 10.times do |i|
352
+ key = prefix + [i]
353
+ val = n
354
+ keys.push(key)
355
+ m.set(key => val)
356
+ n *= 10
357
+ end
358
+ m.get(:a).class.should == Map
359
+ m.get(:a, :b).class.should == Map
360
+ m.get(:a, :b, :c).class.should == Array
361
+
362
+ n = 0.42
363
+ m.depth_first_each do |key, val|
364
+ key.should == keys.shift
365
+ val.should == n
366
+ n *= 10
367
+ end
368
+ end
369
+
370
+ it 'ensures #each_pair works on arrays' do
371
+ each = []
372
+ array = %w( a b c )
373
+ Map.each_pair(array){|k,v| each.push(k,v)}
374
+ each.should == ['a', 'b', 'c', nil]
375
+ end
376
+
377
+ it 'supports breath_first_each' do
378
+ m = Map[
379
+ 'hash' , {'x' => 'y'},
380
+ 'nested hash' , {'nested' => {'a' => 'b'}},
381
+ 'array' , [0, 1, 2],
382
+ 'nested array' , [[3], [4], [5]],
383
+ 'string' , '42'
384
+ ]
385
+
386
+ accum = []
387
+ m.breadth_first_each(Map){|k, v| accum.push([k, v])}
388
+ expected =
389
+ [
390
+ [["hash"], {"x"=>"y"}],
391
+ [["nested hash"], {"nested"=>{"a"=>"b"}}],
392
+ [["array"], [0, 1, 2]],
393
+ [["nested array"], [[3], [4], [5]]],
394
+ [["string"], "42"],
395
+
396
+ [["hash", "x"], "y"],
397
+ [["nested hash", "nested"], {"a"=>"b"}],
398
+ [["array", 0], 0],
399
+ [["array", 1], 1],
400
+ [["array", 2], 2],
401
+ [["nested array", 0], [3]],
402
+ [["nested array", 1], [4]],
403
+ [["nested array", 2], [5]],
404
+
405
+ [["nested hash", "nested", "a"], "b"],
406
+ [["nested array", 0, 0], 3],
407
+ [["nested array", 1, 0], 4],
408
+ [["nested array", 2, 0], 5]
409
+ ]
410
+ accum.should == expected
411
+ end
412
+ end
413
+
414
+ describe '#contains' do
415
+ it 'handles needle-in-a-haystack like #contains? method' do
416
+ haystack = Map[
417
+ 'hash' , {'x' => 'y'},
418
+ 'nested hash' , {'nested' => {'a' => 'b'}},
419
+ 'array' , [0, 1, 2],
420
+ 'nested array' , [[3], [4], [5]],
421
+ 'string' , '42'
422
+ ]
423
+
424
+ needles = [
425
+ {'x' => 'y'},
426
+ {'nested' => {'a' => 'b'}},
427
+ {'a' => 'b'},
428
+ [0,1,2],
429
+ [[3], [4], [5]],
430
+ [3],
431
+ [4],
432
+ [5],
433
+ '42',
434
+ 0,1,2,
435
+ 3,4,5
436
+ ]
437
+
438
+ needles.each do |needle|
439
+ haystack.contains?(needle).should == true
440
+ end
441
+ end
442
+ end
443
+
444
+ it 'ensures #blank? method that is sane' do
445
+ m = Map.new(:a => 0, :b => ' ', :c => '', :d => {}, :e => [], :f => false)
446
+ m.each do |key, val|
447
+ m.blank?(key).should == true
448
+ end
449
+
450
+ m = Map.new(:a => 1, :b => '_', :d => {:k=>:v}, :e => [42], :f => true)
451
+ m.each do |key, val|
452
+ m.blank?(key).should == false
453
+ end
454
+ Map.new.blank?.should == true
455
+ end
456
+
457
+ it 'ensures self referential Maps do not make #inspect puke' do
458
+ a = Map.new
459
+ b = Map.new
460
+
461
+ b[:a] = a
462
+ a[:b] = b
463
+
464
+ begin
465
+ a.inspect
466
+ b.inspect
467
+ true.should == true
468
+ rescue => boom
469
+ fail boom
470
+ end
471
+ end
472
+
473
+ it 'has a clever little rm operator' do
474
+ m = Map.new
475
+ m.set :a, :b, 42
476
+ m.set :x, :y, 42
477
+ m.set :x, :z, 42
478
+ m.set :array, [0,1,2]
479
+
480
+ m.rm(:x, :y)
481
+ m.get(:x).should =~ {:z => 42}
482
+
483
+ m.rm(:a, :b)
484
+ m.get(:a).should =~ {}
485
+
486
+ m.rm(:array, 0)
487
+ m.get(:array).should == [1,2]
488
+ m.rm(:array, 1)
489
+ m.get(:array).should == [1]
490
+ m.rm(:array, 0)
491
+ m.get(:array).should == []
492
+
493
+ m.rm(:array)
494
+ m.get(:array).should == nil
495
+
496
+ m.rm(:a)
497
+ m.get(:a).should == nil
498
+
499
+ m.rm(:x)
500
+ m.get(:x).should == nil
501
+ end
502
+
503
+ it 'Maps a clever little question method' do
504
+ m = Map.new
505
+ m.set(:a, :b, :c, 42)
506
+ m.set([:x, :y, :z] => 42.0, [:A, 2] => 'forty-two')
507
+
508
+ m.b?.should == false
509
+ m.a?.should == true
510
+ m.a.b?.should == true
511
+ m.a.b.c?.should == true
512
+ m.a.b.d?.should == false
513
+
514
+ m.x?.should == true
515
+ m.x.y?.should == true
516
+ m.x.y.z?.should == true
517
+ m.y?.should == false
518
+
519
+ m.A?.should == true
520
+ end
521
+ end