activesupport 4.1.0.beta2 → 4.1.0.rc1
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 +166 -8
- data/MIT-LICENSE +1 -1
- data/lib/active_support.rb +1 -1
- data/lib/active_support/cache.rb +2 -2
- data/lib/active_support/cache/mem_cache_store.rb +6 -8
- data/lib/active_support/cache/strategy/local_cache.rb +8 -2
- data/lib/active_support/callbacks.rb +1 -1
- data/lib/active_support/configurable.rb +1 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -15
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +14 -0
- data/lib/active_support/core_ext/class/delegating_attributes.rb +9 -8
- data/lib/active_support/core_ext/date/conversions.rb +5 -2
- data/lib/active_support/core_ext/date_time/calculations.rb +5 -1
- data/lib/active_support/core_ext/enumerable.rb +1 -1
- data/lib/active_support/core_ext/hash.rb +1 -0
- data/lib/active_support/core_ext/hash/compact.rb +20 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +6 -6
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -1
- data/lib/active_support/core_ext/module/concerning.rb +1 -1
- data/lib/active_support/core_ext/module/delegation.rb +22 -20
- data/lib/active_support/core_ext/object/blank.rb +43 -17
- data/lib/active_support/core_ext/object/inclusion.rb +12 -0
- data/lib/active_support/core_ext/object/json.rb +4 -4
- data/lib/active_support/core_ext/object/to_param.rb +7 -3
- data/lib/active_support/core_ext/object/to_query.rb +6 -1
- data/lib/active_support/core_ext/range/each.rb +1 -2
- data/lib/active_support/core_ext/string/access.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +12 -12
- data/lib/active_support/core_ext/thread.rb +1 -1
- data/lib/active_support/core_ext/time/calculations.rb +6 -4
- data/lib/active_support/dependencies.rb +10 -1
- data/lib/active_support/hash_with_indifferent_access.rb +7 -3
- data/lib/active_support/i18n_railtie.rb +1 -1
- data/lib/active_support/inflections.rb +6 -0
- data/lib/active_support/inflector/methods.rb +1 -1
- data/lib/active_support/json/encoding.rb +7 -1
- data/lib/active_support/key_generator.rb +7 -9
- data/lib/active_support/message_encryptor.rb +1 -1
- data/lib/active_support/multibyte/unicode.rb +36 -36
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +37 -7
- data/lib/active_support/ordered_hash.rb +4 -0
- data/lib/active_support/testing/isolation.rb +2 -0
- data/lib/active_support/testing/time_helpers.rb +84 -12
- data/lib/active_support/time_with_zone.rb +8 -7
- data/lib/active_support/values/time_zone.rb +9 -21
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini.rb +4 -2
- metadata +3 -1
@@ -0,0 +1,20 @@
|
|
1
|
+
class Hash
|
2
|
+
# Returns a hash with non +nil+ values.
|
3
|
+
#
|
4
|
+
# hash = { a: true, b: false, c: nil}
|
5
|
+
# hash.compact # => { a: true, b: false}
|
6
|
+
# hash # => { a: true, b: false, c: nil}
|
7
|
+
# { c: nil }.compact # => {}
|
8
|
+
def compact
|
9
|
+
self.select { |_, value| !value.nil? }
|
10
|
+
end
|
11
|
+
|
12
|
+
# Replaces current hash with non +nil+ values.
|
13
|
+
#
|
14
|
+
# hash = { a: true, b: false, c: nil}
|
15
|
+
# hash.compact! # => { a: true, b: false}
|
16
|
+
# hash # => { a: true, b: false}
|
17
|
+
def compact!
|
18
|
+
self.reject! { |_, value| value.nil? }
|
19
|
+
end
|
20
|
+
end
|
@@ -105,7 +105,7 @@ class Hash
|
|
105
105
|
# hash = Hash.from_xml(xml)
|
106
106
|
# # => {"hash"=>{"foo"=>1, "bar"=>2}}
|
107
107
|
#
|
108
|
-
# DisallowedType is
|
108
|
+
# DisallowedType is raised if the XML contains attributes with <tt>type="yaml"</tt> or
|
109
109
|
# <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML.
|
110
110
|
def from_xml(xml, disallowed_types = nil)
|
111
111
|
ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Hash
|
2
|
-
#
|
2
|
+
# Returns a hash that includes everything but the given keys. This is useful for
|
3
3
|
# limiting a set of parameters to everything but a few known toggles:
|
4
4
|
#
|
5
5
|
# @person.update(params[:person].except(:admin))
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Hash
|
2
|
-
#
|
2
|
+
# Returns a new hash with all keys converted using the block operation.
|
3
3
|
#
|
4
4
|
# hash = { name: 'Rob', age: '28' }
|
5
5
|
#
|
@@ -22,7 +22,7 @@ class Hash
|
|
22
22
|
self
|
23
23
|
end
|
24
24
|
|
25
|
-
#
|
25
|
+
# Returns a new hash with all keys converted to strings.
|
26
26
|
#
|
27
27
|
# hash = { name: 'Rob', age: '28' }
|
28
28
|
#
|
@@ -38,7 +38,7 @@ class Hash
|
|
38
38
|
transform_keys!{ |key| key.to_s }
|
39
39
|
end
|
40
40
|
|
41
|
-
#
|
41
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
42
42
|
# they respond to +to_sym+.
|
43
43
|
#
|
44
44
|
# hash = { 'name' => 'Rob', 'age' => '28' }
|
@@ -73,7 +73,7 @@ class Hash
|
|
73
73
|
end
|
74
74
|
end
|
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
78
|
# nested hashes.
|
79
79
|
#
|
@@ -100,7 +100,7 @@ class Hash
|
|
100
100
|
self
|
101
101
|
end
|
102
102
|
|
103
|
-
#
|
103
|
+
# Returns a new hash with all keys converted to strings.
|
104
104
|
# This includes the keys from the root hash and from all
|
105
105
|
# nested hashes.
|
106
106
|
#
|
@@ -119,7 +119,7 @@ class Hash
|
|
119
119
|
deep_transform_keys!{ |key| key.to_s }
|
120
120
|
end
|
121
121
|
|
122
|
-
#
|
122
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
123
123
|
# they respond to +to_sym+. This includes the keys from the root hash
|
124
124
|
# and from all nested hashes.
|
125
125
|
#
|
@@ -27,7 +27,8 @@ class Module
|
|
27
27
|
|
28
28
|
def attr_internal_define(attr_name, type)
|
29
29
|
internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
|
30
|
-
|
30
|
+
# class_eval is necessary on 1.9 or else the methods are made private
|
31
|
+
class_eval do
|
31
32
|
# use native attr_* methods as they are faster on some Ruby implementations
|
32
33
|
send("attr_#{type}", internal_name)
|
33
34
|
end
|
@@ -63,7 +63,7 @@ class Module
|
|
63
63
|
#
|
64
64
|
# == Mix-in noise exiled to its own file:
|
65
65
|
#
|
66
|
-
# Once our chunk of behavior starts pushing the scroll-to-understand it
|
66
|
+
# Once our chunk of behavior starts pushing the scroll-to-understand it's
|
67
67
|
# boundary, we give in and move it to a separate file. At this size, the
|
68
68
|
# overhead feels in good proportion to the size of our extraction, despite
|
69
69
|
# diluting our at-a-glance sense of how things really work.
|
@@ -178,30 +178,32 @@ class Module
|
|
178
178
|
# whereas conceptually, from the user point of view, the delegator should
|
179
179
|
# be doing one call.
|
180
180
|
if allow_nil
|
181
|
-
|
182
|
-
def #{method_prefix}#{method}(#{definition})
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
181
|
+
method_def = [
|
182
|
+
"def #{method_prefix}#{method}(#{definition})", # def customer_name(*args, &block)
|
183
|
+
"_ = #{to}", # _ = client
|
184
|
+
"if !_.nil? || nil.respond_to?(:#{method})", # if !_.nil? || nil.respond_to?(:name)
|
185
|
+
" _.#{method}(#{definition})", # _.name(*args, &block)
|
186
|
+
"end", # end
|
187
|
+
"end" # end
|
188
|
+
].join ';'
|
189
189
|
else
|
190
190
|
exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
|
191
191
|
|
192
|
-
|
193
|
-
def #{method_prefix}#{method}(#{definition})
|
194
|
-
|
195
|
-
|
196
|
-
rescue NoMethodError => e
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
end
|
203
|
-
|
192
|
+
method_def = [
|
193
|
+
"def #{method_prefix}#{method}(#{definition})", # def customer_name(*args, &block)
|
194
|
+
" _ = #{to}", # _ = client
|
195
|
+
" _.#{method}(#{definition})", # _.name(*args, &block)
|
196
|
+
"rescue NoMethodError => e", # rescue NoMethodError => e
|
197
|
+
" if _.nil? && e.name == :#{method}", # if _.nil? && e.name == :name
|
198
|
+
" #{exception}", # # add helpful message to the exception
|
199
|
+
" else", # else
|
200
|
+
" raise", # raise
|
201
|
+
" end", # end
|
202
|
+
"end" # end
|
203
|
+
].join ';'
|
204
204
|
end
|
205
|
+
|
206
|
+
module_eval(method_def, file, line)
|
205
207
|
end
|
206
208
|
end
|
207
209
|
end
|
@@ -4,36 +4,42 @@ class Object
|
|
4
4
|
# An object is blank if it's false, empty, or a whitespace string.
|
5
5
|
# For example, '', ' ', +nil+, [], and {} are all blank.
|
6
6
|
#
|
7
|
-
# This simplifies
|
7
|
+
# This simplifies
|
8
8
|
#
|
9
|
-
#
|
9
|
+
# address.nil? || address.empty?
|
10
10
|
#
|
11
|
-
#
|
11
|
+
# to
|
12
12
|
#
|
13
|
-
#
|
13
|
+
# address.blank?
|
14
|
+
#
|
15
|
+
# @return [true, false]
|
14
16
|
def blank?
|
15
|
-
respond_to?(:empty?) ? empty? : !self
|
17
|
+
respond_to?(:empty?) ? !!empty? : !self
|
16
18
|
end
|
17
19
|
|
18
|
-
# An object is present if it's not
|
20
|
+
# An object is present if it's not blank.
|
21
|
+
#
|
22
|
+
# @return [true, false]
|
19
23
|
def present?
|
20
24
|
!blank?
|
21
25
|
end
|
22
26
|
|
23
|
-
# Returns
|
24
|
-
# <tt>object.presence</tt> is equivalent to
|
27
|
+
# Returns the receiver if it's present otherwise returns +nil+.
|
28
|
+
# <tt>object.presence</tt> is equivalent to
|
25
29
|
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
30
|
+
# object.present? ? object : nil
|
31
|
+
#
|
32
|
+
# For example, something like
|
29
33
|
#
|
30
34
|
# state = params[:state] if params[:state].present?
|
31
35
|
# country = params[:country] if params[:country].present?
|
32
36
|
# region = state || country || 'US'
|
33
37
|
#
|
34
|
-
#
|
38
|
+
# becomes
|
35
39
|
#
|
36
40
|
# region = params[:state].presence || params[:country].presence || 'US'
|
41
|
+
#
|
42
|
+
# @return [Object]
|
37
43
|
def presence
|
38
44
|
self if present?
|
39
45
|
end
|
@@ -43,6 +49,8 @@ class NilClass
|
|
43
49
|
# +nil+ is blank:
|
44
50
|
#
|
45
51
|
# nil.blank? # => true
|
52
|
+
#
|
53
|
+
# @return [true]
|
46
54
|
def blank?
|
47
55
|
true
|
48
56
|
end
|
@@ -52,6 +60,8 @@ class FalseClass
|
|
52
60
|
# +false+ is blank:
|
53
61
|
#
|
54
62
|
# false.blank? # => true
|
63
|
+
#
|
64
|
+
# @return [true]
|
55
65
|
def blank?
|
56
66
|
true
|
57
67
|
end
|
@@ -61,6 +71,8 @@ class TrueClass
|
|
61
71
|
# +true+ is not blank:
|
62
72
|
#
|
63
73
|
# true.blank? # => false
|
74
|
+
#
|
75
|
+
# @return [false]
|
64
76
|
def blank?
|
65
77
|
false
|
66
78
|
end
|
@@ -71,6 +83,8 @@ class Array
|
|
71
83
|
#
|
72
84
|
# [].blank? # => true
|
73
85
|
# [1,2,3].blank? # => false
|
86
|
+
#
|
87
|
+
# @return [true, false]
|
74
88
|
alias_method :blank?, :empty?
|
75
89
|
end
|
76
90
|
|
@@ -79,18 +93,28 @@ class Hash
|
|
79
93
|
#
|
80
94
|
# {}.blank? # => true
|
81
95
|
# { key: 'value' }.blank? # => false
|
96
|
+
#
|
97
|
+
# @return [true, false]
|
82
98
|
alias_method :blank?, :empty?
|
83
99
|
end
|
84
100
|
|
85
101
|
class String
|
102
|
+
BLANK_RE = /\A[[:space:]]*\z/
|
103
|
+
|
86
104
|
# A string is blank if it's empty or contains whitespaces only:
|
87
105
|
#
|
88
|
-
# ''.blank?
|
89
|
-
# ' '.blank?
|
90
|
-
#
|
91
|
-
# '
|
106
|
+
# ''.blank? # => true
|
107
|
+
# ' '.blank? # => true
|
108
|
+
# "\t\n\r".blank? # => true
|
109
|
+
# ' blah '.blank? # => false
|
110
|
+
#
|
111
|
+
# Unicode whitespace is supported:
|
112
|
+
#
|
113
|
+
# "\u00a0".blank? # => true
|
114
|
+
#
|
115
|
+
# @return [true, false]
|
92
116
|
def blank?
|
93
|
-
|
117
|
+
BLANK_RE === self
|
94
118
|
end
|
95
119
|
end
|
96
120
|
|
@@ -99,6 +123,8 @@ class Numeric #:nodoc:
|
|
99
123
|
#
|
100
124
|
# 1.blank? # => false
|
101
125
|
# 0.blank? # => false
|
126
|
+
#
|
127
|
+
# @return [false]
|
102
128
|
def blank?
|
103
129
|
false
|
104
130
|
end
|
@@ -12,4 +12,16 @@ class Object
|
|
12
12
|
rescue NoMethodError
|
13
13
|
raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
|
14
14
|
end
|
15
|
+
|
16
|
+
# Returns the receiver if it's included in the argument otherwise returns +nil+.
|
17
|
+
# Argument must be any object which responds to +#include?+. Usage:
|
18
|
+
#
|
19
|
+
# params[:bucket_type].present_in %w( project calendar )
|
20
|
+
#
|
21
|
+
# This will throw an ArgumentError if the argument doesn't respond to +#include?+.
|
22
|
+
#
|
23
|
+
# @return [Object]
|
24
|
+
def present_in(another_object)
|
25
|
+
self.in?(another_object) ? self : nil
|
26
|
+
end
|
15
27
|
end
|
@@ -16,12 +16,12 @@ require 'active_support/core_ext/module/aliasing'
|
|
16
16
|
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
|
17
17
|
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
|
18
18
|
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
|
19
|
-
#
|
19
|
+
#
|
20
20
|
# On the other hand, we should avoid conflict with ::JSON.{generate,dump}(obj). Unfortunately, the
|
21
21
|
# JSON gem's encoder relies on its own to_json implementation to encode objects. Since it always
|
22
22
|
# passes a ::JSON::State object as the only argument to to_json, we can detect that and forward the
|
23
23
|
# calls to the original to_json method.
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
|
26
26
|
# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
|
27
27
|
# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
|
@@ -163,7 +163,7 @@ end
|
|
163
163
|
class Time
|
164
164
|
def as_json(options = nil) #:nodoc:
|
165
165
|
if ActiveSupport.use_standard_json_time_format
|
166
|
-
xmlschema(
|
166
|
+
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
|
167
167
|
else
|
168
168
|
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
|
169
169
|
end
|
@@ -183,7 +183,7 @@ end
|
|
183
183
|
class DateTime
|
184
184
|
def as_json(options = nil) #:nodoc:
|
185
185
|
if ActiveSupport.use_standard_json_time_format
|
186
|
-
xmlschema(
|
186
|
+
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
|
187
187
|
else
|
188
188
|
strftime('%Y/%m/%d %H:%M:%S %z')
|
189
189
|
end
|
@@ -51,8 +51,12 @@ class Hash
|
|
51
51
|
#
|
52
52
|
# This method is also aliased as +to_query+.
|
53
53
|
def to_param(namespace = nil)
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
57
61
|
end
|
58
62
|
end
|
@@ -18,7 +18,12 @@ class Array
|
|
18
18
|
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
|
19
19
|
def to_query(key)
|
20
20
|
prefix = "#{key}[]"
|
21
|
-
|
21
|
+
|
22
|
+
if empty?
|
23
|
+
nil.to_query(prefix)
|
24
|
+
else
|
25
|
+
collect { |value| value.to_query(prefix) }.join '&'
|
26
|
+
end
|
22
27
|
end
|
23
28
|
end
|
24
29
|
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'active_support/core_ext/module/aliasing'
|
2
|
-
require 'active_support/core_ext/object/acts_like'
|
3
2
|
|
4
3
|
class Range #:nodoc:
|
5
4
|
|
@@ -17,7 +16,7 @@ class Range #:nodoc:
|
|
17
16
|
|
18
17
|
private
|
19
18
|
def ensure_iteration_allowed
|
20
|
-
if first.
|
19
|
+
if first.is_a?(Time)
|
21
20
|
raise TypeError, "can't iterate from #{first.class}"
|
22
21
|
end
|
23
22
|
end
|
@@ -70,9 +70,20 @@ class ERB
|
|
70
70
|
# them inside a script tag to avoid XSS vulnerability:
|
71
71
|
#
|
72
72
|
# <script>
|
73
|
-
# var currentUser = <%= json_escape
|
73
|
+
# var currentUser = <%= raw json_escape(current_user.to_json) %>;
|
74
74
|
# </script>
|
75
75
|
#
|
76
|
+
# It is necessary to +raw+ the result of +json_escape+, so that quotation marks
|
77
|
+
# don't get converted to <tt>"</tt> entities. +json_escape+ doesn't
|
78
|
+
# automatically flag the result as HTML safe, since the raw value is unsafe to
|
79
|
+
# use inside HTML attributes.
|
80
|
+
#
|
81
|
+
# If you need to output JSON elsewhere in your HTML, you can just do something
|
82
|
+
# like this, as any unsafe characters (including quotation marks) will be
|
83
|
+
# automatically escaped for you:
|
84
|
+
#
|
85
|
+
# <div data-user-info="<%= current_user.to_json %>">...</div>
|
86
|
+
#
|
76
87
|
# WARNING: this helper only works with valid JSON. Using this on non-JSON values
|
77
88
|
# will open up serious XSS vulnerabilities. For example, if you replace the
|
78
89
|
# +current_user.to_json+ in the example above with user input instead, the browser
|
@@ -88,17 +99,6 @@ class ERB
|
|
88
99
|
# is recommended that you always apply this helper (other libraries, such as the
|
89
100
|
# JSON gem, do not provide this kind of protection by default; also some gems
|
90
101
|
# might override +to_json+ to bypass Active Support's encoder).
|
91
|
-
#
|
92
|
-
# The output of this helper method is marked as HTML safe so that you can directly
|
93
|
-
# include it inside a <tt><script></tt> tag as shown above.
|
94
|
-
#
|
95
|
-
# However, it is NOT safe to use the output of this inside an HTML attribute,
|
96
|
-
# because quotation marks are not escaped. Doing so might break your page's layout.
|
97
|
-
# If you intend to use this inside an HTML attribute, you should use the
|
98
|
-
# +html_escape+ helper (or its +h+ alias) instead:
|
99
|
-
#
|
100
|
-
# <div data-user-info="<%= h current_user.to_json %>">...</div>
|
101
|
-
#
|
102
102
|
def json_escape(s)
|
103
103
|
result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
|
104
104
|
s.html_safe? ? result.html_safe : result
|