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
@@ -47,7 +47,7 @@ class Object
47
47
  end
48
48
 
49
49
  # Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
50
- # does not implemented the tried method.
50
+ # does not implement the tried method.
51
51
  def try!(*a, &b)
52
52
  if a.empty? && block_given?
53
53
  yield self
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/module/aliasing'
2
+ require 'active_support/core_ext/object/acts_like'
2
3
 
3
4
  class Range #:nodoc:
4
5
 
@@ -16,7 +17,7 @@ class Range #:nodoc:
16
17
 
17
18
  private
18
19
  def ensure_iteration_allowed
19
- if first.is_a?(Time)
20
+ if first.acts_like?(:time)
20
21
  raise TypeError, "can't iterate from #{first.class}"
21
22
  end
22
23
  end
@@ -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) #=> "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) #=> ""
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/) #=> "lo"
24
- # str.at(/ol/) #=> nil
25
- # str.at("lo") #=> "lo"
26
- # str.at("ol") #=> nil
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) #=> "hello"
36
- # str.from(3) #=> "lo"
37
- # str.from(-2) #=> "lo"
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) #=> "hello"
43
- # str.from(1).to(-2) #=> "ell"
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,17 +49,17 @@ 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) #=> "h"
53
- # str.to(3) #=> "hell"
54
- # str.to(-2) #=> "hell"
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) #=> "hello"
60
- # str.from(1).to(-2) #=> "ell"
59
+ # str.from(0).to(-1) # => "hello"
60
+ # str.from(1).to(-2) # => "ell"
61
61
  def to(position)
62
- self[0..position]
62
+ self[0, position + 1]
63
63
  end
64
64
 
65
65
  # Returns the first character. If a limit is supplied, returns a substring
@@ -67,11 +67,11 @@ class String
67
67
  # given limit is greater than or equal to the string length, returns self.
68
68
  #
69
69
  # str = "hello"
70
- # str.first #=> "h"
71
- # str.first(1) #=> "h"
72
- # str.first(2) #=> "he"
73
- # str.first(0) #=> ""
74
- # str.first(6) #=> "hello"
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
  ''
@@ -87,11 +87,11 @@ class String
87
87
  # the given limit is greater than or equal to the string length, returns self.
88
88
  #
89
89
  # str = "hello"
90
- # str.last #=> "o"
91
- # str.last(1) #=> "o"
92
- # str.last(2) #=> "lo"
93
- # str.last(0) #=> ""
94
- # str.last(6) #=> "hello"
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
  ''
@@ -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?
@@ -35,20 +36,20 @@ class String
35
36
 
36
37
  # Converts a string to a Date value.
37
38
  #
38
- # "1-1-2012".to_date #=> Sun, 01 Jan 2012
39
- # "01/01/2012".to_date #=> Sun, 01 Jan 2012
40
- # "2012-12-13".to_date #=> Thu, 13 Dec 2012
41
- # "12/13/2012".to_date #=> ArgumentError: invalid 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 #=> Sun, 01 Jan 2012 00:00:00 +0000
49
- # "01/01/2012 23:59:59".to_datetime #=> Sun, 01 Jan 2012 23:59:59 +0000
50
- # "2012-12-13 12:50".to_datetime #=> Thu, 13 Dec 2012 12:50:00 +0000
51
- # "12/13/2012".to_datetime #=> ArgumentError: invalid date
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" #=> false
6
- # "hello".exclude? "ol" #=> true
7
- # "hello".exclude? ?h #=> false
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
@@ -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 like mongolian vowel separator (U+180E).
7
7
  #
8
8
  # %{ Multi-line
9
9
  # string }.squish # => "Multi-line string"
@@ -20,6 +20,16 @@ class String
20
20
  self
21
21
  end
22
22
 
