Returns a Hash containing a collection of pairs when the key is the node name and the value is its content
xml = <<-XML <?xml version="1.0" encoding="UTF-8"?> <hash> <foo type="integer">1</foo> <bar type="integer">2</bar> </hash> XML hash = Hash.from_xml(xml) # => {"hash"=>{"foo"=>1, "bar"=>2}}
DisallowedType is raise if the XML contains attributes with
type="yaml"
or type="symbol"
.
Use Hash.from_trusted_xml
to parse this XML.
# File lib/active_support/core_ext/hash/conversions.rb, line 107 def from_xml(xml, disallowed_types = nil) ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h end
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 as keys, this will
fail.
{ 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
# File lib/active_support/core_ext/hash/keys.rb, line 67 def assert_valid_keys(*valid_keys) valid_keys.flatten! each_key do |k| raise ArgumentError.new("Unknown key: #{k}") unless valid_keys.include?(k) end end
Returns a deep copy of hash.
hash = { a: { b: 'b' } } dup = hash.deep_dup dup[:a][:c] = 'c' hash[:a][:c] #=> nil dup[:a][:c] #=> "c"
# File lib/active_support/core_ext/object/deep_dup.rb, line 41 def deep_dup each_with_object(dup) do |(key, value), hash| hash[key.deep_dup] = value.deep_dup end end
Returns a new hash with self
and other_hash
merged recursively.
h1 = { x: { y: [4,5,6] }, z: [7,8,9] } h2 = { x: { y: [7,8,9] }, z: 'xyz' } h1.deep_merge(h2) #=> {x: {y: [7, 8, 9]}, z: "xyz"} h2.deep_merge(h1) #=> {x: {y: [4, 5, 6]}, z: [7, 8, 9]} h1.deep_merge(h2) { |key, old, new| Array.wrap(old) + Array.wrap(new) } #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
# File lib/active_support/core_ext/hash/deep_merge.rb, line 11 def deep_merge(other_hash, &block) dup.deep_merge!(other_hash, &block) end
Same as deep_merge
, but modifies self
.
# File lib/active_support/core_ext/hash/deep_merge.rb, line 16 def deep_merge!(other_hash, &block) other_hash.each_pair do |k,v| tv = self[k] if tv.is_a?(Hash) && v.is_a?(Hash) self[k] = tv.deep_merge(v, &block) else self[k] = block && tv ? block.call(k, tv, v) : v end end self end
Return a new hash with all keys converted to strings. This includes the keys from the root hash and from all nested hashes.
hash = { person: { name: 'Rob', age: '28' } } hash.deep_stringify_keys # => { "person" => { "name" => "Rob", "age" => "28" } }
# File lib/active_support/core_ext/hash/keys.rb, line 109 def deep_stringify_keys deep_transform_keys{ |key| key.to_s } end
Destructively convert all keys to strings. This includes the keys from the root hash and from all nested hashes.
# File lib/active_support/core_ext/hash/keys.rb, line 116 def deep_stringify_keys! deep_transform_keys!{ |key| key.to_s } end
Return a new hash with all keys converted to symbols, as long as they
respond to to_sym
. This includes the keys from the root hash
and from all nested hashes.
hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } } hash.deep_symbolize_keys # => { person: { name: "Rob", age: "28" } }
# File lib/active_support/core_ext/hash/keys.rb, line 128 def deep_symbolize_keys deep_transform_keys{ |key| key.to_sym rescue key } end
Destructively convert all keys to symbols, as long as they respond to
to_sym
. This includes the keys from the root hash and from all
nested hashes.
# File lib/active_support/core_ext/hash/keys.rb, line 135 def deep_symbolize_keys! deep_transform_keys!{ |key| key.to_sym rescue key } end
Return a new hash with all keys converted by the block operation. This includes the keys from the root hash and from all nested hashes.
hash = { person: { name: 'Rob', age: '28' } } hash.deep_transform_keys{ |key| key.to_s.upcase } # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
# File lib/active_support/core_ext/hash/keys.rb, line 82 def deep_transform_keys(&block) result = {} each do |key, value| result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value end result end
Destructively convert all keys by using the block operation. This includes the keys from the root hash and from all nested hashes.
# File lib/active_support/core_ext/hash/keys.rb, line 93 def deep_transform_keys!(&block) keys.each do |key| value = delete(key) self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value end self end
Returns a hash that represents the difference between two hashes.
{1 => 2}.diff(1 => 2) # => {} {1 => 2}.diff(1 => 3) # => {1 => 2} {}.diff(1 => 2) # => {1 => 2} {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
# File lib/active_support/core_ext/hash/diff.rb, line 8 def diff(other) ActiveSupport::Deprecation.warn "Hash#diff is no longer used inside of Rails, and is being deprecated with no replacement. If you're using it to compare hashes for the purpose of testing, please use MiniTest's assert_equal instead." dup. delete_if { |k, v| other[k] == v }. merge!(other.dup.delete_if { |k, v| has_key?(k) }) end
Return a hash that includes everything but the given keys. This is useful for limiting a set of parameters to everything but a few known toggles:
@person.update(params[:person].except(:admin))
# File lib/active_support/core_ext/hash/except.rb, line 6 def except(*keys) dup.except!(*keys) end
Replaces the hash without the given keys.
# File lib/active_support/core_ext/hash/except.rb, line 11 def except!(*keys) keys.each { |key| delete(key) } self end
Removes and returns the key/value pairs matching the given keys.
{ a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2} { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
# File lib/active_support/core_ext/hash/slice.rb, line 37 def extract!(*keys) keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) } end
By default, only instances of Hash itself are extractable. Subclasses of Hash may implement this method and return true to declare themselves as extractable. If a Hash is extractable, Array#extract_options! pops it from the Array when it is the last element of the Array.
# File lib/active_support/core_ext/array/extract_options.rb, line 7 def extractable_options? instance_of?(Hash) end
Called when object is nested under an object that receives with_indifferent_access.
This method will be called on the current object by the enclosing object
and is aliased to with_indifferent_access
by default. Subclasses of Hash may overwrite this
method to return self
if converting to an
ActiveSupport::HashWithIndifferentAccess
would not be
desirable.
b = { b: 1 } { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
Merges the caller into other_hash
. For example,
options = options.reverse_merge(size: 25, velocity: 10)
is equivalent to
options = { size: 25, velocity: 10 }.merge(options)
This is particularly useful for initializing an options hash with default values.
# File lib/active_support/core_ext/hash/reverse_merge.rb, line 12 def reverse_merge(other_hash) other_hash.merge(self) end
Destructive reverse_merge
.
# File lib/active_support/core_ext/hash/reverse_merge.rb, line 17 def reverse_merge!(other_hash) # right wins if there is no left merge!( other_hash ){|key,left,right| left } end
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 = {}) criteria.assert_valid_keys(:mass, :velocity, :time) end search(options.slice(:mass, :velocity, :time))
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))
# File lib/active_support/core_ext/hash/slice.rb, line 15 def slice(*keys) keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) } end
Replaces the hash with only the given keys. Returns a hash containing the removed key/value pairs.
{ a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b) # => {:c=>3, :d=>4}
# File lib/active_support/core_ext/hash/slice.rb, line 25 def slice!(*keys) keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) omit = slice(*self.keys - keys) hash = slice(*keys) replace(hash) omit end
Return a new hash with all keys converted to strings.
hash = { name: 'Rob', age: '28' } hash.stringify_keys #=> { "name" => "Rob", "age" => "28" }
# File lib/active_support/core_ext/hash/keys.rb, line 31 def stringify_keys transform_keys{ |key| key.to_s } end
Destructively convert all keys to strings. Same as
stringify_keys
, but modifies self
.
# File lib/active_support/core_ext/hash/keys.rb, line 37 def stringify_keys! transform_keys!{ |key| key.to_s } end
Return a new hash with all keys converted to symbols, as long as they
respond to to_sym
.
hash = { 'name' => 'Rob', 'age' => '28' } hash.symbolize_keys #=> { name: "Rob", age: "28" }
# File lib/active_support/core_ext/hash/keys.rb, line 48 def symbolize_keys transform_keys{ |key| key.to_sym rescue key } end
Destructively convert all keys to symbols, as long as they respond to
to_sym
. Same as symbolize_keys
, but modifies
self
.
# File lib/active_support/core_ext/hash/keys.rb, line 55 def symbolize_keys! transform_keys!{ |key| key.to_sym rescue key } end
Returns a string representation of the receiver suitable for use as a URL query string:
{name: 'David', nationality: 'Danish'}.to_param # => "name=David&nationality=Danish"
An optional namespace can be passed to enclose the param names:
{name: 'David', nationality: 'Danish'}.to_param('user') # => "user[name]=David&user[nationality]=Danish"
The string pairs “key=value” that conform the query string are sorted lexicographically in ascending order.
This method is also aliased as to_query
.
# File lib/active_support/core_ext/object/to_param.rb, line 53 def to_param(namespace = nil) collect do |key, value| value.to_query(namespace ? "#{namespace}[#{key}]" : key) end.sort * '&' end
Returns a string containing an XML representation of its receiver:
{'foo' => 1, 'bar' => 2}.to_xml # => # <?xml version="1.0" encoding="UTF-8"?> # <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 :root
.
If value
is an array there's a recursive call with
key
as :root
, and key
singularized
as :children
.
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
:root
, and key
singularized as second argument.
The callable can add nodes by using options[:builder]
.
'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 :root
.
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 :skip_types
exists and is true, an attribute “type” is
added as well according to the following mapping:
XML_TYPE_NAMES = { "Symbol" => "symbol", "Fixnum" => "integer", "Bignum" => "integer", "BigDecimal" => "decimal", "Float" => "float", "TrueClass" => "boolean", "FalseClass" => "boolean", "Date" => "date", "DateTime" => "dateTime", "Time" => "dateTime" }
By default the root node is “hash”, but that's configurable via the
:root
option.
The default XML builder is a fresh instance of
Builder::XmlMarkup
. You can configure your own builder with
the :builder
option. The method also accepts options like
:dasherize
and friends, they are forwarded to the builder.
# File lib/active_support/core_ext/hash/conversions.rb, line 71 def to_xml(options = {}) require 'active_support/builder' unless defined?(Builder) options = options.dup options[:indent] ||= 2 options[:root] ||= 'hash' options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent]) builder = options[:builder] builder.instruct! unless options.delete(:skip_instruct) root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options) builder.tag!(root) do each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) } yield builder if block_given? end end
Return a new hash with all keys converted using the block operation.
hash = { name: 'Rob', age: '28' } hash.transform_keys{ |key| key.to_s.upcase } # => { "NAME" => "Rob", "AGE" => "28" }
# File lib/active_support/core_ext/hash/keys.rb, line 8 def transform_keys result = {} each_key do |key| result[yield(key)] = self[key] end result end
Destructively convert all keys using the block operations. Same as #transform_keys but modifies
self
.
# File lib/active_support/core_ext/hash/keys.rb, line 18 def transform_keys! keys.each do |key| self[yield(key)] = delete(key) end self end
Returns an ActiveSupport::HashWithIndifferentAccess
out of its
receiver:
{ a: 1 }.with_indifferent_access['a'] # => 1
# File lib/active_support/core_ext/hash/indifferent_access.rb, line 8 def with_indifferent_access ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self) end