map 5.6.1 → 5.8.0

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.
Files changed (5) hide show
  1. data/a.rb +41 -5
  2. data/lib/map.rb +128 -45
  3. data/map.gemspec +1 -1
  4. data/test/map_test.rb +70 -1
  5. metadata +3 -3
data/a.rb CHANGED
@@ -1,20 +1,56 @@
1
1
  #!/usr/bin/env ruby
2
-
3
2
  # -*- encoding : utf-8 -*-
4
3
 
4
+ require './lib/map.rb'
5
+
6
+
5
7
  def assert(&block)
6
8
  block.call
7
9
  end
8
10
 
11
+
12
+ ###
9
13
  m = Map.new
10
- m.set(:a, :b, :c, 42)
11
- p m.get(:a, :b, 0)
14
+ p m.add(
15
+ :comments => [
16
+ { :body => 'a' },
17
+ { :body => 'b' },
18
+ ],
19
+
20
+ [:comments, 0] => {'title' => 'teh title', 'description' => 'description'},
21
+ [:comments, 1] => {'description' => 'description'},
22
+ )
23
+ puts
24
+ puts
25
+ p :m => m
12
26
 
13
27
 
28
+ __END__
29
+ =begin
14
30
 
31
+ {"comments"=>
32
+ [{"body"=>"a", "title"=>"teh title", "description"=>"description"},
33
+ {"body"=>"b", "description"=>"description"}]}
34
+
35
+ =end
36
+
37
+
38
+ ###
15
39
  m = Map.new
16
- m.set(:a, 0, 42)
17
- p m.get(:a, :b, 0)
40
+ p m.add(
41
+ [:a, :b, :c] => 42,
42
+
43
+ [:a, :b] => {:d => 42.0}
44
+ )
45
+ puts
46
+ puts
47
+ p m
48
+
49
+ =begin
18
50
 
51
+ {"a"=>{"b"=>{"c"=>42, "d"=>42.0}}}
19
52
 
53
+ =end
20
54
 
55
+ Map.new.add
56
+ Map.new.add({})
data/lib/map.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  class Map < Hash
3
- Version = '5.6.1' unless defined?(Version)
3
+ Version = '5.8.0' unless defined?(Version)
4
4
  Load = Kernel.method(:load) unless defined?(Load)
5
5
 
6
6
  class << Map
@@ -726,79 +726,162 @@ class Map < Hash
726
726
  end
727
727
 
728
728
  def set(*args)
729
- if args.size == 1 and args.first.is_a?(Hash)
730
- spec = args.shift
731
- else
732
- spec = {}
733
- value = args.pop
734
- keys = args
735
- spec[keys] = value
729
+ case
730
+ when args.empty?
731
+ return []
732
+ when args.size == 1 && args.first.is_a?(Hash)
733
+ hash = args.shift
734
+ else
735
+ hash = {}
736
+ value = args.pop
737
+ key = Array(args).flatten
738
+ hash[key] = value
736
739
  end
737
740
 
738
- begin
739
- spec.each do |keys, value|
740
- keys = Array(keys).flatten
741
- collection = self
742
- key = keys.pop
743
-
744
- while((k = keys.shift)) do
745
- k = alphanumeric_key_for(k)
746
- collection = collection[k]
747
- end
741
+ strategy = hash.map{|key, value| [Array(key), value]}
748
742
 
749
- collection[key] = value
743
+ strategy.each do |key, value|
744
+ leaf_for(key, :autovivify => true) do |leaf, k|
745
+ leaf[k] = value
750
746
  end
751
- rescue
752
- spec.each do |keys, value|
753
- keys = Array(keys).flatten
747
+ end
748
+
749
+ self
750
+ end
751
+
752
+ def add(*args)
753
+ case
754
+ when args.empty?
755
+ return []
756
+ when args.size == 1 && args.first.is_a?(Hash)
757
+ hash = args.shift
758
+ else
759
+ hash = {}
760
+ value = args.pop
761
+ key = Array(args).flatten
762
+ hash[key] = value
763
+ end
764
+
765
+ exploded = Map.explode(hash)
766
+
767
+ exploded[:branches].each do |key, type|
768
+ set(key, type.new) unless get(key).is_a?(type)
769
+ end
770
+
771
+ exploded[:leaves].each do |key, value|
772
+ set(key, value)
773
+ end
774
+
775
+ self
776
+ end
777
+
778
+ def Map.explode(hash)
779
+ accum = {:branches => [], :leaves => []}
780
+
781
+ hash.each do |key, value|
782
+ Map._explode(key, value, accum)
783
+ end
784
+
785
+ branches = accum[:branches]
786
+ leaves = accum[:leaves]
787
+
788
+ sort_by_key_size = proc{|a,b| a.first.size <=> b.first.size}
789
+
790
+ branches.sort!(&sort_by_key_size)
791
+ leaves.sort!(&sort_by_key_size)
792
+
793
+ accum
794
+ end
754
795
 
