Class: Hash
- Defined in:
- lib/active_support/json/encoding.rb,
lib/active_support/core_ext/hash/diff.rb,
lib/active_support/core_ext/hash/keys.rb,
lib/active_support/core_ext/hash/slice.rb,
lib/active_support/core_ext/hash/except.rb,
lib/active_support/core_ext/object/blank.rb,
lib/active_support/core_ext/hash/deep_merge.rb,
lib/active_support/core_ext/object/deep_dup.rb,
lib/active_support/core_ext/object/to_param.rb,
lib/active_support/core_ext/object/to_query.rb,
lib/active_support/core_ext/hash/conversions.rb,
lib/active_support/core_ext/hash/reverse_merge.rb,
lib/active_support/core_ext/array/extract_options.rb,
lib/active_support/core_ext/hash/indifferent_access.rb
Direct Known Subclasses
ActiveSupport::HashWithIndifferentAccess, ActiveSupport::OrderedHash, ActiveSupport::OrderedOptions
Class Method Summary collapse
-
.from_trusted_xml(xml) ⇒ Object
Builds a Hash from XML just like
Hash.from_xml
, but also allows Symbol and YAML. -
.from_xml(xml, disallowed_types = nil) ⇒ Object
Returns a Hash containing a collection of pairs when the key is the node name and the value is its content.
Instance Method Summary collapse
-
#as_json(options = nil) ⇒ Object
:nodoc:.
-
#assert_valid_keys(*valid_keys) ⇒ Object
Validate all keys in a hash match
*valid_keys
, raising ArgumentError on a mismatch. -
#deep_dup ⇒ Object
Returns a deep copy of hash.
-
#deep_merge(other_hash, &block) ⇒ Object
Returns a new hash with
self
andother_hash
merged recursively. -
#deep_merge!(other_hash, &block) ⇒ Object
Same as
deep_merge
, but modifiesself
. -
#deep_stringify_keys ⇒ Object
Return a new hash with all keys converted to strings.
-
#deep_stringify_keys! ⇒ Object
Destructively convert all keys to strings.
-
#deep_symbolize_keys ⇒ Object
Return a new hash with all keys converted to symbols, as long as they respond to
to_sym
. -
#deep_symbolize_keys! ⇒ Object
Destructively convert all keys to symbols, as long as they respond to
to_sym
. -
#deep_transform_keys(&block) ⇒ Object
Return a new hash with all keys converted by the block operation.
-
#deep_transform_keys!(&block) ⇒ Object
Destructively convert all keys by using the block operation.
-
#diff(other) ⇒ Object
Returns a hash that represents the difference between two hashes.
-
#encode_json(encoder) ⇒ Object
:nodoc:.
-
#except(*keys) ⇒ Object
Return a hash that includes everything but the given keys.
-
#except!(*keys) ⇒ Object
Replaces the hash without the given keys.
-
#extract!(*keys) ⇒ Object
Removes and returns the key/value pairs matching the given keys.
-
#extractable_options? ⇒ Boolean
By default, only instances of Hash itself are extractable.
-
#reverse_merge(other_hash) ⇒ Object
Merges the caller into
other_hash
. -
#reverse_merge!(other_hash) ⇒ Object
(also: #reverse_update)
Destructive
reverse_merge
. -
#slice(*keys) ⇒ Object
Slice a hash to include only the given keys.
-
#slice!(*keys) ⇒ Object
Replaces the hash with only the given keys.
-
#stringify_keys ⇒ Object
Return a new hash with all keys converted to strings.
-
#stringify_keys! ⇒ Object
Destructively convert all keys to strings.
-
#symbolize_keys ⇒ Object
(also: #to_options)
Return a new hash with all keys converted to symbols, as long as they respond to
to_sym
. -
#symbolize_keys! ⇒ Object
(also: #to_options!)
Destructively convert all keys to symbols, as long as they respond to
to_sym
. -
#to_param(namespace = nil) ⇒ Object
(also: #to_query)
Returns a string representation of the receiver suitable for use as a URL query string:.
-
#to_xml(options = {}) ⇒ Object
Returns a string containing an XML representation of its receiver:.
-
#transform_keys ⇒ Object
Return a new hash with all keys converted using the block operation.
-
#transform_keys! ⇒ Object
Destructively convert all keys using the block operations.
-
#with_indifferent_access ⇒ Object
(also: #nested_under_indifferent_access)
Returns an
ActiveSupport::HashWithIndifferentAccess
out of its receiver:.
Class Method Details
.from_trusted_xml(xml) ⇒ Object
Builds a Hash from XML just like Hash.from_xml
, but also allows Symbol and YAML.
112 113 114 |
# File 'lib/active_support/core_ext/hash/conversions.rb', line 112 def from_trusted_xml(xml) from_xml xml, [] end |
.from_xml(xml, disallowed_types = nil) ⇒ Object
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.
107 108 109 |
# 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 |
Instance Method Details
#as_json(options = nil) ⇒ Object
:nodoc:
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/active_support/json/encoding.rb', line 280 def as_json( = nil) #:nodoc: # create a subset of the hash by applying :only or :except subset = if if attrs = [:only] slice(*Array(attrs)) elsif attrs = [:except] except(*Array(attrs)) else self end else self end # use encoder as a proxy to call as_json on all values in the subset, to protect from circular references encoder = && [:encoder] || ActiveSupport::JSON::Encoding::Encoder.new() Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, )] }] end |
#assert_valid_keys(*valid_keys) ⇒ Object
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
67 68 69 70 71 72 |
# 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 |
#deep_dup ⇒ Object
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"
41 42 43 44 45 |
# 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 |
#deep_merge(other_hash, &block) ⇒ Object
Returns a new hash with self
and other_hash
merged recursively.
h1 = { a: true, b: { c: [1, 2, 3] } }
h2 = { a: false, b: { x: [3, 4, 5] } }
h1.deep_merge(h2) #=> { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
Like with Hash#merge in the standard library, a block can be provided to merge values:
h1 = { a: 100, b: 200, c: { c1: 100 } }
h2 = { b: 250, c: { c1: 200 } }
h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
# => { a: 100, b: 450, c: { c1: 300 } }
16 17 18 |
# File 'lib/active_support/core_ext/hash/deep_merge.rb', line 16 def deep_merge(other_hash, &block) dup.deep_merge!(other_hash, &block) end |
#deep_merge!(other_hash, &block) ⇒ Object
Same as deep_merge
, but modifies self
.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/active_support/core_ext/hash/deep_merge.rb', line 21 def deep_merge!(other_hash, &block) other_hash.each_pair do |current_key, other_value| this_value = self[current_key] self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash) this_value.deep_merge(other_value, &block) else if block_given? && key?(current_key) block.call(current_key, this_value, other_value) else other_value end end end self end |
#deep_stringify_keys ⇒ Object
Return a new hash with all keys converted to strings. This includes the keys from the root hash and from all nested hashes and arrays.
hash = { person: { name: 'Rob', age: '28' } }
hash.deep_stringify_keys
# => { "person" => { "name" => "Rob", "age" => "28" } }
101 102 103 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 101 def deep_stringify_keys deep_transform_keys{ |key| key.to_s } end |
#deep_stringify_keys! ⇒ Object
Destructively convert all keys to strings. This includes the keys from the root hash and from all nested hashes and arrays.
108 109 110 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 108 def deep_stringify_keys! deep_transform_keys!{ |key| key.to_s } end |
#deep_symbolize_keys ⇒ Object
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 and arrays.
hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
hash.deep_symbolize_keys
# => { person: { name: "Rob", age: "28" } }
120 121 122 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 120 def deep_symbolize_keys deep_transform_keys{ |key| key.to_sym rescue key } end |
#deep_symbolize_keys! ⇒ Object
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 and arrays.
127 128 129 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 127 def deep_symbolize_keys! deep_transform_keys!{ |key| key.to_sym rescue key } end |
#deep_transform_keys(&block) ⇒ Object
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 and arrays.
hash = { person: { name: 'Rob', age: '28' } }
hash.deep_transform_keys{ |key| key.to_s.upcase }
# => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
82 83 84 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 82 def deep_transform_keys(&block) _deep_transform_keys_in_object(self, &block) end |
#deep_transform_keys!(&block) ⇒ Object
Destructively convert all keys by using the block operation. This includes the keys from the root hash and from all nested hashes and arrays.
89 90 91 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 89 def deep_transform_keys!(&block) _deep_transform_keys_in_object!(self, &block) end |
#diff(other) ⇒ Object
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}
8 9 10 11 12 13 |
# 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 |
#encode_json(encoder) ⇒ Object
:nodoc:
299 300 301 302 303 304 305 306 307 |
# File 'lib/active_support/json/encoding.rb', line 299 def encode_json(encoder) #:nodoc: # values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be # processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields); # on the other hand, we need to run as_json on the elements, because the model representation may contain fields # like Time/Date in their original (not jsonified) form, etc. "{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v, false)}" } * ','}}" end |
#except(*keys) ⇒ Object
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))
6 7 8 |
# File 'lib/active_support/core_ext/hash/except.rb', line 6 def except(*keys) dup.except!(*keys) end |
#except!(*keys) ⇒ Object
Replaces the hash without the given keys.
11 12 13 14 |
# File 'lib/active_support/core_ext/hash/except.rb', line 11 def except!(*keys) keys.each { |key| delete(key) } self end |
#extract!(*keys) ⇒ Object
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}
39 40 41 |
# File 'lib/active_support/core_ext/hash/slice.rb', line 39 def extract!(*keys) keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) } end |
#extractable_options? ⇒ Boolean
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.
7 8 9 |
# File 'lib/active_support/core_ext/array/extract_options.rb', line 7 def instance_of?(Hash) end |
#reverse_merge(other_hash) ⇒ Object
Merges the caller into other_hash
. For example,
= .reverse_merge(size: 25, velocity: 10)
is equivalent to
= { size: 25, velocity: 10 }.merge()
This is particularly useful for initializing an options hash with default values.
12 13 14 |
# File 'lib/active_support/core_ext/hash/reverse_merge.rb', line 12 def reverse_merge(other_hash) other_hash.merge(self) end |
#reverse_merge!(other_hash) ⇒ Object Also known as: reverse_update
Destructive reverse_merge
.
17 18 19 20 |
# 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(*keys) ⇒ Object
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(.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(.slice(*valid_keys))
15 16 17 18 |
# 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 |
#slice!(*keys) ⇒ Object
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}
25 26 27 28 29 30 31 32 33 |
# 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) hash.default = default hash.default_proc = default_proc if default_proc replace(hash) omit end |
#stringify_keys ⇒ Object
Return a new hash with all keys converted to strings.
hash = { name: 'Rob', age: '28' }
hash.stringify_keys
#=> { "name" => "Rob", "age" => "28" }
31 32 33 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 31 def stringify_keys transform_keys{ |key| key.to_s } end |
#stringify_keys! ⇒ Object
Destructively convert all keys to strings. Same as stringify_keys
, but modifies self
.
37 38 39 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 37 def stringify_keys! transform_keys!{ |key| key.to_s } end |
#symbolize_keys ⇒ Object Also known as: to_options
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" }
48 49 50 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 48 def symbolize_keys transform_keys{ |key| key.to_sym rescue key } end |
#symbolize_keys! ⇒ Object Also known as: to_options!
Destructively convert all keys to symbols, as long as they respond to to_sym
. Same as symbolize_keys
, but modifies self
.
55 56 57 |
# File 'lib/active_support/core_ext/hash/keys.rb', line 55 def symbolize_keys! transform_keys!{ |key| key.to_sym rescue key } end |
#to_param(namespace = nil) ⇒ Object Also known as: to_query
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
.
53 54 55 56 57 |
# 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 |
#to_xml(options = {}) ⇒ Object
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 withkey
as:root
. -
If
value
is an array there’s a recursive call withkey
as:root
, andkey
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 theoptions
hash as first argument withkey
as:root
, andkey
singularized as second argument. The callable can add nodes by usingoptions[:builder]
.'foo'.to_xml(lambda { |, key| [:builder].b(key) }) # => "<b>foo</b>"
-
If
value
responds toto_xml
the method is invoked withkey
as:root
.class Foo def to_xml() [:builder]. '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 ofvalue
as text node. Ifvalue
isnil
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.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/active_support/core_ext/hash/conversions.rb', line 71 def to_xml( = {}) require 'active_support/builder' unless defined?(Builder) = .dup [:indent] ||= 2 [:root] ||= 'hash' [:builder] ||= Builder::XmlMarkup.new(indent: [:indent]) builder = [:builder] builder.instruct! unless .delete(:skip_instruct) root = ActiveSupport::XmlMini.rename_key([:root].to_s, ) builder.tag!(root) do each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, ) } yield builder if block_given? end end |
#transform_keys ⇒ Object
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" }
8 9 10 11 12 13 14 |
# 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 |
#transform_keys! ⇒ Object
Destructively convert all keys using the block operations. Same as transform_keys but modifies self
.
18 19 20 21 22 23 |
# 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 |
#with_indifferent_access ⇒ Object Also known as: nested_under_indifferent_access
Returns an ActiveSupport::HashWithIndifferentAccess
out of its receiver:
{ a: 1 }.with_indifferent_access['a'] # => 1
8 9 10 |
# File 'lib/active_support/core_ext/hash/indifferent_access.rb', line 8 def with_indifferent_access ActiveSupport::HashWithIndifferentAccess.(self) end |