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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +283 -508
- data/README.rdoc +1 -1
- data/lib/active_support.rb +7 -1
- data/lib/active_support/backtrace_cleaner.rb +5 -5
- data/lib/active_support/benchmarkable.rb +0 -10
- data/lib/active_support/cache.rb +62 -26
- data/lib/active_support/cache/file_store.rb +27 -22
- data/lib/active_support/cache/mem_cache_store.rb +2 -2
- data/lib/active_support/cache/memory_store.rb +1 -0
- data/lib/active_support/cache/strategy/local_cache.rb +3 -0
- data/lib/active_support/callbacks.rb +416 -245
- data/lib/active_support/concern.rb +13 -5
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/array/access.rb +2 -0
- data/lib/active_support/core_ext/array/conversions.rb +2 -17
- data/lib/active_support/core_ext/array/grouping.rb +24 -12
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/class/attribute.rb +1 -2
- data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
- data/lib/active_support/core_ext/date/calculations.rb +10 -0
- data/lib/active_support/core_ext/date/conversions.rb +5 -6
- data/lib/active_support/core_ext/date/zones.rb +2 -33
- data/lib/active_support/core_ext/date_and_time/calculations.rb +30 -11
- data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +12 -25
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
- data/lib/active_support/core_ext/date_time/zones.rb +3 -21
- data/lib/active_support/core_ext/hash.rb +0 -1
- data/lib/active_support/core_ext/hash/conversions.rb +6 -3
- data/lib/active_support/core_ext/hash/deep_merge.rb +11 -22
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
- data/lib/active_support/core_ext/hash/keys.rb +27 -47
- data/lib/active_support/core_ext/kernel/reporting.rb +2 -6
- data/lib/active_support/core_ext/module.rb +1 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
- data/lib/active_support/core_ext/module/concerning.rb +135 -0
- data/lib/active_support/core_ext/module/delegation.rb +14 -4
- data/lib/active_support/core_ext/module/deprecation.rb +0 -2
- data/lib/active_support/core_ext/module/introspection.rb +0 -16
- data/lib/active_support/core_ext/module/method_transplanting.rb +11 -0
- data/lib/active_support/core_ext/numeric/time.rb +8 -0
- data/lib/active_support/core_ext/object.rb +1 -1
- data/lib/active_support/core_ext/object/blank.rb +1 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
- data/lib/active_support/core_ext/object/inclusion.rb +4 -15
- data/lib/active_support/core_ext/object/json.rb +197 -0
- data/lib/active_support/core_ext/object/to_json.rb +4 -26
- data/lib/active_support/core_ext/object/to_param.rb +58 -1
- data/lib/active_support/core_ext/object/to_query.rb +7 -56
- data/lib/active_support/core_ext/object/try.rb +1 -1
- data/lib/active_support/core_ext/range/each.rb +2 -1
- data/lib/active_support/core_ext/string/access.rb +31 -31
- data/lib/active_support/core_ext/string/conversions.rb +9 -8
- data/lib/active_support/core_ext/string/exclude.rb +3 -3
- data/lib/active_support/core_ext/string/filters.rb +14 -4
- data/lib/active_support/core_ext/string/inflections.rb +11 -9
- data/lib/active_support/core_ext/string/output_safety.rb +65 -24
- data/lib/active_support/core_ext/string/zones.rb +1 -0
- data/lib/active_support/core_ext/thread.rb +4 -4
- data/lib/active_support/core_ext/time/calculations.rb +10 -57
- data/lib/active_support/core_ext/time/conversions.rb +3 -1
- data/lib/active_support/core_ext/time/zones.rb +2 -21
- data/lib/active_support/dependencies.rb +29 -13
- data/lib/active_support/deprecation.rb +4 -4
- data/lib/active_support/deprecation/behaviors.rb +3 -3
- data/lib/active_support/duration.rb +5 -7
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/hash_with_indifferent_access.rb +4 -9
- data/lib/active_support/i18n.rb +4 -4
- data/lib/active_support/i18n_railtie.rb +2 -6
- data/lib/active_support/inflections.rb +0 -1
- data/lib/active_support/inflector/inflections.rb +17 -17
- data/lib/active_support/inflector/methods.rb +34 -17
- data/lib/active_support/json/decoding.rb +14 -21
- data/lib/active_support/json/encoding.rb +113 -285
- data/lib/active_support/key_generator.rb +1 -1
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber/test_helper.rb +1 -1
- data/lib/active_support/logger.rb +1 -1
- data/lib/active_support/message_encryptor.rb +3 -3
- data/lib/active_support/message_verifier.rb +6 -1
- data/lib/active_support/multibyte/chars.rb +1 -2
- data/lib/active_support/multibyte/unicode.rb +27 -39
- data/lib/active_support/notifications.rb +3 -3
- data/lib/active_support/notifications/instrumenter.rb +2 -1
- data/lib/active_support/number_helper.rb +20 -311
- data/lib/active_support/number_helper/number_converter.rb +182 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +21 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +62 -0
- data/lib/active_support/option_merger.rb +1 -1
- data/lib/active_support/ordered_hash.rb +0 -8
- data/lib/active_support/ordered_options.rb +8 -0
- data/lib/active_support/per_thread_registry.rb +9 -8
- data/lib/active_support/subscriber.rb +26 -3
- data/lib/active_support/test_case.rb +9 -10
- data/lib/active_support/testing/assertions.rb +0 -30
- data/lib/active_support/testing/autorun.rb +2 -2
- data/lib/active_support/testing/declarative.rb +18 -8
- data/lib/active_support/testing/isolation.rb +13 -65
- data/lib/active_support/testing/setup_and_teardown.rb +17 -2
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +55 -0
- data/lib/active_support/time_with_zone.rb +4 -4
- data/lib/active_support/values/time_zone.rb +18 -15
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini.rb +2 -4
- metadata +71 -61
- data/lib/active_support/basic_object.rb +0 -11
- data/lib/active_support/buffered_logger.rb +0 -21
- data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
- data/lib/active_support/core_ext/hash/diff.rb +0 -14
- data/lib/active_support/core_ext/logger.rb +0 -67
- data/lib/active_support/core_ext/proc.rb +0 -17
- data/lib/active_support/core_ext/string/encoding.rb +0 -8
- data/lib/active_support/json/variable.rb +0 -18
- 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
|
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
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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 '
|
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
|
-
|
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 '
|
1
|
+
require 'date'
|
2
|
+
require 'active_support/core_ext/date_and_time/zones'
|
2
3
|
|
3
4
|
class DateTime
|
4
|
-
|
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
|
-
# {
|
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
|
-
# # =>
|
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 = {
|
5
|
-
# h2 = {
|
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)
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
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 |
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
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
|
@@ -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
|
-
# # => {
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
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
|
-
# # => {
|
83
|
+
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
|
82
84
|
def deep_transform_keys(&block)
|
83
|
-
|
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
|
94
|
+
# nested hashes.
|
89
95
|
def deep_transform_keys!(&block)
|
90
|
-
|
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
|
105
|
+
# nested hashes.
|
96
106
|
#
|
97
107
|
# hash = { person: { name: 'Rob', age: '28' } }
|
98
108
|
#
|
99
109
|
# hash.deep_stringify_keys
|
100
|
-
# # => {
|
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
|
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
|
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
|
-
# # => {
|
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
|
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
|