activesupport 4.0.13 → 4.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +283 -508
  3. data/README.rdoc +1 -1
  4. data/lib/active_support.rb +7 -1
  5. data/lib/active_support/backtrace_cleaner.rb +5 -5
  6. data/lib/active_support/benchmarkable.rb +0 -10
  7. data/lib/active_support/cache.rb +62 -26
  8. data/lib/active_support/cache/file_store.rb +27 -22
  9. data/lib/active_support/cache/mem_cache_store.rb +2 -2
  10. data/lib/active_support/cache/memory_store.rb +1 -0
  11. data/lib/active_support/cache/strategy/local_cache.rb +3 -0
  12. data/lib/active_support/callbacks.rb +416 -245
  13. data/lib/active_support/concern.rb +13 -5
  14. data/lib/active_support/core_ext.rb +0 -1
  15. data/lib/active_support/core_ext/array.rb +0 -1
  16. data/lib/active_support/core_ext/array/access.rb +2 -0
  17. data/lib/active_support/core_ext/array/conversions.rb +2 -17
  18. data/lib/active_support/core_ext/array/grouping.rb +24 -12
  19. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
  20. data/lib/active_support/core_ext/class.rb +0 -1
  21. data/lib/active_support/core_ext/class/attribute.rb +1 -2
  22. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  23. data/lib/active_support/core_ext/date/calculations.rb +10 -0
  24. data/lib/active_support/core_ext/date/conversions.rb +5 -6
  25. data/lib/active_support/core_ext/date/zones.rb +2 -33
  26. data/lib/active_support/core_ext/date_and_time/calculations.rb +30 -11
  27. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  28. data/lib/active_support/core_ext/date_time/calculations.rb +12 -25
  29. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  30. data/lib/active_support/core_ext/date_time/zones.rb +3 -21
  31. data/lib/active_support/core_ext/hash.rb +0 -1
  32. data/lib/active_support/core_ext/hash/conversions.rb +6 -3
  33. data/lib/active_support/core_ext/hash/deep_merge.rb +11 -22
  34. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
  35. data/lib/active_support/core_ext/hash/keys.rb +27 -47
  36. data/lib/active_support/core_ext/kernel/reporting.rb +2 -6
  37. data/lib/active_support/core_ext/module.rb +1 -0
  38. data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
  39. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  40. data/lib/active_support/core_ext/module/delegation.rb +14 -4
  41. data/lib/active_support/core_ext/module/deprecation.rb +0 -2
  42. data/lib/active_support/core_ext/module/introspection.rb +0 -16
  43. data/lib/active_support/core_ext/module/method_transplanting.rb +11 -0
  44. data/lib/active_support/core_ext/numeric/time.rb +8 -0
  45. data/lib/active_support/core_ext/object.rb +1 -1
  46. data/lib/active_support/core_ext/object/blank.rb +1 -1
  47. data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
  48. data/lib/active_support/core_ext/object/inclusion.rb +4 -15
  49. data/lib/active_support/core_ext/object/json.rb +197 -0
  50. data/lib/active_support/core_ext/object/to_json.rb +4 -26
  51. data/lib/active_support/core_ext/object/to_param.rb +58 -1
  52. data/lib/active_support/core_ext/object/to_query.rb +7 -56
  53. data/lib/active_support/core_ext/object/try.rb +1 -1
  54. data/lib/active_support/core_ext/range/each.rb +2 -1
  55. data/lib/active_support/core_ext/string/access.rb +31 -31
  56. data/lib/active_support/core_ext/string/conversions.rb +9 -8
  57. data/lib/active_support/core_ext/string/exclude.rb +3 -3
  58. data/lib/active_support/core_ext/string/filters.rb +14 -4
  59. data/lib/active_support/core_ext/string/inflections.rb +11 -9
  60. data/lib/active_support/core_ext/string/output_safety.rb +65 -24
  61. data/lib/active_support/core_ext/string/zones.rb +1 -0
  62. data/lib/active_support/core_ext/thread.rb +4 -4
  63. data/lib/active_support/core_ext/time/calculations.rb +10 -57
  64. data/lib/active_support/core_ext/time/conversions.rb +3 -1
  65. data/lib/active_support/core_ext/time/zones.rb +2 -21
  66. data/lib/active_support/dependencies.rb +29 -13
  67. data/lib/active_support/deprecation.rb +4 -4
  68. data/lib/active_support/deprecation/behaviors.rb +3 -3
  69. data/lib/active_support/duration.rb +5 -7
  70. data/lib/active_support/file_update_checker.rb +1 -1
  71. data/lib/active_support/hash_with_indifferent_access.rb +4 -9
  72. data/lib/active_support/i18n.rb +4 -4
  73. data/lib/active_support/i18n_railtie.rb +2 -6
  74. data/lib/active_support/inflections.rb +0 -1
  75. data/lib/active_support/inflector/inflections.rb +17 -17
  76. data/lib/active_support/inflector/methods.rb +34 -17
  77. data/lib/active_support/json/decoding.rb +14 -21
  78. data/lib/active_support/json/encoding.rb +113 -285
  79. data/lib/active_support/key_generator.rb +1 -1
  80. data/lib/active_support/lazy_load_hooks.rb +1 -1
  81. data/lib/active_support/log_subscriber/test_helper.rb +1 -1
  82. data/lib/active_support/logger.rb +1 -1
  83. data/lib/active_support/message_encryptor.rb +3 -3
  84. data/lib/active_support/message_verifier.rb +6 -1
  85. data/lib/active_support/multibyte/chars.rb +1 -2
  86. data/lib/active_support/multibyte/unicode.rb +27 -39
  87. data/lib/active_support/notifications.rb +3 -3
  88. data/lib/active_support/notifications/instrumenter.rb +2 -1
  89. data/lib/active_support/number_helper.rb +20 -311
  90. data/lib/active_support/number_helper/number_converter.rb +182 -0
  91. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  92. data/lib/active_support/number_helper/number_to_delimited_converter.rb +21 -0
  93. data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
  94. data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
  95. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  96. data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
  97. data/lib/active_support/number_helper/number_to_rounded_converter.rb +62 -0
  98. data/lib/active_support/option_merger.rb +1 -1
  99. data/lib/active_support/ordered_hash.rb +0 -8
  100. data/lib/active_support/ordered_options.rb +8 -0
  101. data/lib/active_support/per_thread_registry.rb +9 -8
  102. data/lib/active_support/subscriber.rb +26 -3
  103. data/lib/active_support/test_case.rb +9 -10
  104. data/lib/active_support/testing/assertions.rb +0 -30
  105. data/lib/active_support/testing/autorun.rb +2 -2
  106. data/lib/active_support/testing/declarative.rb +18 -8
  107. data/lib/active_support/testing/isolation.rb +13 -65
  108. data/lib/active_support/testing/setup_and_teardown.rb +17 -2
  109. data/lib/active_support/testing/tagged_logging.rb +1 -1
  110. data/lib/active_support/testing/time_helpers.rb +55 -0
  111. data/lib/active_support/time_with_zone.rb +4 -4
  112. data/lib/active_support/values/time_zone.rb +18 -15
  113. data/lib/active_support/version.rb +1 -1
  114. data/lib/active_support/xml_mini.rb +2 -4
  115. metadata +71 -61
  116. data/lib/active_support/basic_object.rb +0 -11
  117. data/lib/active_support/buffered_logger.rb +0 -21
  118. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  119. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  120. data/lib/active_support/core_ext/logger.rb +0 -67
  121. data/lib/active_support/core_ext/proc.rb +0 -17
  122. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  123. data/lib/active_support/json/variable.rb +0 -18
  124. data/lib/active_support/testing/pending.rb +0 -14
