hashery 1.4.0 → 1.5.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/.ruby +57 -92
- data/.yardopts +8 -0
- data/COPYING.rdoc +45 -0
- data/HISTORY.rdoc +18 -0
- data/QED.rdoc +1 -0
- data/README.rdoc +42 -16
- data/lib/hashery.rb +16 -9
- data/lib/hashery.yml +57 -92
- data/lib/hashery/association.rb +3 -1
- data/lib/hashery/basic_object.rb +74 -0
- data/lib/hashery/basic_struct.rb +288 -1
- data/lib/hashery/basicobject.rb +1 -74
- data/lib/hashery/basicstruct.rb +1 -280
- data/lib/hashery/casting_hash.rb +171 -1
- data/lib/hashery/castinghash.rb +1 -171
- data/lib/hashery/core_ext.rb +82 -0
- data/lib/hashery/dictionary.rb +3 -0
- data/lib/hashery/fuzzy_hash.rb +154 -1
- data/lib/hashery/fuzzyhash.rb +1 -154
- data/lib/hashery/ini.rb +3 -2
- data/lib/hashery/key_hash.rb +186 -0
- data/lib/hashery/keyhash.rb +1 -0
- data/lib/hashery/linked_list.rb +195 -1
- data/lib/hashery/linkedlist.rb +1 -195
- data/lib/hashery/lru_hash.rb +273 -1
- data/lib/hashery/lruhash.rb +1 -273
- data/lib/hashery/open_cascade.rb +99 -1
- data/lib/hashery/open_hash.rb +77 -1
- data/lib/hashery/opencascade.rb +1 -99
- data/lib/hashery/openhash.rb +1 -77
- data/lib/hashery/ordered_hash.rb +168 -1
- data/lib/hashery/orderedhash.rb +1 -167
- data/lib/hashery/property_hash.rb +97 -1
- data/lib/hashery/propertyhash.rb +1 -97
- data/lib/hashery/query_hash.rb +35 -1
- data/lib/hashery/queryhash.rb +1 -35
- data/lib/hashery/stash.rb +3 -174
- data/lib/hashery/static_hash.rb +48 -1
- data/lib/hashery/statichash.rb +1 -48
- data/qed/06_opencascade.rdoc +12 -12
- data/test/case_association.rb +29 -15
- data/test/case_basicstruct.rb +192 -0
- data/test/case_dictionary.rb +149 -109
- data/test/case_keyhash.rb +175 -0
- data/test/case_opencascade.rb +89 -43
- data/test/case_openhash.rb +15 -11
- metadata +85 -78
- data/LICENSE +0 -206
- data/NOTICE +0 -11
- data/lib/hashery/sparse_array.rb +0 -1
- data/lib/hashery/sparsearray.rb +0 -577
- data/test/case_openobject.rb +0 -130
- data/test/case_sparsearray.rb +0 -316
- data/test/case_stash.rb +0 -131
    
        data/lib/hashery/open_cascade.rb
    CHANGED
    
    | @@ -1 +1,99 @@ | |
| 1 | 
            -
            require 'hashery/ | 
| 1 | 
            +
            require 'hashery/open_hash'
         | 
| 2 | 
            +
            #require 'facets/nullclass'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            # = OpenCascade
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            # OpenCascade is subclass of OpenHash. It differs in a few
         | 
| 7 | 
            +
            # significant ways.
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            # The main reason this class is labeled "cascade", every internal
         | 
| 10 | 
            +
            # Hash is transformed into an OpenCascade dynamically upon access.
         | 
| 11 | 
            +
            # This makes it easy to create "cascading" references.
         | 
| 12 | 
            +
            #
         | 
| 13 | 
            +
            #   h = { :x => { :y => { :z => 1 } } }
         | 
| 14 | 
            +
            #   c = OpenCascade[h]
         | 
| 15 | 
            +
            #   c.x.y.z  #=> 1
         | 
| 16 | 
            +
            #
         | 
| 17 | 
            +
            # As soon as you access a node it automatically becomes an OpenCascade.
         | 