23
+ # Returns a new string with all occurrences of the pattern removed. Short-hand for String#gsub(pattern, '').
24
+ def remove(pattern)
25
+ gsub pattern, ''
26
+ end
27
+
28
+ # Alters the string by removing all occurrences of the pattern. Short-hand for String#gsub!(pattern, '').
29
+ def remove!(pattern)
30
+ gsub! pattern, ''
31
+ end
32
+
23
33
  # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
24
34
  #
25
35
  # 'Once upon a time in a world far far away'.truncate(27)
@@ -41,8 +51,8 @@ class String
41
51
  def truncate(truncate_at, options = {})
42
52
  return dup unless length > truncate_at
43
53
 
44
- options[:omission] ||= '...'
45
- length_with_room_for_omission = truncate_at - options[:omission].length
54
+ omission = options[:omission] || '...'
55
+ length_with_room_for_omission = truncate_at - omission.length
46
56
  stop = \
47
57
  if options[:separator]
48
58
  rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
@@ -50,6 +60,6 @@ class String
50
60
  length_with_room_for_omission
51
61
  end
52
62
 
53
- "#{self[0...stop]}#{options[:omission]}"
63
+ "#{self[0, stop]}#{omission}"
54
64
  end
55
65
  end
@@ -182,21 +182,23 @@ class String
182
182
  #
183
183
  # 'egg_and_hams'.classify # => "EggAndHam"
184
184
  # 'posts'.classify # => "Post"
185
- #
186
- # Singular names are not handled correctly.
187
- #
188
- # 'business'.classify # => "Busines"
189
185
  def classify
190
186
  ActiveSupport::Inflector.classify(self)
191
187
  end
192
188
 
193
- # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
189
+ # Capitalizes the first word, turns underscores into spaces, and strips a
190
+ # trailing '_id' if present.
194
191
  # Like +titleize+, this is meant for creating pretty output.
195
192
  #
196
- # 'employee_salary'.humanize # => "Employee salary"
197
- # 'author_id'.humanize # => "Author"
198
- def humanize
199
- ActiveSupport::Inflector.humanize(self)
193
+ # The capitalization of the first word can be turned off by setting the
194
+ # optional parameter +capitalize+ to false.
195
+ # By default, this parameter is true.
196
+ #
197
+ # 'employee_salary'.humanize # => "Employee salary"
198
+ # 'author_id'.humanize # => "Author"
199
+ # 'author_id'.humanize(capitalize: false) # => "author"
200
+ def humanize(options = {})
201
+ ActiveSupport::Inflector.humanize(self, options)
200
202
  end
201
203
 
202
204
  # Creates a foreign key name from a class name.
@@ -4,9 +4,10 @@ require 'active_support/core_ext/kernel/singleton_class'
4
4
  class ERB
5
5
  module Util
6
6
  HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