755
- collection = self
796
+ def Map._explode(key, value, accum = {:branches => [], :leaves => []})
797
+ key = Array(key).flatten
756
798
 
757
- if keys.size <= 1
758
- key = keys.first
759
- collection[key] = value
760
- next
799
+ case value
800
+ when Array
801
+ accum[:branches].push([key, Array])
802
+
803
+ value.each_with_index do |v, k|
804
+ Map._explode(key + [k], v, accum)
761
805
  end
762
806
 
763
- leaf_for(keys, :autovivify => true) do |leaf, key|
764
- leaf[key] = value
807
+ when Hash
808
+ accum[:branches].push([key, Map])
809
+
810
+ value.each do |k, v|
811
+ Map._explode(key + [k], v, accum)
765
812
  end
766
- end
813
+
814
+ else
815
+ accum[:leaves].push([key, value])
767
816
  end
768
817
 
769
- return spec.values
818
+ accum
770
819
  end
771
820
 
772
- def leaf_for(keys, options = {}, &block)
773
- collection = self
774
- key = nil
821
+ def Map.add(*args)
822
+ args.flatten!
823
+ args.compact!
824
+
825
+ Map.for(args.shift).tap do |map|
826
+ args.each{|arg| map.add(arg)}
827
+ end
828
+ end
829
+
830
+ def Map.combine(*args)
831
+ Map.add(*args)
832
+ end
833
+
834
+ def combine!(*args, &block)
835
+ add(*args, &block)
836
+ end
837
+
838
+ def combine(*args, &block)
839
+ dup.tap do |map|
840
+ map.combine!(*args, &block)
841
+ end
842
+ end
843
+
844
+ def leaf_for(key, options = {}, &block)
845
+ leaf = self
846
+ key = Array(key).flatten
847
+ k = alphanumeric_key_for(key.first)
775
848
 
776
- keys.each_cons(2) do |a, b|
849
+ key.each_cons(2) do |a, b|
777
850
  a, b = alphanumeric_key_for(a), alphanumeric_key_for(b)
778
851
 
779
- exists = collection_has_key?(collection, a)
852
+ exists = collection_has_key?(leaf, a)
780
853
 
781
854
  case b
782
855
  when Numeric
783
856
  if options[:autovivify]
784
- collection[a] = [] unless exists
857
+ leaf[a] = [] unless exists
858
+ end
859
+
860
+ case
861
+ when Array, Hash
862
+ nil
863
+ else
864
+ raise(IndexError, "(#{ leaf.inspect })[#{ a.inspect }][#{ b.inspect }]")
785
865
  end
786
- raise(IndexError, "(#{ collection.inspect })[#{ a.inspect }][#{ b.inspect }]") unless collection[a].is_a?(Array)
787
866
 
788
867
  when String, Symbol
789
868
  if options[:autovivify]
790
- collection[a] = Map.new unless exists
869
+ leaf[a] = Map.new unless exists
870
+ end
871
+
872
+ case
873
+ when Hash
874
+ nil
875
+ else
876
+ raise(IndexError, "(#{ leaf.inspect })[#{ a.inspect }][#{ b.inspect }]")
791
877
  end
792
- raise(IndexError, "(#{ collection.inspect })[#{ a.inspect }][#{ b.inspect }]") unless collection[a].is_a?(Map)
793
878
  end
794
879
 
795
- collection = collection[a]
796
- key = b
880
+ leaf = leaf[a]
881
+ k = b
797
882
  end
798
883
 
799
- leaf = collection
800
-
801
- block ? block.call(leaf, key) : [leaf, key]
884
+ block ? block.call(leaf, k) : [leaf, k]
802
885
  end
803
886
 
804
887
  def rm(*args)
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification::new do |spec|
5
5
  spec.name = "map"
6
- spec.version = "5.6.1"
6
+ spec.version = "5.8.0"
7
7
  spec.platform = Gem::Platform::RUBY