| 18 | 
            +
            #
         | 
| 19 | 
            +
            #   c = OpenCascade.new   #=> #<OpenCascade:0x7fac3680ccf0 {}>
         | 
| 20 | 
            +
            #   c.r                   #=> #<OpenCascade:0x7fac368084c0 {}>
         | 
| 21 | 
            +
            #   c.a.b                 #=> #<OpenCascade:0x7fac3680a4f0 {}>
         | 
| 22 | 
            +
            #
         | 
| 23 | 
            +
            # But if you set a node, then that will be it's value.
         | 
| 24 | 
            +
            #
         | 
| 25 | 
            +
            #   c.a.b = 4             #=> 4
         | 
| 26 | 
            +
            #
         | 
| 27 | 
            +
            # To query a node without causing the auto-creation of an OpenCasade
         | 
| 28 | 
            +
            # object, use the ?-mark.
         | 
| 29 | 
            +
            #
         | 
| 30 | 
            +
            #   c.a.z?                #=> nil
         | 
| 31 | 
            +
            #
         | 
| 32 | 
            +
            # OpenCascade also transforms Hashes within Arrays.
         | 
| 33 | 
            +
            #
         | 
| 34 | 
            +
            #  h = { :x=>[ {:a=>1}, {:a=>2} ], :y=>1 }
         | 
| 35 | 
            +
            #  c = OpenCascade[h]
         | 
| 36 | 
            +
            #  c.x.first.a.assert == 1
         | 
| 37 | 
            +
            #  c.x.last.a.assert == 2
         | 
| 38 | 
            +
            #
         | 
| 39 | 
            +
            # Finally, you can set a node and get the reciever back using
         | 
| 40 | 
            +
            # the !-mark.
         | 
| 41 | 
            +
            #
         | 
| 42 | 
            +
            #   c = OpenCascade.new   #=> #<OpenCascade:0x7fac3680ccf0 {}>
         | 
| 43 | 
            +
            #   c.x!(4).y!(3)         #=> #<OpenCascade:0x7fac3680ccf0 {:x=>4, :y=>3}>
         | 
| 44 | 
            +
            #
         | 
| 45 | 
            +
            #--
         | 
| 46 | 
            +
            # Last, when an entry is not found, 'null' is returned rather then 'nil'.
         | 
| 47 | 
            +
            # This allows for run-on entries withuot error. Eg.
         | 
| 48 | 
            +
            #
         | 
| 49 | 
            +
            #   o = OpenCascade.new
         | 
| 50 | 
            +
            #   o.a.b.c  #=> null
         | 
| 51 | 
            +
            #
         | 
| 52 | 
            +
            # Unfortuately this requires an explict test for null? in 'if' conditions.
         | 
| 53 | 
            +
            #
         | 
| 54 | 
            +
            #   if o.a.b.c.null?  # true if null
         | 
| 55 | 
            +
            #   if o.a.b.c.nil?   # true if nil or null
         | 
| 56 | 
            +
            #   if o.a.b.c.not?   # true if nil or null or false
         | 
| 57 | 
            +
            #
         | 
| 58 | 
            +
            # So be sure to take that into account.
         | 
| 59 | 
            +
            #++
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            class OpenCascade < OpenHash
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              #
         | 
| 64 | 
            +
              def method_missing(sym, *args, &blk)
         | 
| 65 | 
            +
                type = sym.to_s[-1,1]
         | 
| 66 | 
            +
                name = sym.to_s.gsub(/[=!?]$/, '').to_sym
         | 
| 67 | 
            +
                case type
         | 
| 68 | 
            +
                when '='
         | 
| 69 | 
            +
                  self[name] = args.first
         | 
| 70 | 
            +
                when '!'
         | 
| 71 | 
            +
                  #@hash.__send__(name, *args, &blk)
         | 
| 72 | 
            +
                  __send__(name, *args, &blk)
         | 
| 73 | 
            +
                when '?'
         | 
| 74 | 
            +
                  self[name]
         | 
| 75 | 
            +
                else
         | 