7
- JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
7
+ JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003e', '<' => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
8
+ HTML_ESCAPE_REGEXP = /[&"'><]/
8
9
  HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
9
- JSON_ESCAPE_REGEXP = /[&"><]/
10
+ JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
10
11
 
11
12
  # A utility method for escaping HTML tag characters.
12
13
  # This method is also aliased as <tt>h</tt>.
@@ -21,7 +22,7 @@ class ERB
21
22
  if s.html_safe?
22
23
  s
23
24
  else
24
- s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe
25
+ s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE).html_safe
25
26
  end
26
27
  end
27
28
 
@@ -48,17 +49,56 @@ class ERB
48
49
 
49
50
  module_function :html_escape_once
50
51
 
51
- # A utility method for escaping HTML entities in JSON strings
52
- # using \uXXXX JavaScript escape sequences for string literals:
52
+ # A utility method for escaping HTML entities in JSON strings. Specifically, the
53
+ # &, > and < characters are replaced with their equivalent unicode escaped form -
54
+ # \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
55
+ # escaped as they are treated as newline characters in some JavaScript engines.
56
+ # These sequences have identical meaning as the original characters inside the
57
+ # context of a JSON string, so assuming the input is a valid and well-formed
58
+ # JSON value, the output will have equivalent meaning when parsed:
53
59
  #
54
- # json_escape('is a > 0 & a < 10?')
55
- # # => is a \u003E 0 \u0026 a \u003C 10?
60
+ # json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
61
+ # # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
56
62
  #
57
- # Note that after this operation is performed the output is not
58
- # valid JSON. In particular double quotes are removed:
63
+ # json_escape(json)
64
+ # # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
65
+ #
66
+ # JSON.parse(json) == JSON.parse(json_escape(json))
67
+ # # => true
68
+ #
69
+ # The intended use case for this method is to escape JSON strings before including
70
+ # them inside a script tag to avoid XSS vulnerability:
71
+ #
72
+ # <script>
73
+ # var currentUser = <%= json_escape current_user.to_json %>;
74
+ # </script>
75
+ #
76
+ # WARNING: this helper only works with valid JSON. Using this on non-JSON values
77
+ # will open up serious XSS vulnerabilities. For example, if you replace the
78
+ # +current_user.to_json+ in the example above with user input instead, the browser
79
+ # will happily eval() that string as JavaScript.
80
+ #
81
+ # The escaping performed in this method is identical to those performed in the
82
+ # Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
83
+ # set to true. Because this transformation is idempotent, this helper can be
84
+ # applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
85
+ #
86
+ # Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
87
+ # is enabled, or if you are unsure where your JSON string originated from, it
88
+ # is recommended that you always apply this helper (other libraries, such as the
89
+ # JSON gem, do not provide this kind of protection by default; also some gems
90
+ # 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>
59
101
  #
60
- # json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
61
- # # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
62
102
  def json_escape(s)
63
103
  result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
64
104
  s.html_safe? ? result.html_safe : result
@@ -102,11 +142,7 @@ module ActiveSupport #:nodoc:
102
142
  else
103
143
  if html_safe?
104
144
  new_safe_buffer = super
105
-
106
- if new_safe_buffer
107
- new_safe_buffer.instance_eval { @html_safe = true }
108
- end
109
-
145
+ new_safe_buffer.instance_eval { @html_safe = true }
110
146
  new_safe_buffer
111
147
  else
112
148
  to_str[*args]
@@ -147,15 +183,14 @@ module ActiveSupport #:nodoc:
147
183
  end
148
184
 
149
185
  def %(args)
150
- args = Array(args).map do |arg|
151
- if !html_safe? || arg.html_safe?
152
- arg
153
- else
154
- ERB::Util.h(arg)
155
- end
186
+ case args
187
+ when Hash
188
+ escaped_args = Hash[args.map { |k,arg| [k, html_escape_interpolated_argument(arg)] }]
189
+ else
190
+ escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
156
191
  end
157
192
 
158
- self.class.new(super(args))
193
+ self.class.new(super(escaped_args))
159
194
  end
160
195
 
161
196
  def html_safe?
@@ -175,7 +210,7 @@ module ActiveSupport #:nodoc:
175
210
  end
176
211
 
177
212
  UNSAFE_STRING_METHODS.each do |unsafe_method|
178
- if 'String'.respond_to?(unsafe_method)
213
+ if unsafe_method.respond_to?(unsafe_method)
179
214
  class_eval <<-EOT, __FILE__, __LINE__ + 1
180
215
  def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
181
216
  to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
@@ -188,6 +223,12 @@ module ActiveSupport #:nodoc:
188
223
  EOT
189
224
  end
190
225
  end
226
+
227
+ private
228
+
229
+ def html_escape_interpolated_argument(arg)
230
+ (!html_safe? || arg.html_safe?) ? arg : ERB::Util.h(arg)
231
+ end
191
232
  end
192
233
  end
193
234
 
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/string/conversions'
1
2
  require 'active_support/core_ext/time/zones'
2
3
 
3
4
  class String
@@ -39,8 +39,8 @@ class Thread
39
39
  # Thread.current.thread_variable_set(:cat, 'meow')
40
40
  # Thread.current.thread_variable_set("dog", 'woof')
41
41
  # end
42
- # thr.join #=> #<Thread:0x401b3f10 dead>
43
- # thr.thread_variables #=> [:dog, :cat]
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) #=> true
57
- # me.thread_variable?(:stanley) #=> false
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.
@@ -3,7 +3,6 @@ require 'active_support/core_ext/time/conversions'
3
3
  require 'active_support/time_with_zone'