@@ -78,7 +78,7 @@ module DateAndTime
78
78
  # Returns a new date/time at the start of the month.
79
79
  # DateTime objects will have a time set to 0:00.
80
80
  def beginning_of_month
81
- first_hour{ change(:day => 1) }
81
+ first_hour(change(:day => 1))
82
82
  end
83
83
  alias :at_beginning_of_month :beginning_of_month
84
84
 
@@ -113,7 +113,7 @@ module DateAndTime
113
113
  # which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+
114
114
  # when set. +DateTime+ objects have their time set to 0:00.
115
115
  def next_week(given_day_in_next_week = Date.beginning_of_week)
116
- first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)) }
116
+ first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
117
117
  end
118
118
 
119
119
  # Short-hand for months_since(1).
@@ -136,7 +136,7 @@ module DateAndTime
136
136
  # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
137
137
  # DateTime objects have their time set to 0:00.
138
138
  def prev_week(start_day = Date.beginning_of_week)
139
- first_hour{ weeks_ago(1).beginning_of_week.days_since(days_span(start_day)) }
139
+ first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
140
140
  end
141
141
  alias_method :last_week, :prev_week
142
142
 
@@ -188,7 +188,7 @@ module DateAndTime
188
188
  # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
189
189
  # DateTime objects have their time set to 23:59:59.