| 76 | 
            +
                  if key?(name)
         | 
| 77 | 
            +
                    self[name] = transform_entry(self[name])
         | 
| 78 | 
            +
                  else
         | 
| 79 | 
            +
                    self[name] = OpenCascade.new #self.class.new
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              private
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                #
         | 
| 87 | 
            +
                def transform_entry(entry)
         | 
| 88 | 
            +
                  case entry
         | 
| 89 | 
            +
                  when Hash
         | 
| 90 | 
            +
                    OpenCascade.new(entry) #self.class.new(val)
         | 
| 91 | 
            +
                  when Array
         | 
| 92 | 
            +
                    entry.map{ |e| transform_entry(e) }
         | 
| 93 | 
            +
                  else
         | 
| 94 | 
            +
                    entry
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
            end
         | 
| 99 | 
            +
             | 
    
        data/lib/hashery/open_hash.rb
    CHANGED
    
    | @@ -1 +1,77 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # = OpenHash
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # OpenHash is very similar to Ruby's own OpenStruct, but it offers some
         | 
| 4 | 
            +
            # useful advantages in that it is a true Hash object.
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            # Because OpenHash is a subclass of Hash, it can do everything a Hash
         | 
| 7 | 
            +
            # can *unless* a Hash method has been explicity exempted for use
         | 
| 8 | 
            +
            # as an open read/writer via the #omit! method.
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            class OpenHash < Hash
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              # New OpenHash.
         | 
| 13 | 
            +
              def initialize(data={})
         | 
| 14 | 
            +
                super()
         | 
| 15 | 
            +
                merge!(data)
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              #
         | 
| 19 | 
            +
              def <<(x)
         | 
| 20 | 
            +
                case x
         | 
| 21 | 
            +
                when Hash
         | 
| 22 | 
            +
                  update(x)
         | 
| 23 | 
            +
                when Array
         | 
| 24 | 
            +
                  x.each_slice(2) do |(k,v)|
         | 
| 25 | 
            +
                    self[k] = v
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              #
         | 
| 31 | 
            +
              def respond_to?(name)
         | 
| 32 | 
            +
                key?(name.to_sym) || super(name)
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              #
         | 
| 36 | 
            +
              def to_h
         | 
| 37 | 
            +
                dup
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              #
         | 
| 41 | 
            +
              def to_hash
         | 
| 42 | 
            +
                dup
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              #
         | 
| 46 | 
            +
              def inspect
         | 
| 47 | 
            +
                super
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              # Omit specific Hash methods from slot protection.
         | 
| 51 | 
            +
              def omit!(*methods)
         | 
| 52 | 
            +
                methods.reject!{ |x| x.to_s =~ /^__/ }
         | 
| 53 | 
            +
                (class << self; self; end).class_eval{ private *methods }
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              # Route get and set calls.
         | 
| 57 | 
            +
              def method_missing(s,*a, &b)
         | 
| 58 | 
            +
                type = s.to_s[-1,1]
         | 
| 59 | 
            +
                name = s.to_s.sub(/[!?=]$/, '')
         | 
| 60 | 
            +
                key  = name.to_sym
         | 
| 61 | 
            +
                case type
         | 
| 62 | 
            +
                when '='
         | 
| 63 | 
            +
                  self[key] = a[0]
         | 
| 64 | 
            +
                #when '!'
         | 
| 65 | 
            +
                #  self[s] = OpenHash.new
         | 
| 66 | 
            +
                when '?'
         | 
| 67 | 
            +
                  key?(key)
         | 
| 68 | 
            +
                else
         | 
| 69 | 
            +
                  if key?(key)
         | 
| 70 | 
            +
                    self[key]
         | 
| 71 | 
            +
                  else
         | 
| 72 | 
            +
                    super(s,*a,&b)
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            end
         | 
    
        data/lib/hashery/opencascade.rb
    CHANGED
    
    | @@ -1,99 +1 @@ | |
| 1 | 
            -
            require 'hashery/ | 
| 2 | 
            -
            #require 'facets/nullclass'
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            # = OpenCascade
         | 
