activesupport 4.1.0 → 4.1.11
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 +209 -0
- data/lib/active_support/cache/strategy/local_cache.rb +1 -0
- data/lib/active_support/cache.rb +1 -1
- data/lib/active_support/callbacks.rb +118 -76
- data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -11
- data/lib/active_support/core_ext/hash/keys.rb +38 -16
- data/lib/active_support/core_ext/object/duplicable.rb +10 -0
- data/lib/active_support/core_ext/object/json.rb +3 -3
- data/lib/active_support/core_ext/object/to_param.rb +1 -62
- data/lib/active_support/core_ext/object/to_query.rb +58 -7
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +7 -3
- data/lib/active_support/core_ext/time/zones.rb +1 -0
- data/lib/active_support/dependencies.rb +4 -4
- data/lib/active_support/duration.rb +5 -1
- data/lib/active_support/gem_version.rb +1 -1
- data/lib/active_support/hash_with_indifferent_access.rb +2 -1
- data/lib/active_support/i18n_railtie.rb +5 -1
- data/lib/active_support/inflector/methods.rb +1 -1
- data/lib/active_support/json/encoding.rb +4 -0
- data/lib/active_support/multibyte/unicode.rb +0 -1
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +2 -6
- data/lib/active_support/option_merger.rb +1 -1
- data/lib/active_support/subscriber.rb +10 -1
- data/lib/active_support/values/time_zone.rb +5 -2
- data/lib/active_support/xml_mini/jdom.rb +6 -5
- data/lib/active_support/xml_mini/rexml.rb +6 -5
- data/lib/active_support/xml_mini.rb +3 -0
- metadata +3 -3
@@ -151,7 +151,9 @@ class DateTime
|
|
151
151
|
# Layers additional behavior on DateTime#<=> so that Time and
|
152
152
|
# ActiveSupport::TimeWithZone instances can be compared with a DateTime.
|
153
153
|
def <=>(other)
|
154
|
-
if other.
|
154
|
+
if other.kind_of?(Infinity)
|
155
|
+
super
|
156
|
+
elsif other.respond_to? :to_datetime
|
155
157
|
super other.to_datetime
|
156
158
|
else
|
157
159
|
nil
|
@@ -1,27 +1,38 @@
|
|
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 = { a: true, b: { c: [1, 2, 3] } }
|
5
|
+
# h2 = { a: false, b: { x: [3, 4, 5] } }
|
6
6
|
#
|
7
|
-
# h1.deep_merge(h2)
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
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 } }
|
11
16
|
def deep_merge(other_hash, &block)
|
12
17
|
dup.deep_merge!(other_hash, &block)
|
13
18
|
end
|
14
19
|
|
15
20
|
# Same as +deep_merge+, but modifies +self+.
|
16
21
|
def deep_merge!(other_hash, &block)
|
17
|
-
other_hash.each_pair do |
|
18
|
-
|
19
|
-
|
20
|
-
|
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)
|
21
27
|
else
|
22
|
-
|
28
|
+
if block_given? && key?(current_key)
|
29
|
+
block.call(current_key, this_value, other_value)
|
30
|
+
else
|
31
|
+
other_value
|
32
|
+
end
|
23
33
|
end
|
24
34
|
end
|
35
|
+
|
25
36
|
self
|
26
37
|
end
|
27
38
|
end
|
@@ -75,34 +75,26 @@ class Hash
|
|
75
75
|
|
76
76
|
# Returns a new hash with all keys converted by the block operation.
|
77
77
|
# This includes the keys from the root hash and from all
|
78
|
-
# nested hashes.
|
78
|
+
# nested hashes and arrays.
|
79
79
|
#
|
80
80
|
# hash = { person: { name: 'Rob', age: '28' } }
|
81
81
|
#
|
82
82
|
# hash.deep_transform_keys{ |key| key.to_s.upcase }
|
83
83
|
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
|
84
84
|
def deep_transform_keys(&block)
|
85
|
-
|
86
|
-
each do |key, value|
|
87
|
-
result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
|
88
|
-
end
|
89
|
-
result
|
85
|
+
_deep_transform_keys_in_object(self, &block)
|
90
86
|
end
|
91
87
|
|
92
88
|
# Destructively convert all keys by using the block operation.
|
93
89
|
# This includes the keys from the root hash and from all
|
94
|
-
# nested hashes.
|
90
|
+
# nested hashes and arrays.
|
95
91
|
def deep_transform_keys!(&block)
|
96
|
-
|
97
|
-
value = delete(key)
|
98
|
-
self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
|
99
|
-
end
|
100
|
-
self
|
92
|
+
_deep_transform_keys_in_object!(self, &block)
|
101
93
|
end
|
102
94
|
|
103
95
|
# Returns a new hash with all keys converted to strings.
|
104
96
|
# This includes the keys from the root hash and from all
|
105
|
-
# nested hashes.
|
97
|
+
# nested hashes and arrays.
|
106
98
|
#
|
107
99
|
# hash = { person: { name: 'Rob', age: '28' } }
|
108
100
|
#
|
@@ -114,14 +106,14 @@ class Hash
|
|
114
106
|
|
115
107
|
# Destructively convert all keys to strings.
|
116
108
|
# This includes the keys from the root hash and from all
|
117
|
-
# nested hashes.
|
109
|
+
# nested hashes and arrays.
|
118
110
|
def deep_stringify_keys!
|
119
111
|
deep_transform_keys!{ |key| key.to_s }
|
120
112
|
end
|
121
113
|
|
122
114
|
# Returns a new hash with all keys converted to symbols, as long as
|
123
115
|
# they respond to +to_sym+. This includes the keys from the root hash
|
124
|
-
# and from all nested hashes.
|
116
|
+
# and from all nested hashes and arrays.
|
125
117
|
#
|
126
118
|
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
|
127
119
|
#
|
@@ -133,8 +125,38 @@ class Hash
|
|
133
125
|
|
134
126
|
# Destructively convert all keys to symbols, as long as they respond
|
135
127
|
# to +to_sym+. This includes the keys from the root hash and from all
|
136
|
-
# nested hashes.
|
128
|
+
# nested hashes and arrays.
|
137
129
|
def deep_symbolize_keys!
|
138
130
|
deep_transform_keys!{ |key| key.to_sym rescue key }
|
139
131
|
end
|
132
|
+
|
133
|
+
private
|
134
|
+
# support methods for deep transforming nested hashes and arrays
|
135
|
+
def _deep_transform_keys_in_object(object, &block)
|
136
|
+
case object
|
137
|
+
when Hash
|
138
|
+
object.each_with_object({}) do |(key, value), result|
|
139
|
+
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
|
140
|
+
end
|
141
|
+
when Array
|
142
|
+
object.map {|e| _deep_transform_keys_in_object(e, &block) }
|
143
|
+
else
|
144
|
+
object
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def _deep_transform_keys_in_object!(object, &block)
|
149
|
+
case object
|
150
|
+
when Hash
|
151
|
+
object.keys.each do |key|
|
152
|
+
value = object.delete(key)
|
153
|
+
object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
|
154
|
+
end
|
155
|
+
object
|
156
|
+
when Array
|
157
|
+
object.map! {|e| _deep_transform_keys_in_object!(e, &block)}
|
158
|
+
else
|
159
|
+
object
|
160
|
+
end
|
161
|
+
end
|
140
162
|
end
|
@@ -88,3 +88,13 @@ class BigDecimal
|
|
88
88
|
# can't dup, so use superclass implementation
|
89
89
|
end
|
90
90
|
end
|
91
|
+
|
92
|
+
class Method
|
93
|
+
# Methods are not duplicable:
|
94
|
+
#
|
95
|
+
# method(:puts).duplicable? # => false
|
96
|
+
# method(:puts).dup # => TypeError: allocator undefined for Method
|
97
|
+
def duplicable?
|
98
|
+
false
|
99
|
+
end
|
100
|
+
end
|
@@ -162,7 +162,7 @@ end
|
|
162
162
|
|
163
163
|
class Time
|
164
164
|
def as_json(options = nil) #:nodoc:
|
165
|
-
if ActiveSupport.use_standard_json_time_format
|
165
|
+
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
|
166
166
|
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
|
167
167
|
else
|
168
168
|
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
|
@@ -172,7 +172,7 @@ end
|
|
172
172
|
|
173
173
|
class Date
|
174
174
|
def as_json(options = nil) #:nodoc:
|
175
|
-
if ActiveSupport.use_standard_json_time_format
|
175
|
+
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
|
176
176
|
strftime("%Y-%m-%d")
|
177
177
|
else
|
178
178
|
strftime("%Y/%m/%d")
|
@@ -182,7 +182,7 @@ end
|
|
182
182
|
|
183
183
|
class DateTime
|
184
184
|
def as_json(options = nil) #:nodoc:
|
185
|
-
if ActiveSupport.use_standard_json_time_format
|
185
|
+
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
|
186
186
|
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
|
187
187
|
else
|
188
188
|
strftime('%Y/%m/%d %H:%M:%S %z')
|
@@ -1,62 +1 @@
|
|
1
|
-
|
2
|
-
# Alias of <tt>to_s</tt>.
|
3
|
-
def to_param
|
4
|
-
to_s
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
class NilClass
|
9
|
-
# Returns +self+.
|
10
|
-
def to_param
|
11
|
-
self
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class TrueClass
|
16
|
-
# Returns +self+.
|
17
|
-
def to_param
|
18
|
-
self
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class FalseClass
|
23
|
-
# Returns +self+.
|
24
|
-
def to_param
|
25
|
-
self
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class Array
|
30
|
-
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
31
|
-
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
32
|
-
def to_param
|
33
|
-
collect { |e| e.to_param }.join '/'
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
class Hash
|
38
|
-
# Returns a string representation of the receiver suitable for use as a URL
|
39
|
-
# query string:
|
40
|
-
#
|
41
|
-
# {name: 'David', nationality: 'Danish'}.to_param
|
42
|
-
# # => "name=David&nationality=Danish"
|
43
|
-
#
|
44
|
-
# An optional namespace can be passed to enclose the param names:
|
45
|
-
#
|
46
|
-
# {name: 'David', nationality: 'Danish'}.to_param('user')
|
47
|
-
# # => "user[name]=David&user[nationality]=Danish"
|
48
|
-
#
|
49
|
-
# The string pairs "key=value" that conform the query string
|
50
|
-
# are sorted lexicographically in ascending order.
|
51
|
-
#
|
52
|
-
# This method is also aliased as +to_query+.
|
53
|
-
def to_param(namespace = nil)
|
54
|
-
if empty?
|
55
|
-
namespace ? nil.to_query(namespace) : ''
|
56
|
-
else
|
57
|
-
collect do |key, value|
|
58
|
-
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
59
|
-
end.sort! * '&'
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
1
|
+
require 'active_support/core_ext/object/to_query'
|
@@ -1,17 +1,45 @@
|
|
1
|
-
require 'active_support/core_ext/object/to_param'
|
2
|
-
|
3
1
|
class Object
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
# Alias of <tt>to_s</tt>.
|
3
|
+
def to_param
|
4
|
+
to_s
|
5
|
+
end
|
6
|
+
|
7
|
+
# Converts an object into a string suitable for use as a URL query string,
|
8
|
+
# using the given <tt>key</tt> as the param name.
|
8
9
|
def to_query(key)
|
9
10
|
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
10
11
|
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
15
|
+
class NilClass
|
16
|
+
# Returns +self+.
|
17
|
+
def to_param
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class TrueClass
|
23
|
+
# Returns +self+.
|
24
|
+
def to_param
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class FalseClass
|
30
|
+
# Returns +self+.
|
31
|
+
def to_param
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
14
36
|
class Array
|
37
|
+
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
38
|
+
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
39
|
+
def to_param
|
40
|
+
collect { |e| e.to_param }.join '/'
|
41
|
+
end
|
42
|
+
|
15
43
|
# Converts an array into a string suitable for use as a URL query string,
|
16
44
|
# using the given +key+ as the param name.
|
17
45
|
#
|
@@ -28,5 +56,28 @@ class Array
|
|
28
56
|
end
|
29
57
|
|
30
58
|
class Hash
|
31
|
-
|
59
|
+
# Returns a string representation of the receiver suitable for use as a URL
|
60
|
+
# query string:
|
61
|
+
#
|
62
|
+
# {name: 'David', nationality: 'Danish'}.to_query
|
63
|
+
# # => "name=David&nationality=Danish"
|
64
|
+
#
|
65
|
+
# An optional namespace can be passed to enclose key names:
|
66
|
+
#
|
67
|
+
# {name: 'David', nationality: 'Danish'}.to_query('user')
|
68
|
+
# # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
|
69
|
+
#
|
70
|
+
# The string pairs "key=value" that conform the query string
|
71
|
+
# are sorted lexicographically in ascending order.
|
72
|
+
#
|
73
|
+
# This method is also aliased as +to_param+.
|
74
|
+
def to_query(namespace = nil)
|
75
|
+
collect do |key, value|
|
76
|
+
unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
|
77
|
+
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
78
|
+
end
|
79
|
+
end.compact.sort! * '&'
|
80
|
+
end
|
81
|
+
|
82
|
+
alias_method :to_param, :to_query
|
32
83
|
end
|
@@ -3,7 +3,7 @@ class String
|
|
3
3
|
# the string, and then changing remaining consecutive whitespace
|
4
4
|
# groups into one space each.
|
5
5
|
#
|
6
|
-
# Note that it handles both ASCII and Unicode whitespace
|
6
|
+
# Note that it handles both ASCII and Unicode whitespace.
|
7
7
|
#
|
8
8
|
# %{ Multi-line
|
9
9
|
# string }.squish # => "Multi-line string"
|
@@ -6,7 +6,7 @@ class ERB
|
|
6
6
|
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' }
|
7
7
|
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003e', '<' => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
|
8
8
|
HTML_ESCAPE_REGEXP = /[&"'><]/
|
9
|
-
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
9
|
+
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
|
10
10
|
JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
|
11
11
|
|
12
12
|
# A utility method for escaping HTML tag characters.
|
@@ -142,7 +142,11 @@ module ActiveSupport #:nodoc:
|
|
142
142
|
else
|
143
143
|
if html_safe?
|
144
144
|
new_safe_buffer = super
|
145
|
-
|
145
|
+
|
146
|
+
if new_safe_buffer
|
147
|
+
new_safe_buffer.instance_eval { @html_safe = true }
|
148
|
+
end
|
149
|
+
|
146
150
|
new_safe_buffer
|
147
151
|
else
|
148
152
|
to_str[*args]
|
@@ -206,7 +210,7 @@ module ActiveSupport #:nodoc:
|
|
206
210
|
end
|
207
211
|
|
208
212
|
def encode_with(coder)
|
209
|
-
coder.
|
213
|
+
coder.represent_object nil, to_str
|
210
214
|
end
|
211
215
|
|
212
216
|
UNSAFE_STRING_METHODS.each do |unsafe_method|
|
@@ -186,7 +186,7 @@ module ActiveSupport #:nodoc:
|
|
186
186
|
# and we assume therefore the user wants to refer to a top-level constant.
|
187
187
|
def guess_for_anonymous(const_name)
|
188
188
|
if Object.const_defined?(const_name)
|
189
|
-
raise NameError
|
189
|
+
raise NameError.new "#{const_name} cannot be autoloaded from an anonymous class or module", const_name
|
190
190
|
else
|
191
191
|
Object
|
192
192
|
end
|
@@ -515,9 +515,9 @@ module ActiveSupport #:nodoc:
|
|
515
515
|
end
|
516
516
|
end
|
517
517
|
|
518
|
-
|
519
|
-
|
520
|
-
|
518
|
+
name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
|
519
|
+
name_error.set_backtrace(caller.reject {|l| l.starts_with? __FILE__ })
|
520
|
+
raise name_error
|
521
521
|
end
|
522
522
|
|
523
523
|
# Remove the constants that have been autoloaded, and those that have been
|
@@ -49,6 +49,10 @@ module ActiveSupport
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
def eql?(other)
|
53
|
+
other.is_a?(Duration) && self == other
|
54
|
+
end
|
55
|
+
|
52
56
|
def self.===(other) #:nodoc:
|
53
57
|
other.is_a?(Duration)
|
54
58
|
rescue ::NoMethodError
|
@@ -74,7 +78,7 @@ module ActiveSupport
|
|
74
78
|
reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
|
75
79
|
sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}.
|
76
80
|
map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
|
77
|
-
to_sentence(:
|
81
|
+
to_sentence(locale: ::I18n.default_locale)
|
78
82
|
end
|
79
83
|
|
80
84
|
def as_json(options = nil) #:nodoc:
|