2010-10-07 21:05:45 +00:00
|
|
|
# Note: HashWithIndifferentAccess is so commonly used
|
|
|
|
# that we always need to make sure that the driver works
|
|
|
|
# with it.
|
2010-10-12 19:39:28 +00:00
|
|
|
#require File.join(File.dirname(__FILE__), 'keys.rb')
|
2010-10-07 21:05:45 +00:00
|
|
|
|
|
|
|
# This class has dubious semantics and we only have it so that
|
|
|
|
# people can write params[:key] instead of params['key']
|
|
|
|
# and they get the same value for both keys.
|
|
|
|
|
2010-10-12 19:39:28 +00:00
|
|
|
class Hash
|
|
|
|
# Return a new hash with all keys converted to strings.
|
|
|
|
def stringify_keys
|
|
|
|
dup.stringify_keys!
|
|
|
|
end
|
|
|
|
|
|
|
|
# Destructively convert all keys to strings.
|
|
|
|
def stringify_keys!
|
|
|
|
keys.each do |key|
|
|
|
|
self[key.to_s] = delete(key)
|
|
|
|
end
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
# Return a new hash with all keys converted to symbols, as long as
|
|
|
|
# they respond to +to_sym+.
|
|
|
|
def symbolize_keys
|
|
|
|
dup.symbolize_keys!
|
|
|
|
end
|
|
|
|
|
|
|
|
# Destructively convert all keys to symbols, as long as they respond
|
|
|
|
# to +to_sym+.
|
|
|
|
def symbolize_keys!
|
|
|
|
keys.each do |key|
|
|
|
|
self[(key.to_sym rescue key) || key] = delete(key)
|
|
|
|
end
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
alias_method :to_options, :symbolize_keys
|
|
|
|
#alias_method :to_options!, :symbolize_keys!
|
|
|
|
end
|
|
|
|
|
2010-10-07 21:05:45 +00:00
|
|
|
module ActiveSupport
|
|
|
|
class HashWithIndifferentAccess < Hash
|
|
|
|
def extractable_options?
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(constructor = {})
|
|
|
|
if constructor.is_a?(Hash)
|
|
|
|
super()
|
|
|
|
update(constructor)
|
|
|
|
else
|
|
|
|
super(constructor)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def default(key = nil)
|
|
|
|
if key.is_a?(Symbol) && include?(key = key.to_s)
|
|
|
|
self[key]
|
|
|
|
else
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.new_from_hash_copying_default(hash)
|
|
|
|
ActiveSupport::HashWithIndifferentAccess.new(hash).tap do |new_hash|
|
|
|
|
new_hash.default = hash.default
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
|
|
|
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
|
|
|
|
|
|
|
# Assigns a new value to the hash:
|
|
|
|
#
|
|
|
|
# hash = HashWithIndifferentAccess.new
|
|
|
|
# hash[:key] = "value"
|
|
|
|
#
|
|
|
|
def []=(key, value)
|
|
|
|
regular_writer(convert_key(key), convert_value(value))
|
|
|
|
end
|
|
|
|
|
|
|
|
# Updates the instantized hash with values from the second:
|
|
|
|
#
|
|
|
|
# hash_1 = HashWithIndifferentAccess.new
|
|
|
|
# hash_1[:key] = "value"
|
|
|
|
#
|
|
|
|
# hash_2 = HashWithIndifferentAccess.new
|
|
|
|
# hash_2[:key] = "New Value!"
|
|
|
|
#
|
|
|
|
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
|
|
|
|
#
|
|
|
|
def update(other_hash)
|
|
|
|
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
alias_method :merge!, :update
|
|
|
|
|
|
|
|
# Checks the hash for a key matching the argument passed in:
|
|
|
|
#
|
|
|
|
# hash = HashWithIndifferentAccess.new
|
|
|
|
# hash["key"] = "value"
|
|
|
|
# hash.key? :key # => true
|
|
|
|
# hash.key? "key" # => true
|
|
|
|
#
|
|
|
|
def key?(key)
|
|
|
|
super(convert_key(key))
|
|
|
|
end
|
|
|
|
|
|
|
|
alias_method :include?, :key?
|
|
|
|
alias_method :has_key?, :key?
|
|
|
|
alias_method :member?, :key?
|
|
|
|
|
|
|
|
# Fetches the value for the specified key, same as doing hash[key]
|
|
|
|
def fetch(key, *extras)
|
|
|
|
super(convert_key(key), *extras)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns an array of the values at the specified indices:
|
|
|
|
#
|
|
|
|
# hash = HashWithIndifferentAccess.new
|
|
|
|
# hash[:a] = "x"
|
|
|
|
# hash[:b] = "y"
|
|
|
|
# hash.values_at("a", "b") # => ["x", "y"]
|
|
|
|
#
|
|
|
|
def values_at(*indices)
|
|
|
|
indices.collect {|key| self[convert_key(key)]}
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns an exact copy of the hash.
|
|
|
|
def dup
|
|
|
|
HashWithIndifferentAccess.new(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
|
|
|
|
# Does not overwrite the existing hash.
|
|
|
|
def merge(hash)
|
|
|
|
self.dup.update(hash)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
|
|
|
|
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
|
|
|
|
def reverse_merge(other_hash)
|
|
|
|
super self.class.new_from_hash_copying_default(other_hash)
|
|
|
|
end
|
|
|
|
|
|
|
|
def reverse_merge!(other_hash)
|
|
|
|
replace(reverse_merge( other_hash ))
|
|
|
|
end
|
|
|
|
|
|
|
|
# Removes a specified key from the hash.
|
|
|
|
def delete(key)
|
|
|
|
super(convert_key(key))
|
|
|
|
end
|
|
|
|
|
|
|
|
def stringify_keys!; self end
|
|
|
|
def stringify_keys; dup end
|
|
|
|
def symbolize_keys; to_hash.symbolize_keys end
|
|
|
|
def to_options!; self end
|
|
|
|
|
|
|
|
# Convert to a Hash with String keys.
|
|
|
|
def to_hash
|
|
|
|
Hash.new(default).merge!(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
def convert_key(key)
|
|
|
|
key.kind_of?(Symbol) ? key.to_s : key
|
|
|
|
end
|
|
|
|
|
|
|
|
def convert_value(value)
|
|
|
|
case value
|
|
|
|
when Hash
|
|
|
|
self.class.new_from_hash_copying_default(value)
|
|
|
|
when Array
|
|
|
|
value.collect { |e| e.is_a?(Hash) ? self.class.new_from_hash_copying_default(e) : e }
|
|
|
|
else
|
|
|
|
value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
|