| 5 | 
            -
            #
         | 
| 6 | 
            -
            # OpenCascade is subclass of OpenHash. It differs in a few
         | 
| 7 | 
            -
            # significant ways.
         | 
| 8 | 
            -
            #
         | 
| 9 | 
            -
            # The main reason this class is labeled "cascade", every internal
         | 
| 10 | 
            -
            # Hash is transformed into an OpenCascade dynamically upon access.
         | 
| 11 | 
            -
            # This makes it easy to create "cascading" references.
         | 
| 12 | 
            -
            #
         | 
| 13 | 
            -
            #   h = { :x => { :y => { :z => 1 } } }
         | 
| 14 | 
            -
            #   c = OpenCascade[h]
         | 
| 15 | 
            -
            #   c.x.y.z  #=> 1
         | 
| 16 | 
            -
            #
         | 
| 17 | 
            -
            # As soon as you access a node it automatically becomes an OpenCascade.
         | 
| 18 | 
            -
            #
         | 
| 19 | 
            -
            #   c = OpenCascade.new   #=> #<OpenCascade:0x7fac3680ccf0 {}>
         | 
| 20 | 
            -
            #   c.r                   #=> #<OpenCascade:0x7fac368084c0 {}>
         | 
| 21 | 
            -
            #   c.a.b                 #=> #<OpenCascade:0x7fac3680a4f0 {}>
         | 
| 22 | 
            -
            #
         | 
| 23 | 
            -
            # But if you set a node, then that will be it's value.
         | 
| 24 | 
            -
            #
         | 
| 25 | 
            -
            #   c.a.b = 4             #=> 4
         | 
| 26 | 
            -
            #
         | 
| 27 | 
            -
            # To query a node without causing the auto-creation of an OpenCasade
         | 
| 28 | 
            -
            # object, use the ?-mark.
         | 
| 29 | 
            -
            #
         | 
| 30 | 
            -
            #   c.a.z?                #=> nil
         | 
| 31 | 
            -
            #
         | 
| 32 | 
            -
            # OpenCascade also transforms Hashes within Arrays.
         | 
| 33 | 
            -
            #
         | 
| 34 | 
            -
            #  h = { :x=>[ {:a=>1}, {:a=>2} ], :y=>1 }
         | 
| 35 | 
            -
            #  c = OpenCascade[h]
         | 
| 36 | 
            -
            #  c.x.first.a.assert == 1
         | 
| 37 | 
            -
            #  c.x.last.a.assert == 2
         | 
| 38 | 
            -
            #
         | 
| 39 | 
            -
            # Finally, you can set a node and get the reciever back using
         | 
| 40 | 
            -
            # the !-mark.
         | 
| 41 | 
            -
            #
         | 
| 42 | 
            -
            #   c = OpenCascade.new   #=> #<OpenCascade:0x7fac3680ccf0 {}>
         | 
| 43 | 
            -
            #   c.x!(4).y!(3)         #=> #<OpenCascade:0x7fac3680ccf0 {:x=>4, :y=>3}>
         | 
| 44 | 
            -
            #
         | 
| 45 | 
            -
            #--
         | 
| 46 | 
            -
            # Last, when an entry is not found, 'null' is returned rather then 'nil'.
         | 
| 47 | 
            -
            # This allows for run-on entries withuot error. Eg.
         | 
| 48 | 
            -
            #
         | 
| 49 | 
            -
            #   o = OpenCascade.new
         | 
| 50 | 
            -
            #   o.a.b.c  #=> null
         | 
| 51 | 
            -
            #
         | 
| 52 | 
            -
            # Unfortuately this requires an explict test for null? in 'if' conditions.
         | 
| 53 | 
            -
            #
         | 
| 54 | 
            -
            #   if o.a.b.c.null?  # true if null
         | 
| 55 | 
            -
            #   if o.a.b.c.nil?   # true if nil or null
         | 
| 56 | 
            -
            #   if o.a.b.c.not?   # true if nil or null or false
         | 
| 57 | 
            -
            #
         | 