190
190
  def end_of_week(start_day = Date.beginning_of_week)
191
- last_hour{ days_since(6 - days_to_week_start(start_day)) }
191
+ last_hour(days_since(6 - days_to_week_start(start_day)))
192
192
  end
193
193
  alias :at_end_of_week :end_of_week
194
194
 
@@ -202,7 +202,7 @@ module DateAndTime
202
202
  # DateTime objects will have a time set to 23:59:59.
203
203
  def end_of_month
204
204
  last_day = ::Time.days_in_month(month, year)
205
- last_hour{ days_since(last_day - day) }
205
+ last_hour(days_since(last_day - day))
206
206
  end
207
207
  alias :at_end_of_month :end_of_month
208
208
 
@@ -213,16 +213,35 @@ module DateAndTime
213
213
  end
214
214
  alias :at_end_of_year :end_of_year
215
215
 
216
+ # Returns a Range representing the whole week of the current date/time.
217
+ # Week starts on start_day, default is <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
218
+ def all_week(start_day = Date.beginning_of_week)
219
+ beginning_of_week(start_day)..end_of_week(start_day)
220
+ end
221
+
222
+ # Returns a Range representing the whole month of the current date/time.
223
+ def all_month
224
+ beginning_of_month..end_of_month
225
+ end
226
+
227
+ # Returns a Range representing the whole quarter of the current date/time.
228
+ def all_quarter
229
+ beginning_of_quarter..end_of_quarter
230
+ end
231
+
232
+ # Returns a Range representing the whole year of the current date/time.
233
+ def all_year
234
+ beginning_of_year..end_of_year
235
+ end
236
+
216
237
  private
217
238
 
218
- def first_hour
219
- result = yield
220
- acts_like?(:time) ? result.change(:hour => 0) : result
239
+ def first_hour(date_or_time)
240
+ date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
221
241
  end
222
242
 
223
- def last_hour
224
- result = yield
225
- acts_like?(:time) ? result.end_of_day : result
243
+ def last_hour(date_or_time)
244
+ date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
226
245
  end
227
246
 
228
247
  def days_span(day)
@@ -0,0 +1,41 @@
1
+ module DateAndTime
2
+ module Zones
3
+ # Returns the simultaneous time in <tt>Time.zone</tt> if a zone is given or
4
+ # if Time.zone_default is set. Otherwise, it returns the current time.
5
+ #
6
+ # Time.zone = 'Hawaii' # => 'Hawaii'
7
+ # DateTime.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
8
+ # Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00
9
+ #
10
+ # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
11
+ # instead of the operating system's time zone.
12
+ #
13
+ # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
14
+ # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
15
+ #
16
+ # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
17
+ # DateTime.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
18
+ # Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00
19
+ def in_time_zone(zone = ::Time.zone)
20
+ time_zone = ::Time.find_zone! zone
21
+ time = acts_like?(:time) ? self : nil
22
+
23
+ if time_zone
24
+ time_with_zone(time, time_zone)
25
+ else
26
+ time || self.to_time
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def time_with_zone(time, zone)
33
+ if time
34
+ ActiveSupport::TimeWithZone.new(time.utc? ? time : time.getutc, zone)
35
+ else
36
+ ActiveSupport::TimeWithZone.new(nil, zone, to_time(:utc))
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -1,14 +1,7 @@
1
- require 'active_support/deprecation'
1
+ require 'date'
2
2
 
3
3
  class DateTime
4
4
  class << self
5
- # *DEPRECATED*: Use +DateTime.civil_from_format+ directly.
6
- def local_offset
7
- ActiveSupport::Deprecation.warn 'DateTime.local_offset is deprecated. Use DateTime.civil_from_format directly.'
8
-
9
- ::Time.local(2012).utc_offset.to_r / 86400
10
- end
11
-
12
5
  # Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or
13
6
  # <tt>config.time_zone</tt> are set, otherwise returns
14
7
  # <tt>Time.now.to_datetime</tt>.
@@ -17,16 +10,6 @@ class DateTime
17
10
  end
18
11
  end
19
12
 
