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,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjcyN2I4NjUxNDc2NWI0Y2VlNGYxYWIwMGNkYzljY2I5MTMxMjZhNw==
5
+ data.tar.gz: !binary |-
6
+ NDJlNTgxODg4ZDRlNzI1YzQxNjRlNmRmYzUwOGNmMDZlODJkZjUyMg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YTEyMmM1MGU2N2NmYzUzNjUxNDJlZjE3ODEyOWMxNTc4NWIyNzMzZWFjNzJi
10
+ NzZiYWE3MzY4YTU4YzVkYjkxYWViODFlNjM0ZGI2Yjk5YjA4YTliN2EwMDFh
11
+ NjEzYjQwMjdjZTIwZmE0NDhhOTM3OWExZGJmOTIzNWY1MzhjNzE=
12
+ data.tar.gz: !binary |-
13
+ NGYzOGZlMTQ1NDdiMmQwOTY0NzRhYTYwM2M1N2NkZTk2ZjFhNzVmZjM1ZjNk
14
+ ZTVhYjNiMDZiMDQzYjk4Y2Q5ODk5MGE0ZTRhNzQzNWU4NTVhNWIzYmZlOGIy
15
+ MjJiMmQwM2NlMDkzZDY2NGFkN2RkNDRkYTZlMWVmMDRmMzg0MmM=
@@ -0,0 +1,2 @@
1
+ build
2
+ .repl_history
@@ -0,0 +1,51 @@
1
+ # MotionMap - maps for RubyMotion!
2
+
3
+ Adding a bit more sizzles to the boring ol' hashes for RubyMotion.
4
+
5
+ ## ATTA BOY!
6
+
7
+ This is most entirely lifted from the awesome Map from Ara T. Howard (https://github.com/ahoward/map).
8
+ Made some adaptations to make it work in the RubyMotion world, but all the heavy lifting is his.
9
+
10
+ Hence all credits and ATTA BOY! should go to Ara for some very nice work!
11
+
12
+ ## Installation
13
+
14
+ ```
15
+ gem install motion-map
16
+ ```
17
+
18
+ ## From the original author
19
+
20
+ the awesome ruby container you've always wanted: a string/symbol indifferent
21
+ ordered hash that works in all rubies
22
+
23
+ maps are bitchin ordered hashes that are both ordered, string/symbol
24
+ indifferent, and have all sorts of sweetness like recursive conversion, more
25
+ robust implementation than HashWithIndifferentAccess, support for struct
26
+ like (map.foo) access, and support for option/keyword access which avoids
27
+ several nasty classes of errors in many ruby libraries
28
+
29
+ ## Docs
30
+
31
+ See docs (https://github.com/ahoward/map)
32
+
33
+ NOTE: Struct and Options functionality was pulled out for this release.
34
+
35
+ ## Contact
36
+
37
+ Fernand Galiana
38
+
39
+ - http://github.com/derailed
40
+ - http://twitter.com/kitesurfer
41
+ - <fernand.galiana@gmail.com>
42
+
43
+
44
+ ## License
45
+
46
+ MotionMap is released under the [MIT](http://opensource.org/licenses/MIT) license.
47
+
48
+
49
+ ## History
50
+ + 0.0.1:
51
+ + Initial drop
@@ -0,0 +1,15 @@
1
+ $:.unshift('/Library/RubyMotion/lib')
2
+ require 'motion/project'
3
+ require './lib/motion-map'
4
+
5
+ require 'bundler'
6
+ Bundler::GemHelper.install_tasks
7
+
8
+ Motion::Project::App.setup do |app|
9
+ app.name = 'MapTest'
10
+
11
+ app.development do
12
+ app.codesign_certificate = ENV['dev_bs_certificate']
13
+ app.provisioning_profile = ENV['dev_bs_profile']
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ class A < UIViewController
2
+ def viewDidLoad
3
+ view = UIScrollView.alloc.init
4
+ view.backgroundColor = UIColor.whiteColor
5
+ m = Map.new(a:10, b:20, c:{d:30} )
6
+ view.contentSize = [320, m.count*200]
7
+
8
+ i = 0
9
+ m.each_pair do |k,v|
10
+ label = UILabel.alloc.initWithFrame( [[10, 10+i*40], [320, 40]] )
11
+ label.text = "#{k}:#{v}"
12
+ self.view.addSubview(label)
13
+ i += 1
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ return true if RUBYMOTION_ENV == 'test'
4
+
5
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
6
+ @window.rootViewController = A.new
7
+ @window.rootViewController.wantsFullScreenLayout = true
8
+ @window.makeKeyAndVisible
9
+ true
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "motion-map must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+ Motion::Project::App.setup do |app|
6
+ Dir.glob(File.join(File.dirname(__FILE__), 'motion-map/*.rb')).each do |file|
7
+ app.files.unshift(file)
8
+ end
9
+ end
@@ -0,0 +1,1028 @@
1
+ class Map < Hash
2
+ class << Map
3
+ def allocate
4
+ super.instance_eval do
5
+ @keys = []
6
+ self
7
+ end
8
+ end
9
+
10
+ def new(*args, &block)
11
+ allocate.instance_eval do
12
+ initialize(*args, &block)
13
+ self
14
+ end
15
+ end
16
+ alias_method '[]', 'new'
17
+
18
+ def for(*args, &block)
19
+ if(args.size == 1 and block.nil?)
20
+ return args.first if args.first.class == self
21
+ end
22
+ new(*args, &block)
23
+ end
24
+
25
+ def coerce(other)
26
+ case other
27
+ when Map
28
+ other
29
+ else
30
+ allocate.update(other.to_hash)
31
+ end
32
+ end
33
+
34
+ # iterate over arguments in pairs smartly.
35
+ #
36
+ def each_pair(*args, &block)
37
+ size = args.size
38
+ parity = size % 2 == 0 ? :even : :odd
39
+ first = args.first
40
+
41
+ if block.nil?
42
+ result = []
43
+ block = lambda{|*kv| result.push(kv)}
44
+ else
45
+ result = args
46
+ end
47
+
48
+ return args if size == 0
49
+
50
+ if size == 1
51
+ if first.respond_to?(:each_pair)
52
+ first.each_pair do |key, val|
53
+ block.call(key, val)
54
+ end
55
+ return args
56
+ end
57
+
58
+ if first.respond_to?(:each_slice)
59
+ first.each_slice(2) do |key, val|
60
+ block.call(key, val)
61
+ end
62
+ return args
63
+ end
64
+ raise(ArgumentError, 'odd number of arguments for Map')
65
+ end
66
+
67
+ array_of_pairs = args.all?{|a| a.is_a?(Array) and a.size == 2}
68
+
69
+ if array_of_pairs
70
+ args.each do |pair|
71
+ key, val, *ignored = pair
72
+ block.call(key, val)
73
+ end
74
+ else
75
+ 0.step(args.size - 1, 2) do |a|
76
+ key = args[a]
77
+ val = args[a + 1]
78
+ block.call(key, val)
79
+ end
80
+ end
81
+
82
+ args
83
+ end
84
+
85
+ def intersection(a, b)
86
+ a, b, i = Map.for(a), Map.for(b), Map.new
87
+ a.depth_first_each{|key, val| i.set(key, val) if b.has?(key)}
88
+ i
89
+ end
90
+
91
+ def match(haystack, needle)
92
+ intersection(haystack, needle) == needle
93
+ end
94
+ end
95
+
96
+ def keys
97
+ @keys ||= []
98
+ end
99
+
100
+ def initialize(*args, &block)
101
+ case args.size
102
+ when 0
103
+ super(&block)
104
+ when 1
105
+ first = args.first
106
+ case first
107
+ when nil, false
108
+ nil
109
+ when Hash
110
+ initialize_from_hash(first)
111
+ when Array
112
+ initialize_from_array(first)
113
+ else
114
+ if first.respond_to?(:to_hash)
115
+ initialize_from_hash(first.to_hash)
116
+ else
117
+ initialize_from_hash(first)
118
+ end
119
+ end
120
+ else
121
+ initialize_from_array(args)
122
+ end
123
+ end
124
+
125
+ def initialize_from_hash(hash)
126
+ map = self
127
+ map.update(hash)
128
+ map.default = hash.default
129
+ end
130
+
131
+ def initialize_from_array(array)
132
+ map = self
133
+ Map.each_pair(array){|key, val| map[key] = val}
134
+ end
135
+
136
+ def klass
137
+ self.class
138
+ end
139
+
140
+ def Map.map_for(hash)
141
+ hash = klass.coerce(hash)
142
+ hash.default = hash.default
143
+ hash
144
+ end
145
+ def map_for(hash)
146
+ klass.map_for(hash)
147
+ end
148
+
149
+ def self.convert_key(key)
150
+ key = key.kind_of?(Symbol) ? key.to_s : key
151
+ end
152
+
153
+ def convert_key(key)
154
+ if klass.respond_to?(:convert_key)
155
+ klass.convert_key(key)
156
+ else
157
+ Map.convert_key(key)
158
+ end
159
+ end
160
+
161
+ def self.convert_value(value)
162
+ case value
163
+ when Hash
164
+ coerce(value)
165
+ when Array
166
+ value.map!{|v| convert_value(v)}
167
+ else
168
+ value
169
+ end
170
+ end
171
+ def convert_value(value)
172
+ if klass.respond_to?(:convert_value)
173
+ klass.convert_value(value)
174
+ else
175
+ Map.convert_value(value)
176
+ end
177
+ end
178
+ alias_method('convert_val', 'convert_value')
179
+
180
+
181
+ def convert(key, val)
182
+ [convert_key(key), convert_value(val)]
183
+ end
184
+
185
+ def copy
186
+ default = self.default
187
+ self.default = nil
188
+ copy = Marshal.load(Marshal.dump(self)) rescue Dup.bind(self).call()
189
+ copy.default = default
190
+ copy
191
+ ensure
192
+ self.default = default
193
+ end
194
+
195
+ Dup = instance_method(:dup) unless defined?(Dup)
196
+
197
+ def dup
198
+ copy
199
+ end
200
+
201
+ def clone
202
+ copy
203
+ end
204
+
205
+ def default(key = nil)
206
+ key.is_a?(Symbol) && include?(key = key.to_s) ? self[key] : super
207
+ end
208
+
209
+ def default=(value)
210
+ raise ArgumentError.new("Map doesn't work so well with a non-nil default value!") unless value.nil?
211
+ end
212
+
213
+ # writer/reader methods
214
+ #
215
+ alias_method '__set__', '[]=' unless method_defined?('__set__')
216
+ alias_method '__get__', '[]' unless method_defined?('__get__')
217
+ alias_method '__update__', 'update' unless method_defined?('__update__')
218
+
219
+ def []=(key, val)
220
+ key, val = convert(key, val)
221
+ keys.push(key) unless has_key?(key)
222
+ __set__(key, val)
223
+ end
224
+ alias_method 'store', '[]='
225
+
226
+ def [](key)
227
+ key = convert_key(key)
228
+ __get__(key)
229
+ end
230
+
231
+ def fetch(key, *args, &block)
232
+ key = convert_key(key)
233
+ super(key, *args, &block)
234
+ end
235
+
236
+ def key?(key)
237
+ super(convert_key(key))
238
+ end
239
+ alias_method 'include?', 'key?'
240
+ alias_method 'has_key?', 'key?'
241
+ alias_method 'member?', 'key?'
242
+
243
+ def update(*args)
244
+ Map.each_pair(*args){|key, val| store(key, val)}
245
+ self
246
+ end
247
+ alias_method 'merge!', 'update'
248
+
249
+ def merge(*args)
250
+ copy.update(*args)
251
+ end
252
+ alias_method '+', 'merge'
253
+
254
+ def reverse_merge(hash)
255
+ map = copy
256
+ map.each{|key, val| Map[key] = val unless Map.key?(key)}
257
+ map
258
+ end
259
+
260
+ def reverse_merge!(hash)
261
+ replace(reverse_merge(hash))
262
+ end
263
+
264
+ def values
265
+ array = []
266
+ keys.each{|key| array.push(self[key])}
267
+ array
268
+ end
269
+ alias_method 'vals', 'values'
270
+
271
+ def values_at(*keys)
272
+ keys.map{|key| self[key]}
273
+ end
274
+
275
+ def first
276
+ [keys.first, self[keys.first]]
277
+ end
278
+
279
+ def last
280
+ [keys.last, self[keys.last]]
281
+ end
282
+
283
+ # iterator methods
284
+ #
285
+ def each_with_index
286
+ keys.each_with_index{|key, index| yield([key, self[key]], index)}
287
+ self
288
+ end
289
+
290
+ def each_key
291
+ keys.each{|key| yield(key)}
292
+ self
293
+ end
294
+
295
+ def each_value
296
+ keys.each{|key| yield self[key]}
297
+ self
298
+ end
299
+
300
+ def each
301
+ keys.each{|key| yield(key, self[key])}
302
+ self
303
+ end
304
+ alias_method 'each_pair', 'each'
305
+
306
+ # mutators
307
+ #
308
+ def delete(key)
309
+ key = convert_key(key)
310
+ keys.delete(key)
311
+ super(key)
312
+ end
313
+
314
+ def clear
315
+ keys.clear
316
+ super
317
+ end
318
+
319
+ def delete_if
320
+ to_delete = []
321
+ keys.each{|key| to_delete.push(key) if yield(key,self[key])}
322
+ to_delete.each{|key| delete(key)}
323
+ self
324
+ end
325
+
326
+ # See: https://github.com/rubinius/rubinius/blob/98c516820d9f78bd63f29dab7d5ec9bc8692064d/kernel/common/hash19.rb#L476-L484
327
+ def keep_if( &block )
328
+ raise RuntimeError.new( "can't modify frozen #{ self.class.name }" ) if frozen?
329
+ return to_enum( :keep_if ) unless block_given?
330
+ each { | key , val | delete key unless yield( key , val ) }
331
+ self
332
+ end
333
+
334
+ def replace(*args)
335
+ clear
336
+ update(*args)
337
+ end
338
+
339
+ # ordered container specific methods
340
+ #
341
+ def shift
342
+ unless empty?
343
+ key = keys.first
344
+ val = delete(key)
345
+ [key, val]
346
+ end
347
+ end
348
+
349
+ def unshift(*args)
350
+ Map.each_pair(*args) do |key, val|
351
+ if key?(key)
352
+ delete(key)
353
+ else
354
+ keys.unshift(key)
355
+ end
356
+ __set__(key, val)
357
+ end
358
+ self
359
+ end
360
+
361
+ def push(*args)
362
+ Map.each_pair(*args) do |key, val|
363
+ if key?(key)
364
+ delete(key)
365
+ else
366
+ keys.push(key)
367
+ end
368
+ __set__(key, val)
369
+ end
370
+ self
371
+ end
372
+
373
+ def pop
374
+ unless empty?
375
+ key = keys.last
376
+ val = delete(key)
377
+ [key, val]
378
+ end
379
+ end
380
+
381
+ # equality / sorting / matching support
382
+ #
383
+ def ==(other)
384
+ case other
385
+ when Map
386
+ return false if keys != other.keys
387
+ super(other)
388
+
389
+ when Hash
390
+ self == Map.from_hash(other, self)
391
+
392
+ else
393
+ false
394
+ end
395
+ end
396
+
397
+ def <=>(other)
398
+ cmp = keys <=> klass.coerce(other).keys
399
+ return cmp unless cmp.zero?
400
+ values <=> klass.coerce(other).values
401
+ end
402
+
403
+ def =~(hash)
404
+ to_hash == klass.coerce(hash).to_hash
405
+ end
406
+
407
+ # reordering support
408
+ #
409
+ def reorder(order = {})
410
+ order = Map.for(order)
411
+ map = Map.new
412
+ keys = order.depth_first_keys | depth_first_keys
413
+ keys.each{|key| map.set(key, get(key))}
414
+ map
415
+ end
416
+
417
+ def reorder!(order = {})
418
+ replace(reorder(order))
419
+ end
420
+
421
+ # support for building ordered hasshes from a Map's own image
422
+ #
423
+ def Map.from_hash(hash, order = nil)
424
+ map = Map.for(hash)
425
+ map.reorder!(order) if order
426
+ map
427
+ end
428
+
429
+ def invert
430
+ inverted = klass.allocate
431
+ inverted.default = self.default
432
+ keys.each{|key| inverted[self[key]] = key }
433
+ inverted
434
+ end
435
+
436
+ def reject(&block)
437
+ dup.delete_if(&block)
438
+ end
439
+
440
+ def reject!(&block)
441
+ hash = reject(&block)
442
+ self == hash ? nil : hash
443
+ end
444
+
445
+ def select
446
+ array = []
447
+ each{|key, val| array << [key,val] if yield(key, val)}
448
+ array
449
+ end
450
+
451
+ def inspect(*args, &block)
452
+ super.inspect
453
+ end
454
+
455
+ def to_hash
456
+ hash = Hash.new(default)
457
+ each do |key, val|
458
+ val = val.to_hash if val.respond_to?(:to_hash)
459
+ hash[key] = val
460
+ end
461
+ hash
462
+ end
463
+
464
+ def to_array
465
+ array = []
466
+ each{|*pair| array.push(pair)}
467
+ array
468
+ end
469
+ alias_method 'to_a', 'to_array'
470
+
471
+ def to_list
472
+ list = []
473
+ each_pair do |key, val|
474
+ list[key.to_i] = val if(key.is_a?(Numeric) or key.to_s =~ %r/^\d+$/)
475
+ end
476
+ list
477
+ end
478
+
479
+ def to_s
480
+ to_array.to_s
481
+ end
482
+
483
+ # a sane method missing that only supports writing values or reading
484
+ # *previously set* values
485
+ #
486
+ def method_missing(*args, &block)
487
+ method = args.first.to_s
488
+ case method
489
+ when /=$/
490
+ key = args.shift.to_s.chomp('=')
491
+ value = args.shift
492
+ self[key] = value
493
+ when /\?$/
494
+ key = args.shift.to_s.chomp('?')
495
+ self.has?( key )
496
+ else
497
+ key = method
498
+ unless has_key?(key)
499
+ return(block ? fetch(key, &block) : super(*args))
500
+ end
501
+ self[key]
502
+ end
503
+ end
504
+
505
+ def respond_to?(method, *args, &block)
506
+ has_key = has_key?(method)
507
+ setter = method.to_s =~ /=\Z/o
508
+ !!((!has_key and setter) or has_key or super)
509
+ end
510
+
511
+ def id
512
+ return self[:id] if has_key?(:id)
513
+ return self[:_id] if has_key?(:_id)
514
+ raise NoMethodError
515
+ end
516
+
517
+ # support for compound key indexing and depth first iteration
518
+ #
519
+ def get(*keys)
520
+ keys = key_for(keys)
521
+
522
+ if keys.size <= 1
523
+ if !self.has_key?(keys.first) && block_given?
524
+ return yield
525
+ else
526
+ return self[keys.first]
527
+ end
528
+ end
529
+
530
+ keys, key = keys[0..-2], keys[-1]
531
+ collection = self
532
+
533
+ keys.each do |k|
534
+ if Map.collection_has?(collection, k)
535
+ collection = Map.collection_key(collection, k)
536
+ else
537
+ collection = nil
538
+ end
539
+
540
+ unless collection.respond_to?('[]')
541
+ leaf = collection
542
+ return leaf
543
+ end
544
+ end
545
+
546
+ if !Map.collection_has?(collection, key) && block_given?
547
+ default_value = yield
548
+ else
549
+ Map.collection_key(collection, key)
550
+ end
551
+ end
552
+
553
+ def has?(*keys)
554
+ keys = key_for(keys)
555
+ collection = self
556
+
557
+ return Map.collection_has?(collection, keys.first) if keys.size <= 1
558
+
559
+ keys, key = keys[0..-2], keys[-1]
560
+
561
+ keys.each do |k|
562
+ if Map.collection_has?(collection, k)
563
+ collection = Map.collection_key(collection, k)
564
+ else
565
+ collection = nil
566
+ end
567
+
568
+ return collection unless collection.respond_to?('[]')
569
+ end
570
+
571
+ return false unless(collection.is_a?(Hash) or collection.is_a?(Array))
572
+
573
+ Map.collection_has?(collection, key)
574
+ end
575
+
576
+ def Map.blank?(value)
577
+ return value.blank? if value.respond_to?(:blank?)
578
+
579
+ case value
580
+ when String
581
+ value.strip.empty?
582
+ when Numeric
583
+ value == 0
584
+ when false
585
+ true
586
+ else
587
+ value.respond_to?(:empty?) ? value.empty? : !value
588
+ end
589
+ end
590
+
591
+ def blank?(*keys)
592
+ return empty? if keys.empty?
593
+ !has?(*keys) or Map.blank?(get(*keys))
594
+ end
595
+
596
+ def Map.collection_key(collection, key, &block)
597
+ case collection
598
+ when Array
599
+ begin
600
+ key = Integer(key)
601
+ rescue
602
+ raise(IndexError, "(#{ collection.inspect })[#{ key.inspect }]")
603
+ end
604
+ collection[key]
605
+
606
+ when Hash
607
+ collection[key]
608
+
609
+ else
610
+ raise(IndexError, "(#{ collection.inspect })[#{ key.inspect }]")
611
+ end
612
+ end
613
+
614
+ def collection_key(*args, &block)
615
+ Map.collection_key(*args, &block)
616
+ end
617
+
618
+ def Map.collection_has?(collection, key, &block)
619
+ has_key =
620
+ case collection
621
+ when Array
622
+ key = (Integer(key) rescue -1)
623
+ (0...collection.size).include?(key)
624
+
625
+ when Hash
626
+ collection.has_key?(key)
627
+
628
+ else
629
+ raise(IndexError, "(#{ collection.inspect })[#{ key.inspect }]")
630
+ end
631
+
632
+ block.call(key) if(has_key and block)
633
+
634
+ has_key
635
+ end
636
+
637
+ def collection_has?(*args, &block)
638
+ Map.collection_has?(*args, &block)
639
+ end
640
+
641
+ def Map.collection_set(collection, key, value, &block)
642
+ set_key = false
643
+
644
+ case collection
645
+ when Array
646
+ begin
647
+ key = Integer(key)
648
+ rescue
649
+ raise(IndexError, "(#{ collection.inspect })[#{ key.inspect }]=#{ value.inspect }")
650
+ end
651
+ set_key = true
652
+ collection[key] = value
653
+
654
+ when Hash
655
+ set_key = true
656
+ collection[key] = value
657
+
658
+ else
659
+ raise(IndexError, "(#{ collection.inspect })[#{ key.inspect }]=#{ value.inspect }")
660
+ end
661
+
662
+ block.call(key) if(set_key and block)
663
+
664
+ [key, value]
665
+ end
666
+
667
+ def collection_set(*args, &block)
668
+ Map.collection_set(*args, &block)
669
+ end
670
+
671
+ def set(*args)
672
+ case
673
+ when args.empty?
674
+ return []
675
+ when args.size == 1 && args.first.is_a?(Hash)
676
+ hash = args.shift
677
+ else
678
+ hash = {}
679
+ value = args.pop
680
+ key = Array(args).flatten
681
+ hash[key] = value
682
+ end
683
+
684
+ strategy = hash.map{|key, value| [Array(key), value]}
685
+
686
+ strategy.each do |key, value|
687
+ leaf_for(key, :autovivify => true) do |leaf, k|
688
+ Map.collection_set(leaf, k, value)
689
+ end
690
+ end
691
+
692
+ self
693
+ end
694
+
695
+ def add(*args)
696
+ case
697
+ when args.empty?
698
+ return []
699
+ when args.size == 1 && args.first.is_a?(Hash)
700
+ hash = args.shift
701
+ else
702
+ hash = {}
703
+ value = args.pop
704
+ key = Array(args).flatten
705
+ hash[key] = value
706
+ end
707
+
708
+ exploded = Map.explode(hash)
709
+
710
+ exploded[:branches].each do |key, type|
711
+ set(key, type.new) unless get(key).is_a?(type)
712
+ end
713
+
714
+ exploded[:leaves].each do |key, value|
715
+ set(key, value)
716
+ end
717
+
718
+ self
719
+ end
720
+
721
+ def Map.explode(hash)
722
+ accum = {:branches => [], :leaves => []}
723
+
724
+ hash.each do |key, value|
725
+ Map._explode(key, value, accum)
726
+ end
727
+
728
+ branches = accum[:branches]
729
+ leaves = accum[:leaves]
730
+
731
+ sort_by_key_size = proc{|a,b| a.first.size <=> b.first.size}
732
+
733
+ branches.sort!(&sort_by_key_size)
734
+ leaves.sort!(&sort_by_key_size)
735
+
736
+ accum
737
+ end
738
+
739
+ def Map._explode(key, value, accum = {:branches => [], :leaves => []})
740
+ key = Array(key).flatten
741
+
742
+ case value
743
+ when Array
744
+ accum[:branches].push([key, Array])
745
+
746
+ value.each_with_index do |v, k|
747
+ Map._explode(key + [k], v, accum)
748
+ end
749
+
750
+ when Hash
751
+ accum[:branches].push([key, Map])
752
+
753
+ value.each do |k, v|
754
+ Map._explode(key + [k], v, accum)
755
+ end
756
+
757
+ else
758
+ accum[:leaves].push([key, value])
759
+ end
760
+
761
+ accum
762
+ end
763
+
764
+ def Map.add(*args)
765
+ args.flatten!
766
+ args.compact!
767
+
768
+ Map.for(args.shift).tap do |map|
769
+ args.each{|arg| map.add(arg)}
770
+ end
771
+ end
772
+
773
+ def Map.combine(*args)
774
+ Map.add(*args)
775
+ end
776
+
777
+ def combine!(*args, &block)
778
+ add(*args, &block)
779
+ end
780
+
781
+ def combine(*args, &block)
782
+ dup.tap do |map|
783
+ map.combine!(*args, &block)
784
+ end
785
+ end
786
+
787
+ def leaf_for(key, options = {}, &block)
788
+ leaf = self
789
+ key = Array(key).flatten
790
+ k = key.first
791
+
792
+ key.each_cons(2) do |a, b|
793
+ exists = Map.collection_has?(leaf, a)
794
+
795
+ case b
796
+ when Numeric
797
+ if options[:autovivify]
798
+ Map.collection_set(leaf, a, Array.new) unless exists
799
+ end
800
+
801
+ when String, Symbol
802
+ if options[:autovivify]
803
+ Map.collection_set(leaf, a, Map.new) unless exists
804
+ end
805
+ end
806
+
807
+ leaf = Map.collection_key(leaf, a)
808
+ k = b
809
+ end
810
+
811
+ block ? block.call(leaf, k) : [leaf, k]
812
+ end
813
+
814
+ def rm(*args)
815
+ paths, path = args.partition{|arg| arg.is_a?(Array)}
816
+ paths.push(path)
817
+
818
+ paths.each do |path|
819
+ if path.size == 1
820
+ delete(*path)
821
+ next
822
+ end
823
+
824
+ branch, leaf = path[0..-2], path[-1]
825
+ collection = get(branch)
826
+
827
+ case collection
828
+ when Hash
829
+ key = leaf
830
+ collection.delete(key)
831
+ when Array
832
+ index = leaf
833
+ collection.delete_at(index)
834
+ else
835
+ raise(IndexError, "(#{ collection.inspect }).rm(#{ path.inspect })")
836
+ end
837
+ end
838
+ paths
839
+ end
840
+
841
+ def forcing(forcing=nil, &block)
842
+ @forcing ||= nil
843
+
844
+ if block
845
+ begin
846
+ previous = @forcing
847
+ @forcing = forcing
848
+ block.call()
849
+ ensure
850
+ @forcing = previous
851
+ end
852
+ else
853
+ @forcing
854
+ end
855
+ end
856
+
857
+ def forcing?(forcing=nil)
858
+ @forcing ||= nil
859
+ @forcing == forcing
860
+ end
861
+
862
+ def apply(other)
863
+ Map.for(other).depth_first_each do |keys, value|
864
+ set(keys => value) unless !get(keys).nil?
865
+ end
866
+ self
867
+ end
868
+
869
+ def Map.alphanumeric_key_for(key)
870
+ return key if key.is_a?(Numeric)
871
+
872
+ digity, stringy, digits = %r/^(~)?(\d+)$/iomx.match(key).to_a
873
+
874
+ digity ? stringy ? String(digits) : Integer(digits) : key
875
+ end
876
+
877
+ def alphanumeric_key_for(key)
878
+ Map.alphanumeric_key_for(key)
879
+ end
880
+
881
+ def self.key_for(*keys)
882
+ return keys.flatten
883
+ end
884
+
885
+ def key_for(*keys)
886
+ self.class.key_for(*keys)
887
+ end
888
+
889
+ ## TODO - technically this returns only leaves so the name isn't *quite* right. re-factor for 3.0
890
+ #
891
+ def Map.depth_first_each(enumerable, path = [], accum = [], &block)
892
+ Map.pairs_for(enumerable) do |key, val|
893
+ path.push(key)
894
+ if((val.is_a?(Hash) or val.is_a?(Array)) and not val.empty?)
895
+ Map.depth_first_each(val, path, accum)
896
+ else
897
+ accum << [path.dup, val]
898
+ end
899
+ path.pop()
900
+ end
901
+ if block
902
+ accum.each{|keys, val| block.call(keys, val)}
903
+ else
904
+ accum
905
+ end
906
+ end
907
+
908
+ def Map.depth_first_keys(enumerable, path = [], accum = [], &block)
909
+ accum = Map.depth_first_each(enumerable, path = [], accum = [], &block)
910
+ accum.map!{|kv| kv.first}
911
+ accum
912
+ end
913
+
914
+ def Map.depth_first_values(enumerable, path = [], accum = [], &block)
915
+ accum = Map.depth_first_each(enumerable, path = [], accum = [], &block)
916
+ accum.map!{|kv| kv.last}
917
+ accum
918
+ end
919
+
920
+ def Map.pairs_for(enumerable, *args, &block)
921
+ if block.nil?
922
+ pairs, block = [], lambda{|*pair| pairs.push(pair)}
923
+ else
924
+ pairs = false
925
+ end
926
+
927
+ result =
928
+ case enumerable
929
+ when Hash
930
+ enumerable.each_pair(*args, &block)
931
+ when Array
932
+ enumerable.each_with_index(*args) do |val, key|
933
+ block.call(key, val)
934
+ end
935
+ else
936
+ enumerable.each_pair(*args, &block)
937
+ end
938
+
939
+ pairs ? pairs : result
940
+ end
941
+
942
+ def Map.breadth_first_each(enumerable, accum = [], &block)
943
+ levels = []
944
+
945
+ keys = Map.depth_first_keys(enumerable)
946
+
947
+ keys.each do |key|
948
+ key.size.times do |i|
949
+ k = key.slice(0, i + 1)
950
+ level = k.size - 1
951
+ levels[level] ||= Array.new
952
+ last = levels[level].last
953
+ levels[level].push(k) unless last == k
954
+ end
955
+ end
956
+
957
+ levels.each do |level|
958
+ level.each do |key|
959
+ val = enumerable.get(key)
960
+ block ? block.call(key, val) : accum.push([key, val])
961
+ end
962
+ end
963
+
964
+ block ? enumerable : accum
965
+ end
966
+
967
+ def Map.keys_for(enumerable)
968
+ keys = enumerable.respond_to?(:keys) ? enumerable.keys : Array.new(enumerable.size){|i| i}
969
+ end
970
+
971
+ def depth_first_each(*args, &block)
972
+ Map.depth_first_each(enumerable=self, *args, &block)
973
+ end
974
+
975
+ def depth_first_keys(*args, &block)
976
+ Map.depth_first_keys(enumerable=self, *args, &block)
977
+ end
978
+
979
+ def depth_first_values(*args, &block)
980
+ Map.depth_first_values(enumerable=self, *args, &block)
981
+ end
982
+
983
+ def breadth_first_each(*args, &block)
984
+ Map.breadth_first_each(enumerable=self, *args, &block)
985
+ end
986
+
987
+ def contains(other)
988
+ other = other.is_a?(Hash) ? Map.coerce(other) : other
989
+ breadth_first_each{|key, value| return true if value == other}
990
+ return false
991
+ end
992
+ alias_method 'contains?', 'contains'
993
+
994
+ ## for rails' extract_options! compat
995
+ #
996
+ def extractable_options?
997
+ true
998
+ end
999
+
1000
+ ## for mongoid type system support
1001
+ #
1002
+ def serialize(object)
1003
+ ::Map.for(object)
1004
+ end
1005
+
1006
+ def deserialize(object)
1007
+ ::Map.for(object)
1008
+ end
1009
+
1010
+ def Map.demongoize(object)
1011
+ Map.for(object)
1012
+ end
1013
+
1014
+ def Map.evolve(object)
1015
+ Map.for(object)
1016
+ end
1017
+
1018
+ def mongoize
1019
+ self
1020
+ end
1021
+ end
1022
+
1023
+ module Kernel
1024
+ private
1025
+ def Map(*args, &block)
1026
+ Map.new(*args, &block)
1027
+ end
1028
+ end