::Hash
The order of iteration over hashes in Ruby 1.8 is undefined. For example, you do not know the order in which keys will return keys, or each yield pairs. ActiveSupport::OrderedHash implements a hash that preserves insertion order, as in Ruby 1.9:
oh = ActiveSupport::OrderedHash.new oh[:a] = 1 oh[:b] = 2 oh.keys # => [:a, :b], this order is guaranteed
ActiveSupport::OrderedHash is namespaced to prevent conflicts with other implementations.
# File lib/active_support/ordered_hash.rb, line 73 73: def self.[](*args) 74: ordered_hash = new 75: 76: if (args.length == 1 && args.first.is_a?(Array)) 77: args.first.each do |key_value_pair| 78: next unless (key_value_pair.is_a?(Array)) 79: ordered_hash[key_value_pair[0]] = key_value_pair[1] 80: end 81: 82: return ordered_hash 83: end 84: 85: unless (args.size % 2 == 0) 86: raise ArgumentError.new("odd number of arguments for Hash") 87: end 88: 89: args.each_with_index do |val, ind| 90: next if (ind % 2 != 0) 91: ordered_hash[val] = args[ind + 1] 92: end 93: 94: ordered_hash 95: end
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 # because albeit the algorithm itself would work, our []= is not being called at all by the C code.
# File lib/active_support/ordered_hash.rb, line 68 68: def initialize(*args, &block) 69: super 70: @keys = [] 71: end
# File lib/active_support/ordered_hash.rb, line 103 103: def []=(key, value) 104: @keys << key unless has_key?(key) 105: super 106: end
# File lib/active_support/ordered_hash.rb, line 174 174: def clear 175: super 176: @keys.clear 177: self 178: end
# File lib/active_support/ordered_hash.rb, line 108 108: def delete(key) 109: if has_key? key 110: index = @keys.index(key) 111: @keys.delete_at index 112: end 113: super 114: end
# File lib/active_support/ordered_hash.rb, line 116 116: def delete_if 117: super 118: sync_keys! 119: self 120: end
# File lib/active_support/ordered_hash.rb, line 160 160: def each 161: return to_enum(:each) unless block_given? 162: @keys.each {|key| yield [key, self[key]]} 163: self 164: end
# File lib/active_support/ordered_hash.rb, line 148 148: def each_key 149: return to_enum(:each_key) unless block_given? 150: @keys.each { |key| yield key } 151: self 152: end
# File lib/active_support/ordered_hash.rb, line 166 166: def each_pair 167: return to_enum(:each_pair) unless block_given? 168: @keys.each {|key| yield key, self[key]} 169: self 170: end
# File lib/active_support/ordered_hash.rb, line 154 154: def each_value 155: return to_enum(:each_value) unless block_given? 156: @keys.each { |key| yield self[key]} 157: self 158: end
# File lib/active_support/ordered_hash.rb, line 28 28: def encode_with(coder) 29: coder.represent_seq '!omap', map { |k,v| { k => v } } 30: end
Returns true to make sure that this hash is extractable via Array#extract_options!
# File lib/active_support/ordered_hash.rb, line 51 51: def extractable_options? 52: true 53: end
# File lib/active_support/ordered_hash.rb, line 97 97: def initialize_copy(other) 98: super 99: # make a deep copy of keys 100: @keys = other.keys 101: end
# File lib/active_support/ordered_hash.rb, line 212 212: def inspect 213: "#<OrderedHash #{super}>" 214: end
# File lib/active_support/ordered_hash.rb, line 208 208: def invert 209: OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}] 210: end
# File lib/active_support/ordered_hash.rb, line 132 132: def keys 133: @keys.dup 134: end
# File lib/active_support/ordered_hash.rb, line 197 197: def merge(other_hash, &block) 198: dup.merge!(other_hash, &block) 199: end
# File lib/active_support/ordered_hash.rb, line 186 186: def merge!(other_hash) 187: if block_given? 188: other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } 189: else 190: other_hash.each { |k, v| self[k] = v } 191: end 192: self 193: end
# File lib/active_support/ordered_hash.rb, line 46 46: def nested_under_indifferent_access 47: self 48: end
# File lib/active_support/ordered_hash.rb, line 128 128: def reject(&block) 129: dup.reject!(&block) 130: end
# File lib/active_support/ordered_hash.rb, line 122 122: def reject! 123: super 124: sync_keys! 125: self 126: end
When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
# File lib/active_support/ordered_hash.rb, line 202 202: def replace(other) 203: super 204: @keys = other.keys 205: self 206: end
# File lib/active_support/ordered_hash.rb, line 180 180: def shift 181: k = @keys.first 182: v = delete(k) 183: [k, v] 184: end
# File lib/active_support/ordered_hash.rb, line 217 217: def sync_keys! 218: @keys.delete_if {|k| !has_key?(k)} 219: end
# File lib/active_support/ordered_hash.rb, line 144 144: def to_a 145: @keys.map { |key| [ key, self[key] ] } 146: end
# File lib/active_support/ordered_hash.rb, line 140 140: def to_hash 141: self 142: end
# File lib/active_support/ordered_hash.rb, line 32 32: def to_yaml(opts = {}) 33: if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? 34: return super 35: end 36: 37: YAML.quick_emit(self, opts) do |out| 38: out.seq(taguri) do |seq| 39: each do |k, v| 40: seq.add(k => v) 41: end 42: end 43: end 44: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.