| 58 | 
            -
            # So be sure to take that into account.
         | 
| 59 | 
            -
            #++
         | 
| 60 | 
            -
             | 
| 61 | 
            -
            class OpenCascade < OpenHash
         | 
| 62 | 
            -
             | 
| 63 | 
            -
              #
         | 
| 64 | 
            -
              def method_missing(sym, *args, &blk)
         | 
| 65 | 
            -
                type = sym.to_s[-1,1]
         | 
| 66 | 
            -
                name = sym.to_s.gsub(/[=!?]$/, '').to_sym
         | 
| 67 | 
            -
                case type
         | 
| 68 | 
            -
                when '='
         | 
| 69 | 
            -
                  self[name] = args.first
         | 
| 70 | 
            -
                when '!'
         | 
| 71 | 
            -
                  #@hash.__send__(name, *args, &blk)
         | 
| 72 | 
            -
                  __send__(name, *args, &blk)
         | 
| 73 | 
            -
                when '?'
         | 
| 74 | 
            -
                  self[name]
         | 
| 75 | 
            -
                else
         | 
| 76 | 
            -
                  if key?(name)
         | 
| 77 | 
            -
                    self[name] = transform_entry(self[name])
         | 
| 78 | 
            -
                  else
         | 
| 79 | 
            -
                    self[name] = OpenCascade.new #self.class.new
         | 
| 80 | 
            -
                  end
         | 
| 81 | 
            -
                end
         | 
| 82 | 
            -
              end
         | 
| 83 | 
            -
             | 
| 84 | 
            -
              private
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                #
         | 
| 87 | 
            -
                def transform_entry(entry)
         | 
| 88 | 
            -
                  case entry
         | 
| 89 | 
            -
                  when Hash
         | 
| 90 | 
            -
                    OpenCascade.new(entry) #self.class.new(val)
         | 
| 91 | 
            -
                  when Array
         | 
| 92 | 
            -
                    entry.map{ |e| transform_entry(e) }
         | 
| 93 | 
            -
                  else
         | 
| 94 | 
            -
                    entry
         | 
| 95 | 
            -
                  end
         | 
| 96 | 
            -
                end
         | 
| 97 | 
            -
             | 
| 98 | 
            -
            end
         | 
| 99 | 
            -
             | 
| 1 | 
            +
            require 'hashery/open_cascade'
         | 
    
        data/lib/hashery/openhash.rb
    CHANGED
    
    | @@ -1,77 +1 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            #
         | 
| 3 | 
            -
            # OpenHash is very similar to Ruby's own OpenStruct, but it offers some
         | 
| 4 | 
            -
            # useful advantages in that it is a true Hash object.
         | 
| 5 | 
            -
            #
         | 
| 6 | 
            -
            # Because OpenHash is a subclass of Hash, it can do everything a Hash
         | 
| 7 | 
            -
            # can *unless* a Hash method has been explicity exempted for use
         | 
| 8 | 
            -
            # as an open read/writer via the #omit! method.
         | 
| 9 | 
            -
             | 
| 10 | 
            -
            class OpenHash < Hash
         | 
| 11 | 
            -
             | 
| 12 | 
            -
              # New OpenHash.
         | 
| 13 | 
            -
              def initialize(data={})
         | 
| 14 | 
            -
                super()
         | 
| 15 | 
            -
                merge!(data)
         | 
| 16 | 
            -
              end
         | 
| 17 | 
            -
             | 
| 18 | 
            -
              #
         | 
| 19 | 
            -
              def <<(x)
         | 
| 20 | 
            -
                case x
         | 
| 21 | 
            -
                when Hash
         | 
| 22 | 
            -
                  update(x)
         | 
| 23 | 
            -
                when Array
         | 
| 24 | 
            -
                  x.each_slice(2) do |(k,v)|
         | 
| 25 | 
            -
                    self[k] = v
         | 
| 26 | 
            -
                  end
         | 
| 27 | 
            -
                end
         | 
| 28 | 
            -
              end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
              #
         | 
