diff options
Diffstat (limited to 'activesupport/lib/active_support/core_ext/hash')
5 files changed, 54 insertions, 42 deletions
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 2763af6121..3005fef44c 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -7,7 +7,7 @@ require 'active_support/core_ext/string/inflections' class Hash # Returns a string containing an XML representation of its receiver: - # + # # {"foo" => 1, "bar" => 2}.to_xml # # => # # <?xml version="1.0" encoding="UTF-8"?> @@ -15,22 +15,34 @@ class Hash # # <foo type="integer">1</foo> # # <bar type="integer">2</bar> # # </hash> - # + # # To do so, the method loops over the pairs and builds nodes that depend on # the _values_. Given a pair +key+, +value+: - # + # # * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>. - # + # # * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>, # and +key+ singularized as <tt>:children</tt>. - # + # # * If +value+ is a callable object it must expect one or two arguments. Depending # on the arity, the callable is invoked with the +options+ hash as first argument - # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. Its - # return value becomes a new node. - # + # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The + # callable can add nodes by using <tt>options[:builder]</tt>. + # + # "foo".to_xml(lambda { |options, key| options[:builder].b(key) }) + # # => "<b>foo</b>" + # # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>. - # + # + # class Foo + # def to_xml(options) + # options[:builder].bar "fooing!" + # end + # end + # + # {:foo => Foo.new}.to_xml(:skip_instruct => true) + # # => "<hash><bar>fooing!</bar></hash>" + # # * Otherwise, a node with +key+ as tag is created with a string representation of # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added. # Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is @@ -48,9 +60,9 @@ class Hash # "DateTime" => "datetime", # "Time" => "datetime" # } - # + # # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option. - # + # # The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can # configure your own builder with the <tt>:builder</tt> option. The method also accepts # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder. @@ -108,17 +120,14 @@ class Hash # blank or nil parsed values are represented by nil elsif value.blank? || value['nil'] == 'true' nil - # If the type is the only element which makes it then + # If the type is the only element which makes it then # this still makes the value nil, except if type is # a XML node(where type['value'] is a Hash) elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash) nil else - xml_value = value.inject({}) do |h,(k,v)| - h[k] = typecast_xml_value(v) - h - end - + xml_value = Hash[value.map { |k,v| [k, typecast_xml_value(v)] }] + # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with # how multipart uploaded files from HTML appear xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value @@ -136,10 +145,7 @@ class Hash def unrename_keys(params) case params.class.to_s when "Hash" - params.inject({}) do |h,(k,v)| - h[k.to_s.tr("-", "_")] = unrename_keys(v) - h - end + Hash[params.map { |k,v| [k.to_s.tr("-", "_"), unrename_keys(v)] } ] when "Array" params.map { |v| unrename_keys(v) } else diff --git a/activesupport/lib/active_support/core_ext/hash/deep_dup.rb b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb new file mode 100644 index 0000000000..447142605c --- /dev/null +++ b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb @@ -0,0 +1,11 @@ +class Hash + # Returns a deep copy of hash. + def deep_dup + duplicate = self.dup + duplicate.each_pair do |k,v| + tv = duplicate[k] + duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_dup : v + end + duplicate + end +end diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb index 0420e206af..aad4b61e16 100644 --- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb +++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb @@ -7,8 +7,6 @@ class Hash # {:a => 1}.with_indifferent_access["a"] # => 1 # def with_indifferent_access - hash = ActiveSupport::HashWithIndifferentAccess.new(self) - hash.default = self.default - hash + ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self) end end diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb index 045a6944fa..d8748b1138 100644 --- a/activesupport/lib/active_support/core_ext/hash/keys.rb +++ b/activesupport/lib/active_support/core_ext/hash/keys.rb @@ -35,11 +35,13 @@ class Hash # as keys, this will fail. # # ==== Examples - # { :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", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years" + # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key: name" # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing def assert_valid_keys(*valid_keys) - unknown_keys = keys - [valid_keys].flatten - raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty? + valid_keys.flatten! + each_key do |k| + raise(ArgumentError, "Unknown key: #{k}") unless valid_keys.include?(k) + end end end diff --git a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb index d7ebd5feef..01863a162b 100644 --- a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb @@ -1,27 +1,22 @@ class Hash - # 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: + # Merges the caller into +other_hash+. For example, # - # def setup(options = {}) - # options.reverse_merge! :size => 25, :velocity => 10 - # end + # options = options.reverse_merge(:size => 25, :velocity => 10) # - # Using <tt>merge</tt>, the above example would look as follows: + # is equivalent to # - # def setup(options = {}) - # { :size => 25, :velocity => 10 }.merge(options) - # end + # options = {:size => 25, :velocity => 10}.merge(options) # - # 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. + # This is particularly useful for initializing an options hash + # with default values. 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. + # Destructive +reverse_merge+. def reverse_merge!(other_hash) - merge!( other_hash ){|k,o,n| o } + # right wins if there is no left + merge!( other_hash ){|key,left,right| left } end alias_method :reverse_update, :reverse_merge! |