map 2.7.1 → 2.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.
- data/TODO +3 -0
- data/lib/map.rb +112 -9
- data/map.gemspec +1 -1
- data/test/map_test.rb +59 -0
- metadata +5 -5
    
        data/TODO
    CHANGED
    
    
    
        data/lib/map.rb
    CHANGED
    
    | @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            class Map < Hash
         | 
| 2 | 
            -
              Version = '2. | 
| 2 | 
            +
              Version = '2.8.0' unless defined?(Version)
         | 
| 3 3 | 
             
              Load = Kernel.method(:load) unless defined?(Load)
         | 
| 4 4 |  | 
| 5 5 | 
             
              class << Map
         | 
| @@ -145,8 +145,17 @@ class Map < Hash | |
| 145 145 |  | 
| 146 146 | 
             
                  args
         | 
| 147 147 | 
             
                end
         | 
| 148 | 
            -
             | 
| 149 148 | 
             
                alias_method '[]', 'new'
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                def intersection(a, b)
         | 
| 151 | 
            +
                  a, b, i = Map.for(a), Map.for(b), Map.new
         | 
| 152 | 
            +
                  a.depth_first_each{|key, val| i.set(key, val) if b.has?(key)}
         | 
| 153 | 
            +
                  i
         | 
| 154 | 
            +
                end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                def match(haystack, needle)
         | 
| 157 | 
            +
                  intersection(haystack, needle) == needle
         | 
| 158 | 
            +
                end
         | 
| 150 159 | 
             
              end
         | 
| 151 160 |  | 
| 152 161 | 
             
              unless defined?(Dynamic)
         | 
| @@ -443,22 +452,54 @@ class Map < Hash | |
| 443 452 | 
             
                end
         | 
| 444 453 | 
             
              end
         | 
| 445 454 |  | 
| 446 | 
            -
            #  | 
| 455 | 
            +
            # equality / sorting / matching support 
         | 
| 447 456 | 
             
            #
         | 