20
- # Tells whether the DateTime object's datetime lies in the past.
21
- def past?
22
- self < ::DateTime.current
23
- end
24
-
25
- # Tells whether the DateTime object's datetime lies in the future.
26
- def future?
27
- self > ::DateTime.current
28
- end
29
-
30
13
  # Seconds since midnight: DateTime.now.seconds_since_midnight.
31
14
  def seconds_since_midnight
32
15
  sec + (min * 60) + (hour * 3600)
@@ -106,6 +89,16 @@ class DateTime
106
89
  alias :at_midnight :beginning_of_day
107
90
  alias :at_beginning_of_day :beginning_of_day
108
91
 
92
+ # Returns a new DateTime representing the middle of the day (12:00)
93
+ def middle_of_day
94
+ change(:hour => 12)
95
+ end
96
+ alias :midday :middle_of_day
97
+ alias :noon :middle_of_day
98
+ alias :at_midday :middle_of_day
99
+ alias :at_noon :middle_of_day
100
+ alias :at_middle_of_day :middle_of_day
101
+
109
102
  # Returns a new DateTime representing the end of the day (23:59:59).
110
103
  def end_of_day
111
104
  change(:hour => 23, :min => 59, :sec => 59)
@@ -158,13 +151,7 @@ class DateTime
158
151
  # Layers additional behavior on DateTime#<=> so that Time and
159
152
  # ActiveSupport::TimeWithZone instances can be compared with a DateTime.
160
153
  def <=>(other)
161
- if other.kind_of?(Infinity)
162
- super
163
- elsif other.respond_to? :to_datetime
164
- super other.to_datetime
165
- else
166
- nil
167
- end
154
+ super other.to_datetime
168
155
  end
169
156
 
170
157
  end
@@ -1,3 +1,4 @@
1
+ require 'date'
1
2
  require 'active_support/inflector/methods'
2
3
  require 'active_support/core_ext/time/conversions'
3
4
  require 'active_support/core_ext/date_time/calculations'
@@ -18,6 +19,7 @@ class DateTime
18
19
  # datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
19
20
  # datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
20
21
  # datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
22
+ # datetime.to_formatted_s(:iso8601) # => "2007-12-04T00:00:00+00:00"
21
23
  #
22
24
  # == Adding your own datetime formats to to_formatted_s
23
25
  # DateTime formats are shared with Time. You can add your own to the
@@ -1,24 +1,6 @@
1
- require 'active_support/core_ext/time/zones'
1
+ require 'date'
2
+ require 'active_support/core_ext/date_and_time/zones'
2
3
 
3
4
  class DateTime
4
- # Returns the simultaneous time in <tt>Time.zone</tt>.
5
- #
6
- # Time.zone = 'Hawaii' # => 'Hawaii'
7
- # DateTime.new(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
8
- #
9
- # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt>
10
- # as the local zone instead of the operating system's time zone.
11
- #
12
- # You can also pass in a TimeZone instance or string that identifies a TimeZone
13
- # as an argument, and the conversion will be based on that zone instead of
14
- # <tt>Time.zone</tt>.
15
- #
16
- # DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
17
- def in_time_zone(zone = ::Time.zone)
18
- if zone
19
- ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
20
- else
21
- self
22
- end
23
- end
5
+ include DateAndTime::Zones
24
6
  end
@@ -1,6 +1,5 @@
1
1
  require 'active_support/core_ext/hash/conversions'
2
2
  require 'active_support/core_ext/hash/deep_merge'
3
- require 'active_support/core_ext/hash/diff'
4
3
  require 'active_support/core_ext/hash/except'
5
4
  require 'active_support/core_ext/hash/indifferent_access'
6
5
  require 'active_support/core_ext/hash/keys'
@@ -10,7 +10,7 @@ require 'active_support/core_ext/string/inflections'
10
10
  class Hash
11
11
  # Returns a string containing an XML representation of its receiver:
12
12
  #
13
- # {'foo' => 1, 'bar' => 2}.to_xml
13
+ # { foo: 1, bar: 2 }.to_xml
14
14
  # # =>
15
15
  # # <?xml version="1.0" encoding="UTF-8"?>
16
16
  # # <hash>
@@ -43,7 +43,10 @@ class Hash
43
43
  # end
44
44
  #
45
45
  # { foo: Foo.new }.to_xml(skip_instruct: true)