4
4
  require 'active_support/core_ext/time/zones'
5
5
  require 'active_support/core_ext/date_and_time/calculations'
6
- require 'active_support/deprecation'
7
6
 
8
7
  class Time
9
8
  include DateAndTime::Calculations
@@ -26,41 +25,6 @@ class Time
26
25
  end
27
26
  end
28
27
 
29
- # *DEPRECATED*: Use +Time#utc+ or +Time#local+ instead.
30
- #
31
- # Returns a new Time if requested year can be accommodated by Ruby's Time class
32
- # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
33
- # otherwise returns a DateTime.
34
- def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
35
- ActiveSupport::Deprecation.warn 'time_with_datetime_fallback is deprecated. Use Time#utc or Time#local instead', caller
36
- time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
37
-
38
- # This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138.
39
- if time.year == year
40
- time
41
- else
42
- ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
43
- end
44
- rescue
45
- ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
46
- end
47
-
48
- # *DEPRECATED*: Use +Time#utc+ instead.
49
- #
50
- # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>.
51
- def utc_time(*args)
52
- ActiveSupport::Deprecation.warn 'utc_time is deprecated. Use Time#utc instead', caller
53
- time_with_datetime_fallback(:utc, *args)
54
- end
55
-
56
- # *DEPRECATED*: Use +Time#local+ instead.
57
- #
58
- # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>.
59
- def local_time(*args)
60
- ActiveSupport::Deprecation.warn 'local_time is deprecated. Use Time#local instead', caller
61
- time_with_datetime_fallback(:local, *args)
62
- end
63
-
64
28
  # Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
65
29
  def current
66
30
  ::Time.zone ? ::Time.zone.now : ::Time.now
@@ -178,6 +142,16 @@ class Time
178
142
  alias :at_midnight :beginning_of_day
179
143
  alias :at_beginning_of_day :beginning_of_day
180
144
 
145
+ # Returns a new Time representing the middle of the day (12:00)
146
+ def middle_of_day
147
+ change(:hour => 12)
148
+ end
149
+ alias :midday :middle_of_day
150
+ alias :noon :middle_of_day
151
+ alias :at_midday :middle_of_day
152
+ alias :at_noon :middle_of_day
153
+ alias :at_middle_of_day :middle_of_day
154
+
181
155
  # Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
182
156
  def end_of_day
183
157
  change(
@@ -225,27 +199,6 @@ class Time
225
199
  beginning_of_day..end_of_day
226
200
  end
227
201
 
228
- # Returns a Range representing the whole week of the current time.
229
- # Week starts on start_day, default is <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
230
- def all_week(start_day = Date.beginning_of_week)
231
- beginning_of_week(start_day)..end_of_week(start_day)
232
- end
233
-
234
- # Returns a Range representing the whole month of the current time.
235
- def all_month
236
- beginning_of_month..end_of_month
237
- end
238
-
239
- # Returns a Range representing the whole quarter of the current time.
240
- def all_quarter
241
- beginning_of_quarter..end_of_quarter
242
- end
243
-
244
- # Returns a Range representing the whole year of the current time.
245
- def all_year
246
- beginning_of_year..end_of_year
247
- end
248
-
249
202
  def plus_with_duration(other) #:nodoc:
250
203
  if ActiveSupport::Duration === other
251
204
  other.since(self)