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,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