8
8
  spec.summary = "map"
9
9
  spec.description = "description: map kicks the ass"
@@ -343,14 +343,26 @@ Testing Map do
343
343
  testing 'that maps support compound key/val setting' do
344
344
  m = Map.new
345
345
  assert{ m.set(:a, :b, :c, 42) }
346
- assert{ m[:a][:b][:c] == 42 }
347
346
  assert{ m.get(:a, :b, :c) == 42 }
347
+
348
+ m = Map.new
349
+ assert{ m.set([:a, :b, :c], 42) }
350
+ assert{ m.get(:a, :b, :c) == 42 }
351
+
352
+ m = Map.new
353
+ assert{ m.set([:a, :b, :c] => 42) }
354
+ assert{ m.get(:a, :b, :c) == 42 }
355
+
356
+ m = Map.new
348
357
  assert{ m.set([:x, :y, :z] => 42.0, [:A, 2] => 'forty-two') }
349
358
  assert{ m[:A].is_a?(Array) }
350
359
  assert{ m[:A].size == 3}
351
360
  assert{ m[:A][2] == 'forty-two' }
352
361
  assert{ m[:x][:y].is_a?(Map) }
353
362
  assert{ m[:x][:y][:z] == 42.0 }
363
+
364
+ assert{ Map.new.tap{|m| m.set} =~ {} }
365
+ assert{ Map.new.tap{|m| m.set({})} =~ {} }
354
366
  end
355
367
 
356
368
  testing 'that Map#get supports providing a default value in a block' do
@@ -393,6 +405,63 @@ Testing Map do
393
405
  assert{ m[:key][0].is_a?(Map) }
394
406
  end
395
407
 
408
+ testing 'that #add overlays the leaves of one hash onto another without nuking branches' do
409
+ m = Map.new
410
+
411
+ assert do
412
+ m.add(
413
+ :comments => [
414
+ { :body => 'a' },
415
+ { :body => 'b' },
416
+ ],
417
+
418
+ [:comments, 0] => {'title' => 'teh title', 'description' => 'description'},
419
+ [:comments, 1] => {'description' => 'description'},
420
+ )
421
+ end
422
+
423
+ assert do
424
+ m =~
425
+ {"comments"=>
426
+ [{"body"=>"a", "title"=>"teh title", "description"=>"description"},
427
+ {"body"=>"b", "description"=>"description"}]}
428
+ end
429
+
430
+ m = Map.new
431
+
432
+ assert do
433
+ m.add(
434
+ [:a, :b, :c] => 42,
435
+
436
+ [:a, :b] => {:d => 42.0}
437
+ )
438
+ end
439
+
440
+ assert do
441
+ m =~
442
+ {"a"=>{"b"=>{"c"=>42, "d"=>42.0}}}
443
+ end
444
+
445
+ assert{ Map.new.tap{|m| m.add} =~ {} }
446
+ assert{ Map.new.tap{|m| m.add({})} =~ {} }
447
+ end
448
+
449
+ testing 'that Map.combine is teh sweet' do
450
+ {
451
+ [{:a => {:b => 42}}, {:a => {:c => 42.0}}] =>
452
+ {"a"=>{"b"=>42, "c"=>42.0}},
453
+
454
+ [{:a => {:b => 42}}, {:a => {:c => 42.0, :d => [1]}}] =>
455
+ {"a"=>{"b"=>42, "d"=>[1], "c"=>42.0}},
456
+
457
+ [{:a => {:b => 42}}, {:a => {:c => 42.0, :d => {0=>1}}}] =>
458
+ {"a"=>{"b"=>42, "d"=>{0=>1}, "c"=>42.0}}
459
+
460
+ }.each do |args, expected|
461
+ assert{ Map.combine(*args) =~ expected }
462
+ end
463
+ end
464
+
396
465
  testing 'that maps support depth_first_each' do
397
466
  m = Map.new
398
467
  prefix = %w[ a b c ]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: map
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.6.1
4
+ version: 5.8.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-18 00:00:00.000000000 Z
12
+ date: 2012-05-22 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! 'description: map kicks the ass'
15
15
  email: ara.t.howard@gmail.com
@@ -49,7 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
49
  version: '0'
50
50
  requirements: []
51
51
  rubyforge_project: codeforpeople
52
- rubygems_version: 1.8.11
52
+ rubygems_version: 1.8.24
53
53
  signing_key:
54
54
  specification_version: 3
55
55
  summary: map