| 31 | 
            -
              def respond_to?(name)
         | 
| 32 | 
            -
                key?(name.to_sym) || super(name)
         | 
| 33 | 
            -
              end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
              #
         | 
| 36 | 
            -
              def to_h
         | 
| 37 | 
            -
                dup
         | 
| 38 | 
            -
              end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
              #
         | 
| 41 | 
            -
              def to_hash
         | 
| 42 | 
            -
                dup
         | 
| 43 | 
            -
              end
         | 
| 44 | 
            -
             | 
| 45 | 
            -
              #
         | 
| 46 | 
            -
              def inspect
         | 
| 47 | 
            -
                super
         | 
| 48 | 
            -
              end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
              # Omit specific Hash methods from slot protection.
         | 
| 51 | 
            -
              def omit!(*methods)
         | 
| 52 | 
            -
                methods.reject!{ |x| x.to_s =~ /^__/ }
         | 
| 53 | 
            -
                (class << self; self; end).class_eval{ private *methods }
         | 
| 54 | 
            -
              end
         | 
| 55 | 
            -
             | 
| 56 | 
            -
              # Route get and set calls.
         | 
| 57 | 
            -
              def method_missing(s,*a, &b)
         | 
| 58 | 
            -
                type = s.to_s[-1,1]
         | 
| 59 | 
            -
                name = s.to_s.sub(/[!?=]$/, '')
         | 
| 60 | 
            -
                key  = name.to_sym
         | 
| 61 | 
            -
                case type
         | 
| 62 | 
            -
                when '='
         | 
| 63 | 
            -
                  self[key] = a[0]
         | 
| 64 | 
            -
                #when '!'
         | 
| 65 | 
            -
                #  self[s] = OpenHash.new
         | 
| 66 | 
            -
                when '?'
         | 
| 67 | 
            -
                  key?(key)
         | 
| 68 | 
            -
                else
         | 
| 69 | 
            -
                  if key?(key)
         | 
| 70 | 
            -
                    self[key]
         | 
| 71 | 
            -
                  else
         | 
| 72 | 
            -
                    super(s,*a,&b)
         | 
| 73 | 
            -
                  end
         | 
| 74 | 
            -
                end
         | 
| 75 | 
            -
              end
         | 
| 76 | 
            -
             | 
| 77 | 
            -
            end
         | 
| 1 | 
            +
            require 'hashery/open_hash'
         | 
    
        data/lib/hashery/ordered_hash.rb
    CHANGED
    
    | @@ -1 +1,168 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # = OrderedHash
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # A simple ordered hash implmentation, for users of
         | 
| 4 | 
            +
            # Ruby 1.8.7 or less.
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            # NOTE: As of Ruby 1.9+ this class is not needed, since
         | 
| 7 | 
            +
            # Ruby 1.9's standard Hash tracks inseration order.
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            # This implementation derives from the same class in
         | 
| 10 | 
            +
            # ActiveSupport library.
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            class OrderedHash < ::Hash
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def to_yaml_type
         | 
| 15 | 
            +
                "!tag:yaml.org,2002:omap"
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def to_yaml(opts = {})
         | 
| 19 | 
            +
                YAML.quick_emit(self, opts) do |out|
         | 
| 20 | 
            +
                  out.seq(taguri, to_yaml_style) do |seq|
         | 
| 21 | 
            +
                    each do |k, v|
         | 
| 22 | 
            +
                      seq.add(k => v)
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              # Hash is ordered in Ruby 1.9!
         | 
| 29 | 
            +
              if RUBY_VERSION < '1.9'
         | 
| 30 | 
            +
                def initialize(*args, &block)
         | 
| 31 | 
            +
                  super
         | 
| 32 | 
            +
                  @keys = []
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def self.[](*args)
         | 
| 36 | 
            +
                  ordered_hash = new
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  if (args.length == 1 && args.first.is_a?(Array))
         | 
| 39 | 
            +
                    args.first.each do |key_value_pair|
         | 
| 40 | 
            +
                      next unless (key_value_pair.is_a?(Array))
         | 