| 448 | 
            -
              def ==( | 
| 449 | 
            -
                 | 
| 450 | 
            -
             | 
| 451 | 
            -
             | 
| 457 | 
            +
              def ==(other)
         | 
| 458 | 
            +
                case other
         | 
| 459 | 
            +
                  when Map
         | 
| 460 | 
            +
                    return false if keys != other.keys
         | 
| 461 | 
            +
                    super(other)
         | 
| 462 | 
            +
             | 
| 463 | 
            +
                  when Hash
         | 
| 464 | 
            +
                    self == Map.from_hash(other, self)
         | 
| 465 | 
            +
             | 
| 466 | 
            +
                  else
         | 
| 467 | 
            +
                    false
         | 
| 468 | 
            +
                end
         | 
| 452 469 | 
             
              end
         | 
| 453 470 |  | 
| 454 471 | 
             
              def <=>(other)
         | 
| 455 | 
            -
                keys <=> klass.coerce(other).keys
         | 
| 472 | 
            +
                cmp = keys <=> klass.coerce(other).keys
         | 
| 473 | 
            +
                return cmp unless cmp.zero?
         | 
| 474 | 
            +
                values <=> klass.coerce(other).values
         | 
| 456 475 | 
             
              end
         | 
| 457 476 |  | 
| 458 477 | 
             
              def =~(hash)
         | 
| 459 478 | 
             
                to_hash == klass.coerce(hash).to_hash
         | 
| 460 479 | 
             
              end
         | 
| 461 480 |  | 
| 481 | 
            +
            # reordering support
         | 
| 482 | 
            +
            #
         | 
| 483 | 
            +
              def reorder(order = {})
         | 
| 484 | 
            +
                order = Map.for(order)
         | 
| 485 | 
            +
                map = Map.new
         | 
| 486 | 
            +
                keys = order.depth_first_keys | depth_first_keys
         | 
| 487 | 
            +
                keys.each{|key| map.set(key, get(key))}
         | 
| 488 | 
            +
                map
         | 
| 489 | 
            +
              end
         | 
| 490 | 
            +
             | 
| 491 | 
            +
              def reorder!(order = {})
         | 
| 492 | 
            +
                replace(reorder(order))
         | 
| 493 | 
            +
              end
         | 
| 494 | 
            +
             | 
| 495 | 
            +
            # support for building ordered hasshes from a map's own image
         | 
| 496 | 
            +
            #
         | 
| 497 | 
            +
              def Map.from_hash(hash, order = nil)
         | 
| 498 | 
            +
                map = Map.for(hash)
         | 
| 499 | 
            +
                map.reorder!(order) if order
         | 
| 500 | 
            +
                map
         | 
| 501 | 
            +
              end
         | 
| 502 | 
            +
             | 
| 462 503 | 
             
              def invert
         | 
| 463 504 | 
             
                inverted = klass.allocate
         | 
| 464 505 | 
             
                inverted.default = self.default
         | 
| @@ -697,6 +738,8 @@ class Map < Hash | |
| 697 738 | 
             
                Map.alphanumeric_key_for(key)
         | 
| 698 739 | 
             
              end
         | 
| 699 740 |  | 
| 741 | 
            +
            ## TODO - technically this returns only leaves so the name isn't *quite* right.  re-factor for 3.0
         | 
| 742 | 
            +
            #
         | 
| 700 743 | 
             
              def Map.depth_first_each(enumerable, path = [], accum = [], &block)
         | 
| 701 744 | 
             
                Map.pairs_for(enumerable) do |key, val|
         | 
| 702 745 | 
             
                  path.push(key)
         | 
| @@ -710,10 +753,22 @@ class Map < Hash | |
| 710 753 | 
             
                if block
         | 
| 711 754 | 
             
                  accum.each{|keys, val| block.call(keys, val)}
         | 
| 712 755 | 
             
                else
         | 
| 713 | 
            -
                   | 
| 756 | 
            +
                  accum
         | 
| 714 757 | 
             
                end
         | 
| 715 758 | 
             
              end
         | 
| 716 759 |  | 
| 760 | 
            +
              def Map.depth_first_keys(enumerable, path = [], accum = [], &block)
         | 
| 761 | 
            +
                accum = Map.depth_first_each(enumerable, path = [], accum = [], &block)
         | 
| 762 | 
            +
                accum.map!{|kv| kv.first}
         | 
| 763 | 
            +
                accum
         | 
| 764 | 
            +
              end
         | 
| 765 | 
            +
             | 
| 766 | 
            +
              def Map.depth_first_values(enumerable, path = [], accum = [], &block)
         | 
| 767 | 
            +
                accum = Map.depth_first_each(enumerable, path = [], accum = [], &block)
         | 
| 768 | 
            +
                accum.map!{|kv| kv.last}
         | 
| 769 | 
            +
                accum
         | 
| 770 | 
            +
              end
         | 
| 771 | 
            +
             | 
| 717 772 | 
             
              def Map.pairs_for(enumerable, *args, &block)
         | 
| 718 773 | 
             
                if block.nil?
         | 
| 719 774 | 
             
                  pairs, block = [], lambda{|*pair| pairs.push(pair)}
         | 
| @@ -736,9 +791,57 @@ class Map < Hash | |
| 736 791 | 
             
                pairs ? pairs : result
         | 
| 737 792 | 
             
              end
         | 
| 738 793 |  | 
| 794 | 
            +
              def Map.breadth_first_each(enumerable, accum = [], &block)
         | 
| 795 | 
            +
                levels = []
         | 
| 796 | 
            +
             | 
| 797 | 
            +
                keys = Map.depth_first_keys(enumerable)
         | 
| 798 | 
            +
             | 
| 799 | 
            +
                keys.each do |key|
         | 
| 800 | 
            +
                  key.size.times do |i|
         | 
| 801 | 
            +
                    k = key.slice(0, i + 1)
         | 
| 802 | 
            +
                    level = k.size - 1
         | 
| 803 | 
            +
                    levels[level] ||= Array.new
         | 
| 804 | 
            +
                    last = levels[level].last
         | 
| 805 | 
            +
                    levels[level].push(k) unless last == k
         | 
| 806 | 
            +
                  end
         | 
| 807 | 
            +
                end
         | 
| 808 | 
            +
             | 
| 809 | 
            +
                levels.each do |level|
         | 
| 810 | 
            +
                  level.each do |key|
         | 
| 811 | 
            +
                    val = enumerable.get(key)
         | 
| 812 | 
            +
                    block ? block.call(key, val) : accum.push([key, val])
         | 
| 813 | 
            +
                  end
         | 
| 814 | 
            +
                end
         | 
| 815 | 
            +
             | 
| 816 | 
            +
                block ? enumerable : accum
         | 
| 817 | 
            +
              end
         | 
| 818 | 
            +
             | 
| 819 | 
            +
              def Map.keys_for(enumerable)
         | 
| 820 | 
            +
                keys = enumerable.respond_to?(:keys) ? enumerable.keys : Array.new(enumerable.size){|i| i}
         | 
| 821 | 
            +
              end
         | 
| 822 | 
            +
             | 
| 739 823 | 
             
              def depth_first_each(*args, &block)
         | 
| 740 824 | 
             
                Map.depth_first_each(enumerable=self, *args, &block)
         | 
| 741 825 | 
             
              end
         | 
| 826 | 
            +
             | 
| 827 | 
            +
              def depth_first_keys(*args, &block)
         | 
| 828 | 
            +
                Map.depth_first_keys(enumerable=self, *args, &block)
         | 
| 829 | 
            +
              end
         | 
| 830 | 
            +
             | 
| 831 | 
            +
              def depth_first_values(*args, &block)
         | 
| 832 | 
            +
                Map.depth_first_values(enumerable=self, *args, &block)
         | 
| 833 | 
            +
              end
         | 
| 834 | 
            +
             | 
| 835 | 
            +
              def breadth_first_each(*args, &block)
         | 
| 836 | 
            +
                Map.breadth_first_each(enumerable=self, *args, &block)
         | 
| 837 | 
            +
              end
         | 
| 838 | 
            +
             | 
| 839 | 
            +
              def contains(other)
         | 
| 840 | 
            +
                other = other.is_a?(Hash) ? Map.coerce(other) : other
         | 
| 841 | 
            +
                breadth_first_each{|key, value| return true if value == other}
         | 
| 842 | 
            +
                return false
         | 
| 843 | 
            +
              end
         | 
| 844 | 
            +
              alias_method 'contains?', 'contains'
         | 
| 742 845 | 
             
            end
         | 
| 743 846 |  | 
| 744 847 | 
             
            module Kernel
         | 
    
        data/map.gemspec
    CHANGED
    
    
    
        data/test/map_test.rb
    CHANGED
    
    | @@ -371,6 +371,65 @@ Testing Map do | |
| 371 371 | 
             
                assert{ each_pair = ['a', 'b', 'c', nil] }
         | 
| 372 372 | 
             
              end
         | 
| 373 373 |  | 
| 374 | 
            +
              testing 'that maps support breath_first_each' do
         | 
| 375 | 
            +
                map = Map[
         | 
| 376 | 
            +
                  'hash'         , {'x' => 'y'},
         | 
| 377 | 
            +
                  'nested hash'  , {'nested' => {'a' => 'b'}},
         | 
| 378 | 
            +
                  'array'        , [0, 1, 2],
         | 
| 379 | 
            +
                  'nested array' , [[3], [4], [5]],
         | 
| 380 | 
            +
                  'string'       , '42'
         | 
| 381 | 
            +
                ]
         | 
| 382 | 
            +
             | 
| 383 | 
            +
                accum = []
         | 
| 384 | 
            +
                Map.breadth_first_each(map){|k, v| accum.push([k, v])}
         | 
| 385 | 
            +
                expected =
         | 
| 386 | 
            +
                  [[["hash"], {"x"=>"y"}],
         | 
| 387 | 
            +
                   [["nested hash"], {"nested"=>{"a"=>"b"}}],
         | 
| 388 | 
            +
                   [["array"], [0, 1, 2]],
         | 
| 389 | 
            +
                   [["nested array"], [[3], [4], [5]]],
         | 
| 390 | 
            +
                   [["string"], "42"],
         | 
| 391 | 
            +
                   [["hash", "x"], "y"],
         | 
| 392 | 
            +
                   [["nested hash", "nested"], {"a"=>"b"}],
         | 
| 393 | 
            +
                   [["array", 0], 0],
         | 
| 394 | 
            +
                   [["array", 1], 1],
         | 
| 395 | 
            +
                   [["array", 2], 2],
         | 
| 396 | 
            +
                   [["nested array", 0], [3]],
         | 
| 397 | 
            +
                   [["nested array", 1], [4]],
         | 
| 398 | 
            +
                   [["nested array", 2], [5]],
         | 
| 399 | 
            +
                   [["nested hash", "nested", "a"], "b"],
         | 
| 400 | 
            +
                   [["nested array", 0, 0], 3],
         | 
| 401 | 
            +
                   [["nested array", 1, 0], 4],
         | 
| 402 | 
            +
                   [["nested array", 2, 0], 5]]
         | 
| 403 | 
            +
              end
         | 
| 404 | 
            +
             | 
| 405 | 
            +
              testing 'that maps have a needle-in-a-haystack like #contains? method' do
         | 
| 406 | 
            +
                haystack = Map[
         | 
| 407 | 
            +
                  'hash'         , {'x' => 'y'},
         | 
| 408 | 
            +
                  'nested hash'  , {'nested' => {'a' => 'b'}},
         | 
| 409 | 
            +
                  'array'        , [0, 1, 2],
         | 
| 410 | 
            +
                  'nested array' , [[3], [4], [5]],
         | 
| 411 | 
            +
                  'string'       , '42'
         | 
| 412 | 
            +
                ]
         | 
| 413 | 
            +
             | 
| 414 | 
            +
                needles = [
         | 
| 415 | 
            +
                  {'x' => 'y'},
         | 
| 416 | 
            +
                  {'nested' => {'a' => 'b'}},
         | 
| 417 | 
            +
                  {'a' => 'b'},
         | 
| 418 | 
            +
                  [0,1,2],
         | 
| 419 | 
            +
                  [[3], [4], [5]],
         | 
| 420 | 
            +
                  [3],
         | 
| 421 | 
            +
                  [4],
         | 
| 422 | 
            +
                  [5],
         | 
| 423 | 
            +
                  '42',
         | 
| 424 | 
            +
                  0,1,2,
         | 
| 425 | 
            +
                  3,4,5
         | 
| 426 | 
            +
                ]
         | 
| 427 | 
            +
             | 
| 428 | 
            +
                needles.each do |needle|
         | 
| 429 | 
            +
                  assert{ haystack.contains?(needle) }
         | 
| 430 | 
            +
                end
         | 
| 431 | 
            +
              end
         | 
| 432 | 
            +
             | 
| 374 433 | 
             
              testing 'that #update and #replace accept map-ish objects' do
         | 
| 375 434 | 
             
                o = Object.new
         | 
| 376 435 | 
             
                def o.to_map() {:k => :v} end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: map
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash:  | 
| 4 | 
            +
              hash: 47
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 2
         | 
| 8 | 
            -
              -  | 
| 9 | 
            -
              -  | 
| 10 | 
            -
              version: 2. | 
| 8 | 
            +
              - 8
         | 
| 9 | 
            +
              - 0
         | 
| 10 | 
            +
              version: 2.8.0
         | 
| 11 11 | 
             
            platform: ruby
         | 
| 12 12 | 
             
            authors: 
         | 
| 13 13 | 
             
            - Ara T. Howard
         | 
| @@ -15,7 +15,7 @@ autorequire: | |
| 15 15 | 
             
            bindir: bin
         | 
| 16 16 | 
             
            cert_chain: []
         | 
| 17 17 |  | 
| 18 | 
            -
            date: 2011- | 
| 18 | 
            +
            date: 2011-04-08 00:00:00 -06:00
         | 
| 19 19 | 
             
            default_executable: 
         | 
| 20 20 | 
             
            dependencies: []
         | 
| 21 21 |  |