提交 b4a17188 编写于 作者: J Jeremy Kemper

Convert hash extension modules to class reopens

上级 bc4e2aa9
require 'active_support/core_ext/hash/deep_merge'
require 'active_support/core_ext/hash/diff'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/util' require 'active_support/core_ext/util'
ActiveSupport.core_ext Hash, %w(keys indifferent_access deep_merge reverse_merge conversions diff slice except) ActiveSupport.core_ext Hash, %w(conversions)
module ActiveSupport #:nodoc: class Hash
module CoreExtensions #:nodoc: # Returns a new hash with +self+ and +other_hash+ merged recursively.
module Hash #:nodoc: def deep_merge(other_hash)
# Allows for deep merging merge(other_hash) do |key, oldval, newval|
module DeepMerge oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
# Returns a new hash with +self+ and +other_hash+ merged recursively. newval = newval.to_hash if newval.respond_to?(:to_hash)
def deep_merge(other_hash) oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
self.merge(other_hash) do |key, oldval, newval|
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
newval = newval.to_hash if newval.respond_to?(:to_hash)
oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
end
end
# Returns a new hash with +self+ and +other_hash+ merged recursively.
# Modifies the receiver in place.
def deep_merge!(other_hash)
replace(deep_merge(other_hash))
end
end
end end
end end
# Returns a new hash with +self+ and +other_hash+ merged recursively.
# Modifies the receiver in place.
def deep_merge!(other_hash)
replace(deep_merge(other_hash))
end
end end
module ActiveSupport #:nodoc: class Hash
module CoreExtensions #:nodoc: # Returns a hash that represents the difference between two hashes.
module Hash #:nodoc: #
module Diff # Examples:
# Returns a hash that represents the difference between two hashes. #
# # {1 => 2}.diff(1 => 2) # => {}
# Examples: # {1 => 2}.diff(1 => 3) # => {1 => 2}
# # {}.diff(1 => 2) # => {1 => 2}
# {1 => 2}.diff(1 => 2) # => {} # {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
# {1 => 2}.diff(1 => 3) # => {1 => 2} def diff(h2)
# {}.diff(1 => 2) # => {1 => 2} dup.delete_if { |k, v| h2[k] == v }.merge(h2.dup.delete_if { |k, v| has_key?(k) })
# {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
def diff(h2)
self.dup.delete_if { |k, v| h2[k] == v }.merge(h2.dup.delete_if { |k, v| self.has_key?(k) })
end
end
end
end end
end end
require 'set' class Hash
# Return a hash that includes everything but the given keys. This is useful for
module ActiveSupport #:nodoc: # limiting a set of parameters to everything but a few known toggles:
module CoreExtensions #:nodoc: #
module Hash #:nodoc: # @person.update_attributes(params[:person].except(:admin))
# Return a hash that includes everything but the given keys. This is useful for def except(*keys)
# limiting a set of parameters to everything but a few known toggles: dup.except!(*keys)
# end
# @person.update_attributes(params[:person].except(:admin))
module Except
# Returns a new hash without the given keys.
def except(*keys)
dup.except!(*keys)
end
# Replaces the hash without the given keys. # Replaces the hash without the given keys.
def except!(*keys) def except!(*keys)
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
keys.each { |key| delete(key) } keys.each { |key| delete(key) }
self self
end
end
end
end end
end end
# This class has dubious semantics and we only have it so that require 'active_support/hash_with_indifferent_access'
# people can write params[:key] instead of params['key']
# and they get the same value for both keys.
class HashWithIndifferentAccess < Hash class Hash
def initialize(constructor = {}) def with_indifferent_access
if constructor.is_a?(Hash) hash = HashWithIndifferentAccess.new(self)
super() hash.default = self.default
update(constructor) hash
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
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 other_hash.with_indifferent_access
end
# Removes a specified key from the hash.
def delete(key)
super(convert_key(key))
end
def stringify_keys!; self end
def symbolize_keys!; self 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
value.with_indifferent_access
when Array
value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
else
value
end
end
end
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Hash #:nodoc:
module IndifferentAccess #:nodoc:
def with_indifferent_access
hash = HashWithIndifferentAccess.new(self)
hash.default = self.default
hash
end
end
end
end end
end end
module ActiveSupport #:nodoc: class Hash
module CoreExtensions #:nodoc: # Return a new hash with all keys converted to strings.
module Hash #:nodoc: def stringify_keys
module Keys inject({}) do |options, (key, value)|
# Return a new hash with all keys converted to strings. options[key.to_s] = value
def stringify_keys options
inject({}) do |options, (key, value)| end
options[key.to_s] = value end
options
end
end
# Destructively convert all keys to strings. # Destructively convert all keys to strings.
def stringify_keys! def stringify_keys!
keys.each do |key| keys.each do |key|
self[key.to_s] = delete(key) self[key.to_s] = delete(key)
end end
self self
end end
# Return a new hash with all keys converted to symbols. # Return a new hash with all keys converted to symbols.
def symbolize_keys def symbolize_keys
inject({}) do |options, (key, value)| inject({}) do |options, (key, value)|
options[(key.to_sym rescue key) || key] = value options[(key.to_sym rescue key) || key] = value
options options
end end
end end
# Destructively convert all keys to symbols. # Destructively convert all keys to symbols.
def symbolize_keys! def symbolize_keys!
self.replace(self.symbolize_keys) self.replace(self.symbolize_keys)
end end
alias_method :to_options, :symbolize_keys alias_method :to_options, :symbolize_keys
alias_method :to_options!, :symbolize_keys! alias_method :to_options!, :symbolize_keys!
# Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch. # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
# Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
# as keys, this will fail. # as keys, this will fail.
# #
# ==== Examples # ==== Examples
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years" # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age" # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
def assert_valid_keys(*valid_keys) def assert_valid_keys(*valid_keys)
unknown_keys = keys - [valid_keys].flatten unknown_keys = keys - [valid_keys].flatten
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty? raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
end
end
end
end end
end end
module ActiveSupport #:nodoc: class Hash
module CoreExtensions #:nodoc: # Allows for reverse merging two hashes where the keys in the calling hash take precedence over those
module Hash #:nodoc: # in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values:
# Allows for reverse merging two hashes where the keys in the calling hash take precedence over those #
# in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values: # def setup(options = {})
# # options.reverse_merge! :size => 25, :velocity => 10
# def setup(options = {}) # end
# options.reverse_merge! :size => 25, :velocity => 10 #
# end # Using <tt>merge</tt>, the above example would look as follows:
# #
# Using <tt>merge</tt>, the above example would look as follows: # def setup(options = {})
# # { :size => 25, :velocity => 10 }.merge(options)
# def setup(options = {}) # end
# { :size => 25, :velocity => 10 }.merge(options) #
# end # The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already
# # have the respective key.
# The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already def reverse_merge(other_hash)
# have the respective key. other_hash.merge(self)
module ReverseMerge end
# Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
def reverse_merge(other_hash)
other_hash.merge(self)
end
# Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
# Modifies the receiver in place.
def reverse_merge!(other_hash)
replace(reverse_merge(other_hash))
end
alias_method :reverse_update, :reverse_merge! # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
end # Modifies the receiver in place.
end def reverse_merge!(other_hash)
replace(reverse_merge(other_hash))
end end
alias_method :reverse_update, :reverse_merge!
end end
module ActiveSupport #:nodoc: class Hash
module CoreExtensions #:nodoc: # Slice a hash to include only the given keys. This is useful for
module Hash #:nodoc: # limiting an options hash to valid keys before passing to a method:
# Slice a hash to include only the given keys. This is useful for #
# limiting an options hash to valid keys before passing to a method: # def search(criteria = {})
# # assert_valid_keys(:mass, :velocity, :time)
# def search(criteria = {}) # end
# assert_valid_keys(:mass, :velocity, :time) #
# end # search(options.slice(:mass, :velocity, :time))
# #
# search(options.slice(:mass, :velocity, :time)) # If you have an array of keys you want to limit to, you should splat them:
# #
# If you have an array of keys you want to limit to, you should splat them: # valid_keys = [:mass, :velocity, :time]
# # search(options.slice(*valid_keys))
# valid_keys = [:mass, :velocity, :time] def slice(*keys)
# search(options.slice(*valid_keys)) keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
module Slice hash = self.class.new
# Returns a new hash with only the given keys. keys.each { |k| hash[k] = self[k] if has_key?(k) }
def slice(*keys) hash
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) end
hash = self.class.new
keys.each { |k| hash[k] = self[k] if has_key?(k) }
hash
end
# Replaces the hash with only the given keys. # Replaces the hash with only the given keys.
# Returns a hash contained the removed key/value pairs # Returns a hash contained the removed key/value pairs
# {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d =>4} # {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d =>4}
def slice!(*keys) def slice!(*keys)
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
omit = slice(*self.keys - keys) omit = slice(*self.keys - keys)
hash = slice(*keys) hash = slice(*keys)
replace(hash) replace(hash)
omit omit
end
end
end
end end
end end
# 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.
class HashWithIndifferentAccess < Hash
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
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 other_hash.with_indifferent_access
end
# Removes a specified key from the hash.
def delete(key)
super(convert_key(key))
end
def stringify_keys!; self end
def symbolize_keys!; self 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
value.with_indifferent_access
when Array
value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
else
value
end
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册