| 41 | 
            +
                      ordered_hash[key_value_pair[0]] = key_value_pair[1]
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    return ordered_hash
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  unless (args.size % 2 == 0)
         | 
| 48 | 
            +
                    raise ArgumentError.new("odd number of arguments for Hash")
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  args.each_with_index do |val, ind|
         | 
| 52 | 
            +
                    next if (ind % 2 != 0)
         | 
| 53 | 
            +
                    ordered_hash[val] = args[ind + 1]
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  ordered_hash
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def initialize_copy(other)
         | 
| 60 | 
            +
                  super(other)
         | 
| 61 | 
            +
                  @keys = other.keys
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def []=(key, value)
         | 
| 65 | 
            +
                  @keys << key unless key?(key)
         | 
| 66 | 
            +
                  super(key, value)
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def delete(key)
         | 
| 70 | 
            +
                  if has_key? key
         | 
| 71 | 
            +
                    index = @keys.index(key)
         | 
| 72 | 
            +
                    @keys.delete_at(index)
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                  super(key)
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def delete_if
         | 
| 78 | 
            +
                  super
         | 
| 79 | 
            +
                  sync_keys!
         | 
| 80 | 
            +
                  self
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                def reject!
         | 
| 84 | 
            +
                  super
         | 
| 85 | 
            +
                  sync_keys!
         | 
| 86 | 
            +
                  self
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def reject(&block)
         | 
| 90 | 
            +
                  dup.reject!(&block)
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def keys
         | 
| 94 | 
            +
                  @keys.dup
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                def values
         | 
| 98 | 
            +
                  @keys.collect{ |key| self[key] }
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def to_hash
         | 
| 102 | 
            +
                  self
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                def to_a
         | 
| 106 | 
            +
                  @keys.map{ |key| [ key, self[key] ] }
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                def each_key
         | 
| 110 | 
            +
                  @keys.each{ |key| yield(key) }
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                def each_value
         | 
| 114 | 
            +
                  @keys.each{ |key| yield(self[key]) }
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                def each
         | 
| 118 | 
            +
                  @keys.each{ |key| yield(key, self[key]) }
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                alias_method :each_pair, :each
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                def clear
         | 
| 124 | 
            +
                  super
         | 
| 125 | 
            +
                  @keys.clear
         | 
| 126 | 
            +
                  self
         | 
| 127 | 
            +
                end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                def shift
         | 
| 130 | 
            +
                  k = @keys.first
         | 
| 131 | 
            +
                  v = delete(k)
         | 
| 132 | 
            +
                  [k, v]
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                def merge!(other_hash)
         | 
| 136 | 
            +
                  other_hash.each{ |k,v| self[k] = v }
         | 
| 137 | 
            +
                  self
         | 
| 138 | 
            +
                end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                def merge(other_hash)
         | 
| 141 | 
            +
                  dup.merge!(other_hash)
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                # When replacing with another hash, the initial order of our
         | 
| 145 | 
            +
                # keys must come from the other hash, ordered or not.
         | 
| 146 | 
            +
                def replace(other)
         | 
| 147 | 
            +
                  super
         | 
| 148 | 
            +
                  @keys = other.keys
         | 
| 149 | 
            +
                  self
         | 
| 150 | 
            +
                end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                def inspect
         | 
| 153 | 
            +
                  "#<OrderedHash #{super}>"
         | 
| 154 | 
            +
                end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                private
         | 
| 157 | 
            +
                  def sync_keys!
         | 
| 158 | 
            +
                    @keys.delete_if{ |k| !key?(k) }
         | 
| 159 | 
            +
                  end
         | 
| 160 | 
            +
              end
         | 
| 161 | 
            +
            end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
            require 'yaml'
         | 
| 164 | 
            +
             | 
| 165 | 
            +
            YAML.add_builtin_type("omap") do |type, val|
         | 
| 166 | 
            +
              OrderedHash[val.map(&:to_a).map(&:first)]
         | 
| 167 | 
            +
            end
         | 
| 168 | 
            +
             |