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.

Methods
#
C
D
E
I
K
M
N
R
S
T
V
Class Public methods
[](*args)
# File activesupport/lib/active_support/ordered_hash.rb, line 73
def self.[](*args)
  ordered_hash = new

  if (args.length == 1 && args.first.is_a?(Array))
    args.first.each do |key_value_pair|
      next unless (key_value_pair.is_a?(Array))
      ordered_hash[key_value_pair[0]] = key_value_pair[1]
    end

    return ordered_hash
  end

  unless (args.size % 2 == 0)
    raise ArgumentError.new("odd number of arguments for Hash")
  end

  args.each_with_index do |val, ind|
    next if (ind % 2 != 0)
    ordered_hash[val] = args[ind + 1]
  end

  ordered_hash
end
new(*args, &block)

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.

# File activesupport/lib/active_support/ordered_hash.rb, line 68
def initialize(*args, &block)
  super
  @keys = []
end
Instance Public methods
[]=(key, value)
# File activesupport/lib/active_support/ordered_hash.rb, line 103
def []=(key, value)
  @keys << key unless has_key?(key)
  super
end
clear()
# File activesupport/lib/active_support/ordered_hash.rb, line 174
def clear
  super
  @keys.clear
  self
end
delete(key)
# File activesupport/lib/active_support/ordered_hash.rb, line 108
def delete(key)
  if has_key? key
    index = @keys.index(key)
    @keys.delete_at index
  end
  super
end
delete_if()
# File activesupport/lib/active_support/ordered_hash.rb, line 116
def delete_if
  super
  sync_keys!
  self
end
each()
# File activesupport/lib/active_support/ordered_hash.rb, line 160
def each
  return to_enum(:each) unless block_given?
  @keys.each {|key| yield [key, self[key]]}
  self
end
each_key()
# File activesupport/lib/active_support/ordered_hash.rb, line 148
def each_key
  return to_enum(:each_key) unless block_given?
  @keys.each { |key| yield key }
  self
end
each_pair()
# File activesupport/lib/active_support/ordered_hash.rb, line 166
def each_pair
  return to_enum(:each_pair) unless block_given?
  @keys.each {|key| yield key, self[key]}
  self
end
each_value()
# File activesupport/lib/active_support/ordered_hash.rb, line 154
def each_value
  return to_enum(:each_value) unless block_given?
  @keys.each { |key| yield self[key]}
  self
end
encode_with(coder)
# File activesupport/lib/active_support/ordered_hash.rb, line 28
def encode_with(coder)
  coder.represent_seq '!omap', map { |k,v| { k => v } }
end
extractable_options?()

Returns true to make sure that this hash is extractable via Array#extract_options!

# File activesupport/lib/active_support/ordered_hash.rb, line 51
def extractable_options?
  true
end
initialize_copy(other)
# File activesupport/lib/active_support/ordered_hash.rb, line 97
def initialize_copy(other)
  super
  # make a deep copy of keys
  @keys = other.keys
end
inspect()
# File activesupport/lib/active_support/ordered_hash.rb, line 212
def inspect
  "#<OrderedHash #{super}>"
end
invert()
# File activesupport/lib/active_support/ordered_hash.rb, line 208
def invert
  OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}]
end
keys()
# File activesupport/lib/active_support/ordered_hash.rb, line 132
def keys
  @keys.dup
end
merge(other_hash, &block)
# File activesupport/lib/active_support/ordered_hash.rb, line 197
def merge(other_hash, &block)
  dup.merge!(other_hash, &block)
end
merge!(other_hash)
# File activesupport/lib/active_support/ordered_hash.rb, line 186
def merge!(other_hash)
  if block_given?
    other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v }
  else
    other_hash.each { |k, v| self[k] = v }
  end
  self
end
nested_under_indifferent_access()
# File activesupport/lib/active_support/ordered_hash.rb, line 46
def nested_under_indifferent_access
  self
end
reject(&block)
# File activesupport/lib/active_support/ordered_hash.rb, line 128
def reject(&block)
  dup.reject!(&block)
end
reject!()
# File activesupport/lib/active_support/ordered_hash.rb, line 122
def reject!
  super
  sync_keys!
  self
end
replace(other)

When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.

# File activesupport/lib/active_support/ordered_hash.rb, line 202
def replace(other)
  super
  @keys = other.keys
  self
end
shift()
# File activesupport/lib/active_support/ordered_hash.rb, line 180
def shift
  k = @keys.first
  v = delete(k)
  [k, v]
end
sync_keys!()
# File activesupport/lib/active_support/ordered_hash.rb, line 217
def sync_keys!
  @keys.delete_if {|k| !has_key?(k)}
end
to_a()
# File activesupport/lib/active_support/ordered_hash.rb, line 144
def to_a
  @keys.map { |key| [ key, self[key] ] }
end
to_hash()
# File activesupport/lib/active_support/ordered_hash.rb, line 140
def to_hash
  self
end
to_yaml(opts = {})
# File activesupport/lib/active_support/ordered_hash.rb, line 32
def to_yaml(opts = {})
  if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?
    return super
  end

  YAML.quick_emit(self, opts) do |out|
    out.seq(taguri) do |seq|
      each do |k, v|
        seq.add(k => v)
      end
    end
  end
end
to_yaml_type()
# File activesupport/lib/active_support/ordered_hash.rb, line 24
def to_yaml_type
  "!tag:yaml.org,2002:omap"
end
values()
# File activesupport/lib/active_support/ordered_hash.rb, line 136
def values
  @keys.collect { |key| self[key] }
end