hashie 3.3.2 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -1
- data/README.md +42 -3
- data/UPGRADING.md +24 -0
- data/lib/hashie.rb +5 -0
- data/lib/hashie/dash.rb +26 -8
- data/lib/hashie/extensions/coercion.rb +15 -7
- data/lib/hashie/extensions/deep_merge.rb +17 -12
- data/lib/hashie/extensions/indifferent_access.rb +2 -2
- data/lib/hashie/extensions/mash/safe_assignment.rb +6 -3
- data/lib/hashie/extensions/method_access.rb +13 -3
- data/lib/hashie/extensions/stringify_keys.rb +41 -23
- data/lib/hashie/extensions/symbolize_keys.rb +42 -18
- data/lib/hashie/mash.rb +13 -7
- data/lib/hashie/version.rb +1 -1
- data/spec/hashie/dash_spec.rb +44 -2
- data/spec/hashie/extensions/coercion_spec.rb +15 -0
- data/spec/hashie/extensions/deep_merge_spec.rb +31 -8
- data/spec/hashie/extensions/indifferent_access_spec.rb +5 -0
- data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -1
- data/spec/hashie/extensions/key_conversion_spec.rb +6 -97
- data/spec/hashie/extensions/mash/safe_assignment_spec.rb +6 -0
- data/spec/hashie/extensions/method_access_spec.rb +8 -0
- data/spec/hashie/extensions/stringify_keys_spec.rb +101 -0
- data/spec/hashie/extensions/symbolize_keys_spec.rb +106 -0
- data/spec/hashie/mash_spec.rb +44 -0
- data/spec/hashie/trash_spec.rb +0 -1
- data/spec/spec_helper.rb +5 -0
- data/spec/support/module_context.rb +11 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea2ab0de9a0b73bb6f212ed67075c27c287bd4dc
|
4
|
+
data.tar.gz: 25fd352d7cf79e9b45aca7f00c1ee399d50b42a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2c88e593d475002121b7f5129b03f3675d23f09d06d2b828595f4e7c2e67b08a1d8ebcaf37e15539744e53f83ff932e25e45239c0375e82098608f57cbbe663
|
7
|
+
data.tar.gz: 5531b32d1b26fee4d63a41db2b96ceabc53821e783af229131fc78f3746362577c690997c3789b7f682ca42cde6c0280efe22f48145f2c63f9969e81ca48ab2f
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
## Next Release
|
2
|
+
|
3
|
+
* Your contribution here
|
4
|
+
|
5
|
+
## 3.4.0 (02/02/2014)
|
6
|
+
|
7
|
+
* [#271](https://github.com/intridea/hashie/pull/271): Added ability to define defaults based on current hash - [@gregory](https://github.com/gregory).
|
8
|
+
* [#247](https://github.com/intridea/hashie/pull/247): Fixed #stringify_keys and #symbolize_keys collision with ActiveSupport - [@bartoszkopinski](https://github.com/bartoszkopinski).
|
9
|
+
* [#249](https://github.com/intridea/hashie/pull/249): SafeAssignment will now also protect hash-style assignments - [@jrochkind](https://github.com/jrochkind).
|
10
|
+
* [#251](https://github.com/intridea/hashie/pull/251): Added block support to indifferent access #fetch - [@jgraichen](https://github.com/jgraichen).
|
11
|
+
* [#252](https://github.com/intridia/hashie/pull/252): Added support for conditionally required Hashie::Dash attributes - [@ccashwell](https://github.com/ccashwell).
|
12
|
+
* [#256](https://github.com/intridia/hashie/pull/256): Inherit key coercions - [@Erol](https://github.com/Erol).
|
13
|
+
* [#259](https://github.com/intridia/hashie/pull/259): Fixed handling of default proc values in Mash - [@Erol](https://github.com/Erol).
|
14
|
+
* [#260](https://github.com/intridia/hashie/pull/260): Added block support to Extensions::DeepMerge - [@galathius](https://github.com/galathius).
|
15
|
+
* [#254](https://github.com/intridea/hashie/pull/254): Added public utility methods for stringify and symbolize keys - [@maxlinc](https://github.com/maxlinc).
|
16
|
+
* [#261](https://github.com/intridea/hashie/pull/261): Fixed bug where Dash.property modifies argument object - [@d_tw](https://github.com/d_tw).
|
17
|
+
* [#264](https://github.com/intridea/hashie/pull/264): Methods such as abc? return true/false with Hashie::Extensions::MethodReader - [@Zloy](https://github.com/Zloy).
|
18
|
+
* [#269](https://github.com/intridea/hashie/pull/269): Add #extractable_options? so ActiveSupport Array#extract_options! can extract it - [@ridiculous](https://github.com/ridiculous).
|
19
|
+
* Your contribution here.
|
20
|
+
|
1
21
|
## 3.3.2 (11/26/2014)
|
2
22
|
|
3
23
|
* [#233](https://github.com/intridea/hashie/pull/233): Custom error messages for required properties in Hashie::Dash subclasses - [@joss](https://github.com/joss).
|
@@ -14,7 +34,7 @@
|
|
14
34
|
* [#201](https://github.com/intridea/hashie/pull/201): Hashie::Trash transforms can be inherited - [@fobocaster](https://github.com/fobocaster).
|
15
35
|
* [#189](https://github.com/intridea/hashie/pull/189): Added Rash#fetch - [@medcat](https://github.com/medcat).
|
16
36
|
* [#200](https://github.com/intridea/hashie/pull/200): Improved coercion: primitives and error handling - [@maxlinc](https://github.com/maxlinc).
|
17
|
-
* [#204](https://github.com/intridea/hashie/pull/204): Added Hashie::Extensions::MethodOverridingWriter and
|
37
|
+
* [#204](https://github.com/intridea/hashie/pull/204): Added Hashie::Extensions::MethodOverridingWriter and MethodAccessWithOverride - [@michaelherold](https://github.com/michaelherold).
|
18
38
|
* [#205](http://github.com/intridea/hashie/pull/205): Added Hashie::Extensions::Mash::SafeAssignment - [@michaelherold](https://github.com/michaelherold).
|
19
39
|
* [#206](http://github.com/intridea/hashie/pull/206): Fixed stack overflow from repetitively including coercion in subclasses - [@michaelherold](https://github.com/michaelherold).
|
20
40
|
* [#207](http://github.com/intridea/hashie/pull/207): Fixed inheritance of transformations in Trash - [@fobocaster](https://github.com/fobocaster).
|
data/README.md
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
# Hashie
|
1
|
+
# Hashie
|
2
|
+
|
3
|
+
[![Gem Version](http://img.shields.io/gem/v/hashie.svg)](http://badge.fury.io/rb/hashie)
|
4
|
+
[![Build Status](http://img.shields.io/travis/intridea/hashie.svg)](https://travis-ci.org/intridea/hashie)
|
5
|
+
[![Dependency Status](https://gemnasium.com/intridea/hashie.svg)](https://gemnasium.com/intridea/hashie)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/intridea/hashie.svg)](https://codeclimate.com/github/intridea/hashie)
|
7
|
+
[![Coverage Status](https://codeclimate.com/github/intridea/hashie/badges/coverage.svg)](https://codeclimate.com/github/intridea/hashie)
|
2
8
|
|
3
9
|
Hashie is a growing collection of tools that extend Hashes and make them more useful.
|
4
10
|
|
@@ -141,6 +147,15 @@ end
|
|
141
147
|
|
142
148
|
The KeyConversion extension gives you the convenience methods of `symbolize_keys` and `stringify_keys` along with their bang counterparts. You can also include just stringify or just symbolize with `Hashie::Extensions::StringifyKeys` or `Hashie::Extensions::SymbolizeKeys`.
|
143
149
|
|
150
|
+
Hashie also has a utility method for converting keys on a Hash without a mixin:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
Hashie.symbolize_keys! hash # => Symbolizes keys of hash.
|
154
|
+
Hashie.symbolize_keys hash # => Returns a copy of hash with keys symbolized.
|
155
|
+
Hashie.stringify_keys hash # => Stringifies keys of hash.
|
156
|
+
Hashie.stringify_keys hash # => Returns a copy of hash with keys stringified.
|
157
|
+
```
|
158
|
+
|
144
159
|
### MergeInitializer
|
145
160
|
|
146
161
|
The MergeInitializer extension simply makes it possible to initialize a Hash subclass with another Hash, giving you a quick short-hand.
|
@@ -177,7 +192,7 @@ non_overriding = MyHash.new
|
|
177
192
|
non_overriding.zip = 'a-dee-doo-dah'
|
178
193
|
non_overriding.zip #=> [[['zip', 'a-dee-doo-dah']]]
|
179
194
|
|
180
|
-
overriding =
|
195
|
+
overriding = MyOverridingHash.new
|
181
196
|
overriding.zip = 'a-dee-doo-dah'
|
182
197
|
overriding.zip #=> 'a-dee-doo-dah'
|
183
198
|
overriding.__zip #=> [[['zip', 'a-dee-doo-dah']]]
|
@@ -230,6 +245,21 @@ h1.deep_merge(h2) # => { x: { y: [7, 8, 9] }, z: "xyz" }
|
|
230
245
|
h2.deep_merge(h1) # => { x: { y: [4, 5, 6] }, z: [7, 8, 9] }
|
231
246
|
```
|
232
247
|
|
248
|
+
Like with Hash#merge in the standard library, a block can be provided to merge values:
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
class MyHash < Hash
|
252
|
+
include Hashie::Extensions::DeepMerge
|
253
|
+
end
|
254
|
+
|
255
|
+
h1 = MyHash[{ a: 100, b: 200, c: { c1: 100 } }]
|
256
|
+
h2 = MyHash[{ b: 250, c: { c1: 200 } }]
|
257
|
+
|
258
|
+
h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
|
259
|
+
# => { a: 100, b: 450, c: { c1: 300 } }
|
260
|
+
```
|
261
|
+
|
262
|
+
|
233
263
|
### DeepFetch
|
234
264
|
|
235
265
|
This extension can be mixed in to provide for safe and concise retrieval of deeply nested hash values. In the event that the requested key does not exist a block can be provided and its value will be returned.
|
@@ -377,13 +407,16 @@ class SafeMash < ::Hashie::Mash
|
|
377
407
|
end
|
378
408
|
|
379
409
|
safe_mash = SafeMash.new
|
380
|
-
safe_mash.zip
|
410
|
+
safe_mash.zip = 'Test' # => ArgumentError
|
411
|
+
safe_mash[:zip] = 'test' # => still ArgumentError
|
381
412
|
```
|
382
413
|
|
383
414
|
## Dash
|
384
415
|
|
385
416
|
Dash is an extended Hash that has a discrete set of defined properties and only those properties may be set on the hash. Additionally, you can set defaults for each property. You can also flag a property as required. Required properties will raise an exception if unset. Another option is message for required properties, which allow you to add custom messages for required property.
|
386
417
|
|
418
|
+
You can also conditionally require certain properties by passing a Proc or Symbol. If a Proc is provided, it will be run in the context of the Dash instance. If a Symbol is provided, the value returned for the property or method of the same name will be evaluated. The property will be required if the result of the conditional is truthy.
|
419
|
+
|
387
420
|
### Example:
|
388
421
|
|
389
422
|
```ruby
|
@@ -391,7 +424,13 @@ class Person < Hashie::Dash
|
|
391
424
|
property :name, required: true
|
392
425
|
property :age, required: true, message: 'must be set.'
|
393
426
|
property :email
|
427
|
+
property :phone, required: -> { email.nil? }, message: 'is required if email is not set.'
|
428
|
+
property :pants, required: :weekday?, message: 'are only required on weekdays.'
|
394
429
|
property :occupation, default: 'Rubyist'
|
430
|
+
|
431
|
+
def weekday?
|
432
|
+
[ Time.now.saturday?, Time.now.sunday? ].none?
|
433
|
+
end
|
395
434
|
end
|
396
435
|
|
397
436
|
p = Person.new # => ArgumentError: The property 'name' is required for this Dash.
|
data/UPGRADING.md
CHANGED
@@ -1,6 +1,30 @@
|
|
1
1
|
Upgrading Hashie
|
2
2
|
================
|
3
3
|
|
4
|
+
### Upgrading to 3.2.2
|
5
|
+
|
6
|
+
#### Testing if key defined
|
7
|
+
|
8
|
+
In versions <= 3.2.1 Hash object being questioned doesn't return a boolean value as it's mentioned in README.md
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class MyHash < Hash
|
12
|
+
include Hashie::Extensions::MethodAccess
|
13
|
+
end
|
14
|
+
|
15
|
+
h = MyHash.new
|
16
|
+
h.abc = 'def'
|
17
|
+
h.abc # => 'def'
|
18
|
+
h.abc? # => 'def'
|
19
|
+
```
|
20
|
+
|
21
|
+
In versions >= 3.2.2 it returns a boolean value
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
h.abc? # => true
|
25
|
+
h.abb? # => false
|
26
|
+
```
|
27
|
+
|
4
28
|
### Upgrading to 3.2.1
|
5
29
|
|
6
30
|
#### Possible coercion changes
|
data/lib/hashie.rb
CHANGED
data/lib/hashie/dash.rb
CHANGED
@@ -26,7 +26,11 @@ module Hashie
|
|
26
26
|
#
|
27
27
|
# * <tt>:required</tt> - Specify the value as required for this
|
28
28
|
# property, to raise an error if a value is unset in a new or
|
29
|
-
# existing Dash.
|
29
|
+
# existing Dash. If a Proc is provided, it will be run in the
|
30
|
+
# context of the Dash instance. If a Symbol is provided, the
|
31
|
+
# property it represents must not be nil. The property is only
|
32
|
+
# required if the value is truthy.
|
33
|
+
#
|
30
34
|
# * <tt>:message</tt> - Specify custom error message for required property
|
31
35
|
#
|
32
36
|
def self.property(property_name, options = {})
|
@@ -40,7 +44,7 @@ module Hashie
|
|
40
44
|
|
41
45
|
unless instance_methods.map(&:to_s).include?("#{property_name}=")
|
42
46
|
define_method(property_name) { |&block| self.[](property_name, &block) }
|
43
|
-
property_assignment = property_name
|
47
|
+
property_assignment = "#{property_name}=".to_sym
|
44
48
|
define_method(property_assignment) { |value| self.[]=(property_name, value) }
|
45
49
|
end
|
46
50
|
|
@@ -48,8 +52,10 @@ module Hashie
|
|
48
52
|
@subclasses.each { |klass| klass.property(property_name, options) }
|
49
53
|
end
|
50
54
|
|
51
|
-
|
52
|
-
|
55
|
+
condition = options.delete(:required)
|
56
|
+
if condition
|
57
|
+
message = options.delete(:message) || "is required for #{name}."
|
58
|
+
required_properties[property_name] = { condition: condition, message: message }
|
53
59
|
else
|
54
60
|
fail ArgumentError, 'The :message option should be used with :required option.' if options.key?(:message)
|
55
61
|
end
|
@@ -90,7 +96,8 @@ module Hashie
|
|
90
96
|
|
91
97
|
self.class.defaults.each_pair do |prop, value|
|
92
98
|
self[prop] = begin
|
93
|
-
value.dup
|
99
|
+
val = value.dup
|
100
|
+
val.is_a?(Proc) && val.arity > 0 ? val.call(self) : val
|
94
101
|
rescue TypeError
|
95
102
|
value
|
96
103
|
end
|
@@ -180,19 +187,30 @@ module Hashie
|
|
180
187
|
end
|
181
188
|
|
182
189
|
def assert_property_set!(property)
|
183
|
-
fail_property_required_error!(property) if send(property).nil?
|
190
|
+
fail_property_required_error!(property) if send(property).nil? && required?(property)
|
184
191
|
end
|
185
192
|
|
186
193
|
def assert_property_required!(property, value)
|
187
|
-
fail_property_required_error!(property) if
|
194
|
+
fail_property_required_error!(property) if value.nil? && required?(property)
|
188
195
|
end
|
189
196
|
|
190
197
|
def fail_property_required_error!(property)
|
191
|
-
fail ArgumentError, "The property '#{property}' #{self.class.required_properties[property]}"
|
198
|
+
fail ArgumentError, "The property '#{property}' #{self.class.required_properties[property][:message]}"
|
192
199
|
end
|
193
200
|
|
194
201
|
def fail_no_property_error!(property)
|
195
202
|
fail NoMethodError, "The property '#{property}' is not defined for #{self.class.name}."
|
196
203
|
end
|
204
|
+
|
205
|
+
def required?(property)
|
206
|
+
return false unless self.class.required?(property)
|
207
|
+
|
208
|
+
condition = self.class.required_properties[property][:condition]
|
209
|
+
case condition
|
210
|
+
when Proc then !!(instance_exec(&condition))
|
211
|
+
when Symbol then !!(send(condition))
|
212
|
+
else !!(condition)
|
213
|
+
end
|
214
|
+
end
|
197
215
|
end
|
198
216
|
end
|
@@ -86,6 +86,9 @@ module Hashie
|
|
86
86
|
end
|
87
87
|
|
88
88
|
module ClassMethods
|
89
|
+
attr_writer :key_coercions
|
90
|
+
protected :key_coercions=
|
91
|
+
|
89
92
|
# Set up a coercion rule such that any time the specified
|
90
93
|
# key is set it will be coerced into the specified class.
|
91
94
|
# Coercion will occur by first attempting to call Class.coerce
|
@@ -101,16 +104,15 @@ module Hashie
|
|
101
104
|
# coerce_key :user, User
|
102
105
|
# end
|
103
106
|
def coerce_key(*attrs)
|
104
|
-
@key_coercions ||= {}
|
105
107
|
into = attrs.pop
|
106
|
-
attrs.each { |key|
|
108
|
+
attrs.each { |key| key_coercions[key] = into }
|
107
109
|
end
|
108
110
|
|
109
111
|
alias_method :coerce_keys, :coerce_key
|
110
112
|
|
111
113
|
# Returns a hash of any existing key coercions.
|
112
114
|
def key_coercions
|
113
|
-
@key_coercions
|
115
|
+
@key_coercions ||= {}
|
114
116
|
end
|
115
117
|
|
116
118
|
# Returns the specific key coercion for the specified key,
|
@@ -149,10 +151,10 @@ module Hashie
|
|
149
151
|
end
|
150
152
|
|
151
153
|
if options[:strict]
|
152
|
-
|
154
|
+
strict_value_coercions[from] = into
|
153
155
|
else
|
154
156
|
while from.superclass && from.superclass != Object
|
155
|
-
|
157
|
+
lenient_value_coercions[from] = into
|
156
158
|
from = from.superclass
|
157
159
|
end
|
158
160
|
end
|
@@ -160,11 +162,11 @@ module Hashie
|
|
160
162
|
|
161
163
|
# Return all value coercions that have the :strict rule as true.
|
162
164
|
def strict_value_coercions
|
163
|
-
@strict_value_coercions
|
165
|
+
@strict_value_coercions ||= {}
|
164
166
|
end
|
165
167
|
# Return all value coercions that have the :strict rule as false.
|
166
168
|
def lenient_value_coercions
|
167
|
-
@
|
169
|
+
@lenient_value_coercions ||= {}
|
168
170
|
end
|
169
171
|
|
170
172
|
# Fetch the value coercion, if any, for the specified object.
|
@@ -172,6 +174,12 @@ module Hashie
|
|
172
174
|
from = value.class
|
173
175
|
strict_value_coercions[from] || lenient_value_coercions[from]
|
174
176
|
end
|
177
|
+
|
178
|
+
def inherited(klass)
|
179
|
+
super
|
180
|
+
|
181
|
+
klass.key_coercions = key_coercions
|
182
|
+
end
|
175
183
|
end
|
176
184
|
end
|
177
185
|
end
|
@@ -2,28 +2,33 @@ module Hashie
|
|
2
2
|
module Extensions
|
3
3
|
module DeepMerge
|
4
4
|
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
5
|
-
def deep_merge(other_hash)
|
6
|
-
dup.deep_merge!(other_hash)
|
5
|
+
def deep_merge(other_hash, &block)
|
6
|
+
dup.deep_merge!(other_hash, &block)
|
7
7
|
end
|
8
8
|
|
9
9
|
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
10
10
|
# Modifies the receiver in place.
|
11
|
-
def deep_merge!(other_hash)
|
12
|
-
|
11
|
+
def deep_merge!(other_hash, &block)
|
12
|
+
return self unless other_hash.is_a?(::Hash)
|
13
|
+
_recursive_merge(self, other_hash, &block)
|
13
14
|
self
|
14
15
|
end
|
15
16
|
|
16
17
|
private
|
17
18
|
|
18
|
-
def _recursive_merge(hash, other_hash)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
def _recursive_merge(hash, other_hash, &block)
|
20
|
+
other_hash.each do |k, v|
|
21
|
+
hash[k] = if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
|
22
|
+
_recursive_merge(hash[k], v, &block)
|
23
|
+
else
|
24
|
+
if hash.key?(k) && block_given?
|
25
|
+
block.call(k, hash[k], v)
|
26
|
+
else
|
27
|
+
v
|
28
|
+
end
|
29
|
+
end
|
26
30
|
end
|
31
|
+
hash
|
27
32
|
end
|
28
33
|
end
|
29
34
|
end
|
@@ -107,8 +107,8 @@ module Hashie
|
|
107
107
|
regular_writer convert_key(key), convert_value(value)
|
108
108
|
end
|
109
109
|
|
110
|
-
def indifferent_fetch(key, *args)
|
111
|
-
regular_fetch convert_key(key), *args
|
110
|
+
def indifferent_fetch(key, *args, &block)
|
111
|
+
regular_fetch convert_key(key), *args, &block
|
112
112
|
end
|
113
113
|
|
114
114
|
def indifferent_delete(key)
|
@@ -2,10 +2,13 @@ module Hashie
|
|
2
2
|
module Extensions
|
3
3
|
module Mash
|
4
4
|
module SafeAssignment
|
5
|
-
def
|
6
|
-
fail ArgumentError, "The property #{
|
5
|
+
def custom_writer(key, *args) #:nodoc:
|
6
|
+
fail ArgumentError, "The property #{key} clashes with an existing method." if methods.include?(key.to_sym)
|
7
|
+
super
|
8
|
+
end
|
7
9
|
|
8
|
-
|
10
|
+
def []=(*args)
|
11
|
+
custom_writer(*args)
|
9
12
|
end
|
10
13
|
end
|
11
14
|
end
|
@@ -33,9 +33,19 @@ module Hashie
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def method_missing(name, *args)
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
if key?(name)
|
37
|
+
self[name]
|
38
|
+
else
|
39
|
+
sname = name.to_s
|
40
|
+
if key?(sname)
|
41
|
+
self[sname]
|
42
|
+
elsif sname[-1] == '?'
|
43
|
+
kname = sname[0..-2]
|
44
|
+
key?(kname) || key?(kname.to_sym)
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
39
49
|
end
|
40
50
|
end
|
41
51
|
|
@@ -8,10 +8,7 @@ module Hashie
|
|
8
8
|
# test.stringify_keys!
|
9
9
|
# test # => {'abc' => 'def'}
|
10
10
|
def stringify_keys!
|
11
|
-
|
12
|
-
stringify_keys_recursively!(self[k])
|
13
|
-
self[k.to_s] = delete(k)
|
14
|
-
end
|
11
|
+
StringifyKeys.stringify_keys!(self)
|
15
12
|
self
|
16
13
|
end
|
17
14
|
|
@@ -21,30 +18,51 @@ module Hashie
|
|
21
18
|
dup.stringify_keys!
|
22
19
|
end
|
23
20
|
|
24
|
-
|
21
|
+
module ClassMethods
|
22
|
+
# Stringify all keys recursively within nested
|
23
|
+
# hashes and arrays.
|
24
|
+
# @api private
|
25
|
+
def stringify_keys_recursively!(object)
|
26
|
+
case object
|
27
|
+
when self.class
|
28
|
+
object.stringify_keys!
|
29
|
+
when ::Array
|
30
|
+
object.each do |i|
|
31
|
+
stringify_keys_recursively!(i)
|
32
|
+
end
|
33
|
+
when ::Hash
|
34
|
+
stringify_keys!(object)
|
35
|
+
end
|
36
|
+
end
|
25
37
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
38
|
+
# Convert all keys in the hash to strings.
|
39
|
+
#
|
40
|
+
# @param [::Hash] hash
|
41
|
+
# @example
|
42
|
+
# test = {:abc => 'def'}
|
43
|
+
# test.stringify_keys!
|
44
|
+
# test # => {'abc' => 'def'}
|
45
|
+
def stringify_keys!(hash)
|
46
|
+
hash.keys.each do |k|
|
47
|
+
stringify_keys_recursively!(hash[k])
|
48
|
+
hash[k.to_s] = hash.delete(k)
|
34
49
|
end
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
50
|
+
hash
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return a copy of hash with all keys converted
|
54
|
+
# to strings.
|
55
|
+
# @param [::Hash] hash
|
56
|
+
def stringify_keys(hash)
|
57
|
+
hash.dup.tap do | new_hash |
|
58
|
+
stringify_keys! new_hash
|
42
59
|
end
|
43
|
-
object
|
44
|
-
else
|
45
|
-
object
|
46
60
|
end
|
47
61
|
end
|
62
|
+
|
63
|
+
class << self
|
64
|
+
include ClassMethods
|
65
|
+
end
|
48
66
|
end
|
49
67
|
end
|
50
68
|
end
|