motion-map 0.0.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.
@@ -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