# Copyright (c) 2005-2013 David Heinemeier Hansson## Permission is hereby granted, free of charge, to any person obtaining# a copy of this software and associated documentation files (the# "Software"), to deal in the Software without restriction, including# without limitation the rights to use, copy, modify, merge, publish,# distribute, sublicense, and/or sell copies of the Software, and to# permit persons to whom the Software is furnished to do so, subject to# the following conditions:## The above copyright notice and this permission notice shall be# included in all copies or substantial portions of the Software.## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.# This class was copied from an old version of ActiveSupport.classOrderedHash<::Hash# In MRI the Hash class is core and written in C. In particular, methods are# programmed with explicit C function calls and polymorphism is not honored.## For example, []= is crucial in this implementation to maintain the @keys# array but hash.c invokes rb_hash_aset() originally. This prevents method# reuse through inheritance and forces us to reimplement stuff.## For instance, we cannot use the inherited #merge! because albeit the algorithm# itself would work, our []= is not being called at all by the C code.definitialize(*args)super@keys=[]enddefself.[](*args)ordered_hash=newifargs.length==1&&args.first.is_a?(Array)args.first.eachdo|key_value_pair|nextunlesskey_value_pair.is_a?(Array)ordered_hash[key_value_pair[0]]=key_value_pair[1]endreturnordered_hashendunlessargs.size.even?raiseArgumentError.new("odd number of arguments for Hash")endargs.each_with_indexdo|val,ind|nextifind.odd?ordered_hash[val]=args[ind+1]endordered_hashenddefinitialize_copy(other)super# make a deep copy of keys@keys=other.keysenddef[]=(key,value)@keys<<keyunlesshas_key?(key)superenddefdelete(key)ifhas_key?keyindex=@keys.index(key)@keys.delete_atindexendsuperenddefdelete_ifsupersync_keys!selfenddefreject!supersync_keys!selfenddefrejectdup.reject!{|h,k|yieldh,k}enddefkeys@keys.dupenddefvalues@keys.map{|key|self[key]}enddefto_hashselfenddefto_a@keys.map{|key|[key,self[key]]}enddefeach_keyreturnto_enum(:each_key)unlessblock_given?@keys.each{|key|yieldkey}selfenddefeach_valuereturnto_enum(:each_value)unlessblock_given?@keys.each{|key|yieldself[key]}selfenddefeachreturnto_enum(:each)unlessblock_given?@keys.each{|key|yield[key,self[key]]}selfenddefeach_pairreturnto_enum(:each_pair)unlessblock_given?@keys.each{|key|yieldkey,self[key]}selfendalias_method:select,:find_alldefclearsuper@keys.clearselfenddefshiftk=@keys.firstv=delete(k)[k,v]enddefmerge!(other_hash)ifblock_given?other_hash.each{|k,v|self[k]=key?(k)?yield(k,self[k],v):v}elseother_hash.each{|k,v|self[k]=v}endselfendalias_method:update,:merge!defmerge(other_hash)ifblock_given?dup.merge!(other_hash){|k,v1,v2|yieldk,v1,v2}elsedup.merge!(other_hash)endend# When replacing with another hash, the initial order of our keys must come from the other hash --# ordered or not.defreplace(other)super@keys=other.keysselfenddefinvertOrderedHash[to_a.map!{|key_value_pair|key_value_pair.reverse}]enddefinspect"#<OrderedHash #{super}>"endprivatedefsync_keys!@keys.delete_if{|k|!has_key?(k)}endend