activesupport 4.0.13 → 4.2.11.3
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 +5 -5
- data/CHANGELOG.md +406 -418
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -2
- data/lib/active_support/backtrace_cleaner.rb +8 -8
- data/lib/active_support/benchmarkable.rb +0 -10
- data/lib/active_support/cache/file_store.rb +32 -22
- data/lib/active_support/cache/mem_cache_store.rb +5 -7
- data/lib/active_support/cache/memory_store.rb +1 -0
- data/lib/active_support/cache/strategy/local_cache.rb +11 -30
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
- data/lib/active_support/cache.rb +75 -41
- data/lib/active_support/callbacks.rb +482 -261
- data/lib/active_support/concern.rb +23 -7
- data/lib/active_support/configurable.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +11 -1
- data/lib/active_support/core_ext/array/conversions.rb +2 -17
- data/lib/active_support/core_ext/array/grouping.rb +29 -12
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -15
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +16 -0
- data/lib/active_support/core_ext/class/attribute.rb +1 -2
- data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -170
- data/lib/active_support/core_ext/class/delegating_attributes.rb +13 -8
- data/lib/active_support/core_ext/class/subclasses.rb +0 -2
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/date/calculations.rb +10 -0
- data/lib/active_support/core_ext/date/conversions.rb +9 -1
- data/lib/active_support/core_ext/date/zones.rb +2 -33
- data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -11
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +45 -22
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
- data/lib/active_support/core_ext/date_time/zones.rb +3 -21
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +51 -0
- data/lib/active_support/core_ext/enumerable.rb +17 -1
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/compact.rb +24 -0
- data/lib/active_support/core_ext/hash/conversions.rb +9 -8
- data/lib/active_support/core_ext/hash/except.rb +8 -2
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
- data/lib/active_support/core_ext/hash/keys.rb +25 -19
- data/lib/active_support/core_ext/hash/slice.rb +8 -2
- data/lib/active_support/core_ext/hash/transform_values.rb +23 -0
- data/lib/active_support/core_ext/hash.rb +2 -1
- data/lib/active_support/core_ext/integer/time.rb +0 -15
- data/lib/active_support/core_ext/kernel/concern.rb +10 -0
- data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +13 -2
- data/lib/active_support/core_ext/kernel.rb +3 -2
- data/lib/active_support/core_ext/load_error.rb +4 -1
- data/lib/active_support/core_ext/marshal.rb +8 -5
- data/lib/active_support/core_ext/module/aliasing.rb +2 -2
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -1
- 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 +53 -25
- 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 +13 -0
- data/lib/active_support/core_ext/module.rb +1 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +11 -3
- data/lib/active_support/core_ext/numeric/time.rb +4 -29
- data/lib/active_support/core_ext/object/blank.rb +44 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
- data/lib/active_support/core_ext/object/duplicable.rb +72 -33
- data/lib/active_support/core_ext/object/inclusion.rb +16 -15
- data/lib/active_support/core_ext/object/itself.rb +15 -0
- data/lib/active_support/core_ext/object/json.rb +197 -0
- data/lib/active_support/core_ext/object/to_query.rb +14 -6
- data/lib/active_support/core_ext/object/try.rb +36 -14
- data/lib/active_support/core_ext/object/with_options.rb +30 -3
- data/lib/active_support/core_ext/object.rb +2 -1
- data/lib/active_support/core_ext/string/access.rb +35 -35
- data/lib/active_support/core_ext/string/conversions.rb +10 -9
- data/lib/active_support/core_ext/string/exclude.rb +3 -3
- data/lib/active_support/core_ext/string/filters.rb +51 -3
- data/lib/active_support/core_ext/string/inflections.rb +15 -10
- data/lib/active_support/core_ext/string/output_safety.rb +97 -33
- data/lib/active_support/core_ext/string/zones.rb +1 -0
- data/lib/active_support/core_ext/thread.rb +12 -5
- data/lib/active_support/core_ext/time/calculations.rb +47 -68
- data/lib/active_support/core_ext/time/compatibility.rb +14 -0
- data/lib/active_support/core_ext/time/conversions.rb +4 -2
- data/lib/active_support/core_ext/time/zones.rb +2 -20
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/dependencies/autoload.rb +1 -1
- data/lib/active_support/dependencies.rb +64 -25
- data/lib/active_support/deprecation/behaviors.rb +4 -4
- data/lib/active_support/deprecation.rb +4 -4
- data/lib/active_support/duration.rb +55 -11
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/gem_version.rb +15 -0
- data/lib/active_support/hash_with_indifferent_access.rb +39 -11
- data/lib/active_support/i18n.rb +4 -4
- data/lib/active_support/i18n_railtie.rb +1 -7
- data/lib/active_support/inflections.rb +6 -1
- data/lib/active_support/inflector/inflections.rb +19 -19
- data/lib/active_support/inflector/methods.rb +66 -25
- data/lib/active_support/json/decoding.rb +15 -22
- data/lib/active_support/json/encoding.rb +125 -286
- data/lib/active_support/key_generator.rb +8 -10
- 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 +51 -1
- data/lib/active_support/logger_silence.rb +7 -4
- data/lib/active_support/logger_thread_safe_level.rb +32 -0
- data/lib/active_support/message_encryptor.rb +14 -6
- data/lib/active_support/message_verifier.rb +16 -12
- data/lib/active_support/multibyte/chars.rb +2 -3
- data/lib/active_support/multibyte/unicode.rb +46 -58
- data/lib/active_support/notifications/fanout.rb +12 -7
- data/lib/active_support/notifications/instrumenter.rb +2 -1
- data/lib/active_support/notifications.rb +11 -6
- 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 +23 -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 +87 -0
- data/lib/active_support/number_helper.rb +32 -324
- data/lib/active_support/ordered_options.rb +8 -0
- data/lib/active_support/per_thread_registry.rb +13 -10
- data/lib/active_support/security_utils.rb +27 -0
- data/lib/active_support/subscriber.rb +35 -3
- data/lib/active_support/test_case.rb +52 -19
- data/lib/active_support/testing/assertions.rb +1 -31
- data/lib/active_support/testing/autorun.rb +2 -2
- data/lib/active_support/testing/constant_lookup.rb +1 -5
- data/lib/active_support/testing/declarative.rb +7 -21
- data/lib/active_support/testing/isolation.rb +29 -69
- data/lib/active_support/testing/setup_and_teardown.rb +17 -2
- data/lib/active_support/testing/tagged_logging.rb +2 -2
- data/lib/active_support/testing/time_helpers.rb +134 -0
- data/lib/active_support/time.rb +0 -2
- data/lib/active_support/time_with_zone.rb +60 -40
- data/lib/active_support/values/time_zone.rb +101 -101
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +4 -7
- data/lib/active_support/xml_mini/jdom.rb +6 -5
- data/lib/active_support/xml_mini/libxml.rb +1 -3
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -4
- data/lib/active_support/xml_mini/nokogiri.rb +1 -3
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -3
- data/lib/active_support/xml_mini/rexml.rb +7 -8
- data/lib/active_support/xml_mini.rb +33 -15
- data/lib/active_support.rb +27 -2
- metadata +43 -43
- 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/object/to_json.rb +0 -27
- 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/file_watcher.rb +0 -36
- data/lib/active_support/json/variable.rb +0 -18
- data/lib/active_support/testing/pending.rb +0 -14
@@ -1,5 +1,5 @@
|
|
1
1
|
class String
|
2
|
-
# If you pass a single
|
2
|
+
# If you pass a single integer, returns a substring of one character at that
|
3
3
|
# position. The first character of the string is at position 0, the next at
|
4
4
|
# position 1, and so on. If a range is supplied, a substring containing
|
5
5
|
# characters at offsets given by the range is returned. In both cases, if an
|
@@ -8,22 +8,22 @@ class String
|
|
8
8
|
# the beginning of the range is greater than the end of the string.
|
9
9
|
#
|
10
10
|
# str = "hello"
|
11
|
-
# str.at(0)
|
12
|
-
# str.at(1..3)
|
13
|
-
# str.at(-2)
|
14
|
-
# str.at(-2..-1)
|
15
|
-
# str.at(5)
|
16
|
-
# str.at(5..-1)
|
11
|
+
# str.at(0) # => "h"
|
12
|
+
# str.at(1..3) # => "ell"
|
13
|
+
# str.at(-2) # => "l"
|
14
|
+
# str.at(-2..-1) # => "lo"
|
15
|
+
# str.at(5) # => nil
|
16
|
+
# str.at(5..-1) # => ""
|
17
17
|
#
|
18
18
|
# If a Regexp is given, the matching portion of the string is returned.
|
19
19
|
# If a String is given, that given string is returned if it occurs in
|
20
20
|
# the string. In both cases, nil is returned if there is no match.
|
21
21
|
#
|
22
22
|
# str = "hello"
|
23
|
-
# str.at(/lo/)
|
24
|
-
# str.at(/ol/)
|
25
|
-
# str.at("lo")
|
26
|
-
# str.at("ol")
|
23
|
+
# str.at(/lo/) # => "lo"
|
24
|
+
# str.at(/ol/) # => nil
|
25
|
+
# str.at("lo") # => "lo"
|
26
|
+
# str.at("ol") # => nil
|
27
27
|
def at(position)
|
28
28
|
self[position]
|
29
29
|
end
|
@@ -32,15 +32,15 @@ class String
|
|
32
32
|
# If the position is negative, it is counted from the end of the string.
|
33
33
|
#
|
34
34
|
# str = "hello"
|
35
|
-
# str.from(0)
|
36
|
-
# str.from(3)
|
37
|
-
# str.from(-2)
|
35
|
+
# str.from(0) # => "hello"
|
36
|
+
# str.from(3) # => "lo"
|
37
|
+
# str.from(-2) # => "lo"
|
38
38
|
#
|
39
39
|
# You can mix it with +to+ method and do fun things like:
|
40
40
|
#
|
41
41
|
# str = "hello"
|
42
|
-
# str.from(0).to(-1)
|
43
|
-
# str.from(1).to(-2)
|
42
|
+
# str.from(0).to(-1) # => "hello"
|
43
|
+
# str.from(1).to(-2) # => "ell"
|
44
44
|
def from(position)
|
45
45
|
self[position..-1]
|
46
46
|
end
|
@@ -49,34 +49,34 @@ class String
|
|
49
49
|
# If the position is negative, it is counted from the end of the string.
|
50
50
|
#
|
51
51
|
# str = "hello"
|
52
|
-
# str.to(0)
|
53
|
-
# str.to(3)
|
54
|
-
# str.to(-2)
|
52
|
+
# str.to(0) # => "h"
|
53
|
+
# str.to(3) # => "hell"
|
54
|
+
# str.to(-2) # => "hell"
|
55
55
|
#
|
56
56
|
# You can mix it with +from+ method and do fun things like:
|
57
57
|
#
|
58
58
|
# str = "hello"
|
59
|
-
# str.from(0).to(-1)
|
60
|
-
# str.from(1).to(-2)
|
59
|
+
# str.from(0).to(-1) # => "hello"
|
60
|
+
# str.from(1).to(-2) # => "ell"
|
61
61
|
def to(position)
|
62
62
|
self[0..position]
|
63
63
|
end
|
64
64
|
|
65
65
|
# Returns the first character. If a limit is supplied, returns a substring
|
66
66
|
# from the beginning of the string until it reaches the limit value. If the
|
67
|
-
# given limit is greater than or equal to the string length, returns self.
|
67
|
+
# given limit is greater than or equal to the string length, returns a copy of self.
|
68
68
|
#
|
69
69
|
# str = "hello"
|
70
|
-
# str.first
|
71
|
-
# str.first(1)
|
72
|
-
# str.first(2)
|
73
|
-
# str.first(0)
|
74
|
-
# str.first(6)
|
70
|
+
# str.first # => "h"
|
71
|
+
# str.first(1) # => "h"
|
72
|
+
# str.first(2) # => "he"
|
73
|
+
# str.first(0) # => ""
|
74
|
+
# str.first(6) # => "hello"
|
75
75
|
def first(limit = 1)
|
76
76
|
if limit == 0
|
77
77
|
''
|
78
78
|
elsif limit >= size
|
79
|
-
self
|
79
|
+
self.dup
|
80
80
|
else
|
81
81
|
to(limit - 1)
|
82
82
|
end
|
@@ -84,19 +84,19 @@ class String
|
|
84
84
|
|
85
85
|
# Returns the last character of the string. If a limit is supplied, returns a substring
|
86
86
|
# from the end of the string until it reaches the limit value (counting backwards). If
|
87
|
-
# the given limit is greater than or equal to the string length, returns self.
|
87
|
+
# the given limit is greater than or equal to the string length, returns a copy of self.
|
88
88
|
#
|
89
89
|
# str = "hello"
|
90
|
-
# str.last
|
91
|
-
# str.last(1)
|
92
|
-
# str.last(2)
|
93
|
-
# str.last(0)
|
94
|
-
# str.last(6)
|
90
|
+
# str.last # => "o"
|
91
|
+
# str.last(1) # => "o"
|
92
|
+
# str.last(2) # => "lo"
|
93
|
+
# str.last(0) # => ""
|
94
|
+
# str.last(6) # => "hello"
|
95
95
|
def last(limit = 1)
|
96
96
|
if limit == 0
|
97
97
|
''
|
98
98
|
elsif limit >= size
|
99
|
-
self
|
99
|
+
self.dup
|
100
100
|
else
|
101
101
|
from(-limit)
|
102
102
|
end
|
@@ -15,6 +15,7 @@ class String
|
|
15
15
|
# "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100
|
16
16
|
# "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
|
17
17
|
# "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 05:12:00 UTC
|
18
|
+
# "12/13/2012".to_time # => ArgumentError: argument out of range
|
18
19
|
def to_time(form = :local)
|
19
20
|
parts = Date._parse(self, false)
|
20
21
|
return if parts.empty?
|
@@ -30,25 +31,25 @@ class String
|
|
30
31
|
parts.fetch(:offset, form == :utc ? 0 : nil)
|
31
32
|
)
|
32
33
|
|
33
|
-
form == :utc ? time.utc : time.
|
34
|
+
form == :utc ? time.utc : time.to_time
|
34
35
|
end
|
35
36
|
|
36
37
|
# Converts a string to a Date value.
|
37
38
|
#
|
38
|
-
# "1-1-2012".to_date
|
39
|
-
# "01/01/2012".to_date
|
40
|
-
# "2012-12-13".to_date
|
41
|
-
# "12/13/2012".to_date
|
39
|
+
# "1-1-2012".to_date # => Sun, 01 Jan 2012
|
40
|
+
# "01/01/2012".to_date # => Sun, 01 Jan 2012
|
41
|
+
# "2012-12-13".to_date # => Thu, 13 Dec 2012
|
42
|
+
# "12/13/2012".to_date # => ArgumentError: invalid date
|
42
43
|
def to_date
|
43
44
|
::Date.parse(self, false) unless blank?
|
44
45
|
end
|
45
46
|
|
46
47
|
# Converts a string to a DateTime value.
|
47
48
|
#
|
48
|
-
# "1-1-2012".to_datetime
|
49
|
-
# "01/01/2012 23:59:59".to_datetime
|
50
|
-
# "2012-12-13 12:50".to_datetime
|
51
|
-
# "12/13/2012".to_datetime
|
49
|
+
# "1-1-2012".to_datetime # => Sun, 01 Jan 2012 00:00:00 +0000
|
50
|
+
# "01/01/2012 23:59:59".to_datetime # => Sun, 01 Jan 2012 23:59:59 +0000
|
51
|
+
# "2012-12-13 12:50".to_datetime # => Thu, 13 Dec 2012 12:50:00 +0000
|
52
|
+
# "12/13/2012".to_datetime # => ArgumentError: invalid date
|
52
53
|
def to_datetime
|
53
54
|
::DateTime.parse(self, false) unless blank?
|
54
55
|
end
|
@@ -2,9 +2,9 @@ class String
|
|
2
2
|
# The inverse of <tt>String#include?</tt>. Returns true if the string
|
3
3
|
# does not include the other string.
|
4
4
|
#
|
5
|
-
# "hello".exclude? "lo"
|
6
|
-
# "hello".exclude? "ol"
|
7
|
-
# "hello".exclude? ?h
|
5
|
+
# "hello".exclude? "lo" # => false
|
6
|
+
# "hello".exclude? "ol" # => true
|
7
|
+
# "hello".exclude? ?h # => false
|
8
8
|
def exclude?(string)
|
9
9
|
!include?(string)
|
10
10
|
end
|
@@ -13,6 +13,9 @@ class String
|
|
13
13
|
end
|
14
14
|
|
15
15
|
# Performs a destructive squish. See String#squish.
|
16
|
+
# str = " foo bar \n \t boo"
|
17
|
+
# str.squish! # => "foo bar boo"
|
18
|
+
# str # => "foo bar boo"
|
16
19
|
def squish!
|
17
20
|
gsub!(/\A[[:space:]]+/, '')
|
18
21
|
gsub!(/[[:space:]]+\z/, '')
|
@@ -20,6 +23,27 @@ class String
|
|
20
23
|
self
|
21
24
|
end
|
22
25
|
|
26
|
+
# Returns a new string with all occurrences of the patterns removed.
|
27
|
+
# str = "foo bar test"
|
28
|
+
# str.remove(" test") # => "foo bar"
|
29
|
+
# str.remove(" test", /bar/) # => "foo "
|
30
|
+
# str # => "foo bar test"
|
31
|
+
def remove(*patterns)
|
32
|
+
dup.remove!(*patterns)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Alters the string by removing all occurrences of the patterns.
|
36
|
+
# str = "foo bar test"
|
37
|
+
# str.remove!(" test", /bar/) # => "foo "
|
38
|
+
# str # => "foo "
|
39
|
+
def remove!(*patterns)
|
40
|
+
patterns.each do |pattern|
|
41
|
+
gsub! pattern, ""
|
42
|
+
end
|
43
|
+
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
23
47
|
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
|
24
48
|
#
|
25
49
|
# 'Once upon a time in a world far far away'.truncate(27)
|
@@ -41,8 +65,8 @@ class String
|
|
41
65
|
def truncate(truncate_at, options = {})
|
42
66
|
return dup unless length > truncate_at
|
43
67
|
|
44
|
-
options[:omission]
|
45
|
-
length_with_room_for_omission = truncate_at -
|
68
|
+
omission = options[:omission] || '...'
|
69
|
+
length_with_room_for_omission = truncate_at - omission.length
|
46
70
|
stop = \
|
47
71
|
if options[:separator]
|
48
72
|
rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
|
@@ -50,6 +74,30 @@ class String
|
|
50
74
|
length_with_room_for_omission
|
51
75
|
end
|
52
76
|
|
53
|
-
"#{self[0
|
77
|
+
"#{self[0, stop]}#{omission}"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Truncates a given +text+ after a given number of words (<tt>words_count</tt>):
|
81
|
+
#
|
82
|
+
# 'Once upon a time in a world far far away'.truncate_words(4)
|
83
|
+
# # => "Once upon a time..."
|
84
|
+
#
|
85
|
+
# Pass a string or regexp <tt>:separator</tt> to specify a different separator of words:
|
86
|
+
#
|
87
|
+
# 'Once<br>upon<br>a<br>time<br>in<br>a<br>world'.truncate_words(5, separator: '<br>')
|
88
|
+
# # => "Once<br>upon<br>a<br>time<br>in..."
|
89
|
+
#
|
90
|
+
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "..."):
|
91
|
+
#
|
92
|
+
# 'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)')
|
93
|
+
# # => "And they found that many... (continued)"
|
94
|
+
def truncate_words(words_count, options = {})
|
95
|
+
sep = options[:separator] || /\s+/
|
96
|
+
sep = Regexp.escape(sep.to_s) unless Regexp === sep
|
97
|
+
if self =~ /\A((?>.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
|
98
|
+
$1 + (options[:omission] || '...')
|
99
|
+
else
|
100
|
+
dup
|
101
|
+
end
|
54
102
|
end
|
55
103
|
end
|
@@ -31,7 +31,7 @@ class String
|
|
31
31
|
def pluralize(count = nil, locale = :en)
|
32
32
|
locale = count if count.is_a?(Symbol)
|
33
33
|
if count == 1
|
34
|
-
self
|
34
|
+
self.dup
|
35
35
|
else
|
36
36
|
ActiveSupport::Inflector.pluralize(self, locale)
|
37
37
|
end
|
@@ -130,6 +130,8 @@ class String
|
|
130
130
|
#
|
131
131
|
# 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
|
132
132
|
# 'Inflections'.demodulize # => "Inflections"
|
133
|
+
# '::Inflections'.demodulize # => "Inflections"
|
134
|
+
# ''.demodulize # => ''
|
133
135
|
#
|
134
136
|
# See also +deconstantize+.
|
135
137
|
def demodulize
|
@@ -182,21 +184,24 @@ class String
|
|
182
184
|
#
|
183
185
|
# 'egg_and_hams'.classify # => "EggAndHam"
|
184
186
|
# 'posts'.classify # => "Post"
|
185
|
-
#
|
186
|
-
# Singular names are not handled correctly.
|
187
|
-
#
|
188
|
-
# 'business'.classify # => "Busines"
|
189
187
|
def classify
|
190
188
|
ActiveSupport::Inflector.classify(self)
|
191
189
|
end
|
192
190
|
|
193
|
-
# Capitalizes the first word, turns underscores into spaces, and strips
|
191
|
+
# Capitalizes the first word, turns underscores into spaces, and strips a
|
192
|
+
# trailing '_id' if present.
|
194
193
|
# Like +titleize+, this is meant for creating pretty output.
|
195
194
|
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
|
199
|
-
|
195
|
+
# The capitalization of the first word can be turned off by setting the
|
196
|
+
# optional parameter +capitalize+ to false.
|
197
|
+
# By default, this parameter is true.
|
198
|
+
#
|
199
|
+
# 'employee_salary'.humanize # => "Employee salary"
|
200
|
+
# 'author_id'.humanize # => "Author"
|
201
|
+
# 'author_id'.humanize(capitalize: false) # => "author"
|
202
|
+
# '_id'.humanize # => "Id"
|
203
|
+
def humanize(options = {})
|
204
|
+
ActiveSupport::Inflector.humanize(self, options)
|
200
205
|
end
|
201
206
|
|
202
207
|
# Creates a foreign key name from a class name.
|
@@ -1,12 +1,14 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'active_support/core_ext/kernel/singleton_class'
|
3
|
+
require 'active_support/deprecation'
|
3
4
|
|
4
5
|
class ERB
|
5
6
|
module Util
|
6
7
|
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' }
|
7
|
-
JSON_ESCAPE = { '&' => '\u0026', '>' => '\
|
8
|
-
|
9
|
-
|
8
|
+
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003e', '<' => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
|
9
|
+
HTML_ESCAPE_REGEXP = /[&"'><]/
|
10
|
+
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
|
11
|
+
JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
|
10
12
|
|
11
13
|
# A utility method for escaping HTML tag characters.
|
12
14
|
# This method is also aliased as <tt>h</tt>.
|
@@ -17,12 +19,7 @@ class ERB
|
|
17
19
|
# puts html_escape('is a > 0 & a < 10?')
|
18
20
|
# # => is a > 0 & a < 10?
|
19
21
|
def html_escape(s)
|
20
|
-
s
|
21
|
-
if s.html_safe?
|
22
|
-
s
|
23
|
-
else
|
24
|
-
s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe
|
25
|
-
end
|
22
|
+
unwrapped_html_escape(s).html_safe
|
26
23
|
end
|
27
24
|
|
28
25
|
# Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
|
@@ -34,6 +31,18 @@ class ERB
|
|
34
31
|
singleton_class.send(:remove_method, :html_escape)
|
35
32
|
module_function :html_escape
|
36
33
|
|
34
|
+
# HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
|
35
|
+
# This method is not for public consumption! Seriously!
|
36
|
+
def unwrapped_html_escape(s) # :nodoc:
|
37
|
+
s = s.to_s
|
38
|
+
if s.html_safe?
|
39
|
+
s
|
40
|
+
else
|
41
|
+
s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
module_function :unwrapped_html_escape
|
45
|
+
|
37
46
|
# A utility method for escaping HTML without affecting existing escaped entities.
|
38
47
|
#
|
39
48
|
# html_escape_once('1 < 2 & 3')
|
@@ -48,17 +57,56 @@ class ERB
|
|
48
57
|
|
49
58
|
module_function :html_escape_once
|
50
59
|
|
51
|
-
# A utility method for escaping HTML entities in JSON strings
|
52
|
-
#
|
60
|
+
# A utility method for escaping HTML entities in JSON strings. Specifically, the
|
61
|
+
# &, > and < characters are replaced with their equivalent unicode escaped form -
|
62
|
+
# \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
|
63
|
+
# escaped as they are treated as newline characters in some JavaScript engines.
|
64
|
+
# These sequences have identical meaning as the original characters inside the
|
65
|
+
# context of a JSON string, so assuming the input is a valid and well-formed
|
66
|
+
# JSON value, the output will have equivalent meaning when parsed:
|
67
|
+
#
|
68
|
+
# json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
|
69
|
+
# # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
|
70
|
+
#
|
71
|
+
# json_escape(json)
|
72
|
+
# # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
|
73
|
+
#
|
74
|
+
# JSON.parse(json) == JSON.parse(json_escape(json))
|
75
|
+
# # => true
|
76
|
+
#
|
77
|
+
# The intended use case for this method is to escape JSON strings before including
|
78
|
+
# them inside a script tag to avoid XSS vulnerability:
|
53
79
|
#
|
54
|
-
#
|
55
|
-
#
|
80
|
+
# <script>
|
81
|
+
# var currentUser = <%= raw json_escape(current_user.to_json) %>;
|
82
|
+
# </script>
|
56
83
|
#
|
57
|
-
#
|
58
|
-
#
|
84
|
+
# It is necessary to +raw+ the result of +json_escape+, so that quotation marks
|
85
|
+
# don't get converted to <tt>"</tt> entities. +json_escape+ doesn't
|
86
|
+
# automatically flag the result as HTML safe, since the raw value is unsafe to
|
87
|
+
# use inside HTML attributes.
|
59
88
|
#
|
60
|
-
#
|
61
|
-
#
|
89
|
+
# If you need to output JSON elsewhere in your HTML, you can just do something
|
90
|
+
# like this, as any unsafe characters (including quotation marks) will be
|
91
|
+
# automatically escaped for you:
|
92
|
+
#
|
93
|
+
# <div data-user-info="<%= current_user.to_json %>">...</div>
|
94
|
+
#
|
95
|
+
# WARNING: this helper only works with valid JSON. Using this on non-JSON values
|
96
|
+
# will open up serious XSS vulnerabilities. For example, if you replace the
|
97
|
+
# +current_user.to_json+ in the example above with user input instead, the browser
|
98
|
+
# will happily eval() that string as JavaScript.
|
99
|
+
#
|
100
|
+
# The escaping performed in this method is identical to those performed in the
|
101
|
+
# Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
|
102
|
+
# set to true. Because this transformation is idempotent, this helper can be
|
103
|
+
# applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
|
104
|
+
#
|
105
|
+
# Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
|
106
|
+
# is enabled, or if you are unsure where your JSON string originated from, it
|
107
|
+
# is recommended that you always apply this helper (other libraries, such as the
|
108
|
+
# JSON gem, do not provide this kind of protection by default; also some gems
|
109
|
+
# might override +to_json+ to bypass Active Support's encoder).
|
62
110
|
def json_escape(s)
|
63
111
|
result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
|
64
112
|
s.html_safe? ? result.html_safe : result
|
@@ -84,7 +132,7 @@ module ActiveSupport #:nodoc:
|
|
84
132
|
class SafeBuffer < String
|
85
133
|
UNSAFE_STRING_METHODS = %w(
|
86
134
|
capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
|
87
|
-
slice squeeze strip sub succ swapcase tr tr_s upcase
|
135
|
+
slice squeeze strip sub succ swapcase tr tr_s upcase
|
88
136
|
)
|
89
137
|
|
90
138
|
alias_method :original_concat, :concat
|
@@ -104,7 +152,7 @@ module ActiveSupport #:nodoc:
|
|
104
152
|
new_safe_buffer = super
|
105
153
|
|
106
154
|
if new_safe_buffer
|
107
|
-
new_safe_buffer.
|
155
|
+
new_safe_buffer.instance_variable_set :@html_safe, true
|
108
156
|
end
|
109
157
|
|
110
158
|
new_safe_buffer
|
@@ -134,28 +182,32 @@ module ActiveSupport #:nodoc:
|
|
134
182
|
end
|
135
183
|
|
136
184
|
def concat(value)
|
137
|
-
|
138
|
-
super(value)
|
139
|
-
else
|
140
|
-
super(ERB::Util.h(value))
|
141
|
-
end
|
185
|
+
super(html_escape_interpolated_argument(value))
|
142
186
|
end
|
143
187
|
alias << concat
|
144
188
|
|
189
|
+
def prepend(value)
|
190
|
+
super(html_escape_interpolated_argument(value))
|
191
|
+
end
|
192
|
+
|
193
|
+
def prepend!(value)
|
194
|
+
ActiveSupport::Deprecation.deprecation_warning "ActiveSupport::SafeBuffer#prepend!", :prepend
|
195
|
+
prepend value
|
196
|
+
end
|
197
|
+
|
145
198
|
def +(other)
|
146
199
|
dup.concat(other)
|
147
200
|
end
|
148
201
|
|
149
202
|
def %(args)
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
203
|
+
case args
|
204
|
+
when Hash
|
205
|
+
escaped_args = Hash[args.map { |k,arg| [k, html_escape_interpolated_argument(arg)] }]
|
206
|
+
else
|
207
|
+
escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
|
156
208
|
end
|
157
209
|
|
158
|
-
self.class.new(super(
|
210
|
+
self.class.new(super(escaped_args))
|
159
211
|
end
|
160
212
|
|
161
213
|
def html_safe?
|
@@ -171,11 +223,11 @@ module ActiveSupport #:nodoc:
|
|
171
223
|
end
|
172
224
|
|
173
225
|
def encode_with(coder)
|
174
|
-
coder.
|
226
|
+
coder.represent_object nil, to_str
|
175
227
|
end
|
176
228
|
|
177
229
|
UNSAFE_STRING_METHODS.each do |unsafe_method|
|
178
|
-
if
|
230
|
+
if unsafe_method.respond_to?(unsafe_method)
|
179
231
|
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
180
232
|
def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
|
181
233
|
to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
|
@@ -188,10 +240,22 @@ module ActiveSupport #:nodoc:
|
|
188
240
|
EOT
|
189
241
|
end
|
190
242
|
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
def html_escape_interpolated_argument(arg)
|
247
|
+
(!html_safe? || arg.html_safe?) ? arg :
|
248
|
+
arg.to_s.gsub(ERB::Util::HTML_ESCAPE_REGEXP, ERB::Util::HTML_ESCAPE)
|
249
|
+
end
|
191
250
|
end
|
192
251
|
end
|
193
252
|
|
194
253
|
class String
|
254
|
+
# Marks a string as trusted safe. It will be inserted into HTML with no
|
255
|
+
# additional escaping performed. It is your responsibilty to ensure that the
|
256
|
+
# string contains no malicious content. This method is equivalent to the
|
257
|
+
# `raw` helper in views. It is recommended that you use `sanitize` instead of
|
258
|
+
# this method. It should never be called on user input.
|
195
259
|
def html_safe
|
196
260
|
ActiveSupport::SafeBuffer.new(self)
|
197
261
|
end
|
@@ -33,14 +33,14 @@ class Thread
|
|
33
33
|
_locals[key.to_sym] = value
|
34
34
|
end
|
35
35
|
|
36
|
-
# Returns an
|
36
|
+
# Returns an array of the names of the thread-local variables (as Symbols).
|
37
37
|
#
|
38
38
|
# thr = Thread.new do
|
39
39
|
# Thread.current.thread_variable_set(:cat, 'meow')
|
40
40
|
# Thread.current.thread_variable_set("dog", 'woof')
|
41
41
|
# end
|
42
|
-
# thr.join
|
43
|
-
# thr.thread_variables
|
42
|
+
# thr.join # => #<Thread:0x401b3f10 dead>
|
43
|
+
# thr.thread_variables # => [:dog, :cat]
|
44
44
|
#
|
45
45
|
# Note that these are not fiber local variables. Please see Thread#thread_variable_get
|
46
46
|
# for more details.
|
@@ -53,8 +53,8 @@ class Thread
|
|
53
53
|
#
|
54
54
|
# me = Thread.current
|
55
55
|
# me.thread_variable_set(:oliver, "a")
|
56
|
-
# me.thread_variable?(:oliver)
|
57
|
-
# me.thread_variable?(:stanley)
|
56
|
+
# me.thread_variable?(:oliver) # => true
|
57
|
+
# me.thread_variable?(:stanley) # => false
|
58
58
|
#
|
59
59
|
# Note that these are not fiber local variables. Please see Thread#thread_variable_get
|
60
60
|
# for more details.
|
@@ -62,6 +62,13 @@ class Thread
|
|
62
62
|
_locals.has_key?(key.to_sym)
|
63
63
|
end
|
64
64
|
|
65
|
+
# Freezes the thread so that thread local variables cannot be set via
|
66
|
+
# Thread#thread_variable_set, nor can fiber local variables be set.
|
67
|
+
#
|
68
|
+
# me = Thread.current
|
69
|
+
# me.freeze
|
70
|
+
# me.thread_variable_set(:oliver, "a") #=> RuntimeError: can't modify frozen thread locals
|
71
|
+
# me[:oliver] = "a" #=> RuntimeError: can't modify frozen thread locals
|
65
72
|
def freeze
|
66
73
|
_locals.freeze
|
67
74
|
super
|