46
- # # => "<hash><bar>fooing!</bar></hash>"
46
+ # # =>
47
+ # # <hash>
48
+ # # <bar>fooing!</bar>
49
+ # # </hash>
47
50
  #
48
51
  # * Otherwise, a node with +key+ as tag is created with a string representation of
49
52
  # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
@@ -201,7 +204,7 @@ module ActiveSupport
201
204
  end
202
205
 
203
206
  def become_empty_string?(value)
204
- # {"string" => true}
207
+ # { "string" => true }
205
208
  # No tests fail when the second term is removed.
206
209
  value['type'] == 'string' && value['nil'] != 'true'
207
210
  end
@@ -1,38 +1,27 @@
1
1
  class Hash
2
2
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
3
3
  #
4
- # h1 = { a: true, b: { c: [1, 2, 3] } }
5
- # h2 = { a: false, b: { x: [3, 4, 5] } }
4
+ # h1 = { x: { y: [4, 5, 6] }, z: [7, 8, 9] }
5
+ # h2 = { x: { y: [7, 8, 9] }, z: 'xyz' }
6
6
  #
7
- # h1.deep_merge(h2) #=> { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
8
- #
9
- # Like with Hash#merge in the standard library, a block can be provided
10
- # to merge values:
11
- #
12
- # h1 = { a: 100, b: 200, c: { c1: 100 } }
13
- # h2 = { b: 250, c: { c1: 200 } }
14
- # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
15
- # # => { a: 100, b: 450, c: { c1: 300 } }
7
+ # h1.deep_merge(h2) # => {x: {y: [7, 8, 9]}, z: "xyz"}
8
+ # h2.deep_merge(h1) # => {x: {y: [4, 5, 6]}, z: [7, 8, 9]}
9
+ # h1.deep_merge(h2) { |key, old, new| Array.wrap(old) + Array.wrap(new) }
10
+ # # => {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
16
11
  def deep_merge(other_hash, &block)
17
12
  dup.deep_merge!(other_hash, &block)
18
13
  end
19
14
 
20
15
  # Same as +deep_merge+, but modifies +self+.
21
16
  def deep_merge!(other_hash, &block)
22
- other_hash.each_pair do |current_key, other_value|
23
- this_value = self[current_key]
24
-
25
- self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
26
- this_value.deep_merge(other_value, &block)
17
+ other_hash.each_pair do |k,v|
18
+ tv = self[k]
19
+ if tv.is_a?(Hash) && v.is_a?(Hash)
20
+ self[k] = tv.deep_merge(v, &block)
27
21
  else
28
- if block_given? && key?(current_key)
29
- block.call(current_key, this_value, other_value)
30
- else
31
- other_value
32
- end
22
+ self[k] = block && tv ? block.call(k, tv, v) : v
33
23
  end
34
24
  end
35
-
36
25
  self
37
26
  end
38
27
  end
@@ -18,5 +18,6 @@ class Hash
18
18
  #
19
19
  # b = { b: 1 }
20
20
  # { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
21
+ # # => {"b"=>32}
21
22
  alias nested_under_indifferent_access with_indifferent_access
22
23
  end
@@ -4,7 +4,7 @@ class Hash
4
4
  # hash = { name: 'Rob', age: '28' }
5
5
  #
6
6
  # hash.transform_keys{ |key| key.to_s.upcase }
7
- # # => { "NAME" => "Rob", "AGE" => "28" }
7
+ # # => {"NAME"=>"Rob", "AGE"=>"28"}
8
8
  def transform_keys
9
9
  result = {}
10
10
  each_key do |key|
@@ -27,7 +27,7 @@ class Hash
27
27
  # hash = { name: 'Rob', age: '28' }
28
28
  #
29
29
  # hash.stringify_keys
30
- # #=> { "name" => "Rob", "age" => "28" }
30
+ # # => { "name" => "Rob", "age" => "28" }
31
31
  def stringify_keys
32
32
  transform_keys{ |key| key.to_s }
33
33
  end
@@ -44,7 +44,7 @@ class Hash
44
44
  # hash = { 'name' => 'Rob', 'age' => '28' }
45
45
  #
46
46
  # hash.symbolize_keys
47
- # #=> { name: "Rob", age: "28" }
47
+ # # => { name: "Rob", age: "28" }
48
48
  def symbolize_keys
49
49
  transform_keys{ |key| key.to_sym rescue key }
50
50
  end
@@ -61,100 +61,80 @@ class Hash
61
61
  # on a mismatch. Note that keys are NOT treated indifferently, meaning if you
62
62
  # use strings for keys but assert symbols as keys, this will fail.
63
63
  #
64
- # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
65
- # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: name"
64
+ # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
65
+ # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
66
66
  # { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
67
67
  def assert_valid_keys(*valid_keys)
68
68
  valid_keys.flatten!
69
69
  each_key do |k|
70
- raise ArgumentError.new("Unknown key: #{k}") unless valid_keys.include?(k)
70
+ unless valid_keys.include?(k)
71
+ raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
72
+ end
71
73
  end
72
74
  end
73
75
 
74
76
  # Return a new hash with all keys converted by the block operation.
75
77
  # This includes the keys from the root hash and from all
76
- # nested hashes and arrays.
78
+ # nested hashes.
77
79
  #
78
80
  # hash = { person: { name: 'Rob', age: '28' } }
79
81
  #
80
82
  # hash.deep_transform_keys{ |key| key.to_s.upcase }
81
- # # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
83
+ # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
82
84
  def deep_transform_keys(&block)
83
- _deep_transform_keys_in_object(self, &block)
85
+ result = {}
86
+ each do |key, value|
87
+ result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
88
+ end
89
+ result
84
90
  end
85
91
 
86
92
  # Destructively convert all keys by using the block operation.
87
93
  # This includes the keys from the root hash and from all
88
- # nested hashes and arrays.
94
+ # nested hashes.
89
95
  def deep_transform_keys!(&block)
90
- _deep_transform_keys_in_object!(self, &block)
96
+ keys.each do |key|
97
+ value = delete(key)
98
+ self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
99
+ end
100
+ self
91
101
  end
92
102
 
93
103
  # Return a new hash with all keys converted to strings.
94
104
  # This includes the keys from the root hash and from all
95
- # nested hashes and arrays.
105
+ # nested hashes.
96
106
  #
97
107
  # hash = { person: { name: 'Rob', age: '28' } }
98
108
  #
99
109
  # hash.deep_stringify_keys
100
- # # => { "person" => { "name" => "Rob", "age" => "28" } }
110
+ # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
101
111
  def deep_stringify_keys
102
112
  deep_transform_keys{ |key| key.to_s }
103
113
  end
104
114
 
105
115
  # Destructively convert all keys to strings.
106
116
  # This includes the keys from the root hash and from all
107
- # nested hashes and arrays.
117
+ # nested hashes.
108
118
  def deep_stringify_keys!
109
119
  deep_transform_keys!{ |key| key.to_s }
110
120
  end
111
121
 
112
122
  # Return a new hash with all keys converted to symbols, as long as
113
123
  # they respond to +to_sym+. This includes the keys from the root hash
114
- # and from all nested hashes and arrays.
124
+ # and from all nested hashes.
115
125
  #
116
126
  # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
117
127
  #
118
128
  # hash.deep_symbolize_keys
119
- # # => { person: { name: "Rob", age: "28" } }
129
+ # # => {:person=>{:name=>"Rob", :age=>"28"}}
120
130
  def deep_symbolize_keys
121
131
  deep_transform_keys{ |key| key.to_sym rescue key }
122
132
  end
123
133
 
124
134
  # Destructively convert all keys to symbols, as long as they respond
125
135
  # to +to_sym+. This includes the keys from the root hash and from all
126
- # nested hashes and arrays.
136
+ # nested hashes.
127
137
  def deep_symbolize_keys!
128
138
  deep_transform_keys!{ |key| key.to_sym rescue key }
129
139
  end
130
-
131
- private
132
- # support methods for deep transforming nested hashes and arrays
133
- def _deep_transform_keys_in_object(object, &block)
134
- case object
135
- when Hash
136
- object.each_with_object({}) do |(key, value), result|
137
- result[yield(key)] = _deep_transform_keys_in_object(value, &block)
138
- end
139
- when Array
140
- object.map {|e| _deep_transform_keys_in_object(e, &block) }
141
- else
142
- object
143
- end
144
- end
145
-
146
- def _deep_transform_keys_in_object!(object, &block)
147
- case object
148
- when Hash
149
- object.keys.each do |key|
150
- value = object.delete(key)
151
- object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
152
- end
153
- object
154
- when Array
155
- object.map! {|e| _deep_transform_keys_in_object!(e, &block)}
156
- else
157
- object
158
- end
159
- end
160
140
  end