activesupport 4.0.13 → 5.2.5

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 (264) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +412 -444
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +8 -4
  5. data/lib/active_support/all.rb +5 -3
  6. data/lib/active_support/array_inquirer.rb +48 -0
  7. data/lib/active_support/backtrace_cleaner.rb +14 -12
  8. data/lib/active_support/benchmarkable.rb +6 -14
  9. data/lib/active_support/builder.rb +3 -1
  10. data/lib/active_support/cache/file_store.rb +67 -51
  11. data/lib/active_support/cache/mem_cache_store.rb +95 -97
  12. data/lib/active_support/cache/memory_store.rb +28 -30
  13. data/lib/active_support/cache/null_store.rb +7 -8
  14. data/lib/active_support/cache/redis_cache_store.rb +466 -0
  15. data/lib/active_support/cache/strategy/local_cache.rb +70 -56
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  17. data/lib/active_support/cache.rb +331 -206
  18. data/lib/active_support/callbacks.rb +697 -426
  19. data/lib/active_support/concern.rb +32 -10
  20. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  21. data/lib/active_support/concurrency/share_lock.rb +227 -0
  22. data/lib/active_support/configurable.rb +8 -5
  23. data/lib/active_support/core_ext/array/access.rb +39 -1
  24. data/lib/active_support/core_ext/array/conversions.rb +24 -35
  25. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +23 -13
  27. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  28. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -5
  29. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  30. data/lib/active_support/core_ext/array.rb +9 -7
  31. data/lib/active_support/core_ext/benchmark.rb +3 -1
  32. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  33. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  34. data/lib/active_support/core_ext/class/attribute.rb +41 -23
  35. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  36. data/lib/active_support/core_ext/class/subclasses.rb +20 -8
  37. data/lib/active_support/core_ext/class.rb +4 -4
  38. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  39. data/lib/active_support/core_ext/date/blank.rb +14 -0
  40. data/lib/active_support/core_ext/date/calculations.rb +21 -9
  41. data/lib/active_support/core_ext/date/conversions.rb +32 -22
  42. data/lib/active_support/core_ext/date/zones.rb +5 -34
  43. data/lib/active_support/core_ext/date.rb +6 -4
  44. data/lib/active_support/core_ext/date_and_time/calculations.rb +199 -57
  45. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  46. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  47. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  48. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  49. data/lib/active_support/core_ext/date_time/calculations.rb +78 -37
  50. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  51. data/lib/active_support/core_ext/date_time/conversions.rb +19 -13
  52. data/lib/active_support/core_ext/date_time.rb +7 -4
  53. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  54. data/lib/active_support/core_ext/digest.rb +3 -0
  55. data/lib/active_support/core_ext/enumerable.rb +113 -29
  56. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  57. data/lib/active_support/core_ext/file.rb +3 -1
  58. data/lib/active_support/core_ext/hash/compact.rb +29 -0
  59. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  60. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  61. data/lib/active_support/core_ext/hash/except.rb +12 -3
  62. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  63. data/lib/active_support/core_ext/hash/keys.rb +50 -38
  64. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  65. data/lib/active_support/core_ext/hash/slice.rb +12 -6
  66. data/lib/active_support/core_ext/hash/transform_values.rb +32 -0
  67. data/lib/active_support/core_ext/hash.rb +11 -8
  68. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  69. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  70. data/lib/active_support/core_ext/integer/time.rb +11 -33
  71. data/lib/active_support/core_ext/integer.rb +5 -3
  72. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  74. data/lib/active_support/core_ext/kernel/reporting.rb +5 -74
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +6 -4
  77. data/lib/active_support/core_ext/load_error.rb +5 -21
  78. data/lib/active_support/core_ext/marshal.rb +13 -10
  79. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  80. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  81. data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +170 -21
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  85. data/lib/active_support/core_ext/module/delegation.rb +135 -45
  86. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  87. data/lib/active_support/core_ext/module/introspection.rb +9 -25
  88. data/lib/active_support/core_ext/module/reachable.rb +5 -2
  89. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  90. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  91. data/lib/active_support/core_ext/module.rb +14 -10
  92. data/lib/active_support/core_ext/name_error.rb +22 -2
  93. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  94. data/lib/active_support/core_ext/numeric/conversions.rb +79 -74
  95. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  96. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  97. data/lib/active_support/core_ext/numeric.rb +6 -3
  98. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  99. data/lib/active_support/core_ext/object/blank.rb +70 -19
  100. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  101. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  102. data/lib/active_support/core_ext/object/duplicable.rb +100 -34
  103. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  104. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  105. data/lib/active_support/core_ext/object/json.rb +227 -0
  106. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  107. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  108. data/lib/active_support/core_ext/object/try.rb +94 -24
  109. data/lib/active_support/core_ext/object/with_options.rb +45 -5
  110. data/lib/active_support/core_ext/object.rb +14 -12
  111. data/lib/active_support/core_ext/range/compare_range.rb +61 -0
  112. data/lib/active_support/core_ext/range/conversions.rb +27 -7
  113. data/lib/active_support/core_ext/range/each.rb +19 -17
  114. data/lib/active_support/core_ext/range/include_range.rb +2 -22
  115. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  116. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  117. data/lib/active_support/core_ext/range.rb +7 -4
  118. data/lib/active_support/core_ext/regexp.rb +6 -0
  119. data/lib/active_support/core_ext/securerandom.rb +25 -0
  120. data/lib/active_support/core_ext/string/access.rb +41 -39
  121. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  122. data/lib/active_support/core_ext/string/conversions.rb +17 -13
  123. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  124. data/lib/active_support/core_ext/string/filters.rb +55 -6
  125. data/lib/active_support/core_ext/string/indent.rb +6 -4
  126. data/lib/active_support/core_ext/string/inflections.rb +66 -24
  127. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  128. data/lib/active_support/core_ext/string/multibyte.rb +15 -7
  129. data/lib/active_support/core_ext/string/output_safety.rb +114 -54
  130. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  131. data/lib/active_support/core_ext/string/strip.rb +4 -5
  132. data/lib/active_support/core_ext/string/zones.rb +4 -1
  133. data/lib/active_support/core_ext/string.rb +15 -13
  134. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  135. data/lib/active_support/core_ext/time/calculations.rb +123 -110
  136. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  137. data/lib/active_support/core_ext/time/conversions.rb +23 -14
  138. data/lib/active_support/core_ext/time/zones.rb +42 -26
  139. data/lib/active_support/core_ext/time.rb +7 -5
  140. data/lib/active_support/core_ext/uri.rb +6 -8
  141. data/lib/active_support/core_ext.rb +3 -2
  142. data/lib/active_support/current_attributes.rb +195 -0
  143. data/lib/active_support/dependencies/autoload.rb +3 -1
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies.rb +196 -166
  146. data/lib/active_support/deprecation/behaviors.rb +48 -15
  147. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  149. data/lib/active_support/deprecation/method_wrappers.rb +66 -20
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
  151. data/lib/active_support/deprecation/reporting.rb +32 -12
  152. data/lib/active_support/deprecation.rb +14 -11
  153. data/lib/active_support/descendants_tracker.rb +2 -0
  154. data/lib/active_support/digest.rb +20 -0
  155. data/lib/active_support/duration/iso8601_parser.rb +125 -0
  156. data/lib/active_support/duration/iso8601_serializer.rb +55 -0
  157. data/lib/active_support/duration.rb +354 -34
  158. data/lib/active_support/encrypted_configuration.rb +49 -0
  159. data/lib/active_support/encrypted_file.rb +99 -0
  160. data/lib/active_support/evented_file_update_checker.rb +205 -0
  161. data/lib/active_support/execution_wrapper.rb +128 -0
  162. data/lib/active_support/executor.rb +8 -0
  163. data/lib/active_support/file_update_checker.rb +63 -37
  164. data/lib/active_support/gem_version.rb +17 -0
  165. data/lib/active_support/gzip.rb +7 -5
  166. data/lib/active_support/hash_with_indifferent_access.rb +158 -35
  167. data/lib/active_support/i18n.rb +8 -6
  168. data/lib/active_support/i18n_railtie.rb +38 -20
  169. data/lib/active_support/inflections.rb +19 -12
  170. data/lib/active_support/inflector/inflections.rb +79 -30
  171. data/lib/active_support/inflector/methods.rb +197 -129
  172. data/lib/active_support/inflector/transliterate.rb +48 -27
  173. data/lib/active_support/inflector.rb +7 -5
  174. data/lib/active_support/json/decoding.rb +21 -25
  175. data/lib/active_support/json/encoding.rb +84 -292
  176. data/lib/active_support/json.rb +4 -2
  177. data/lib/active_support/key_generator.rb +26 -28
  178. data/lib/active_support/lazy_load_hooks.rb +51 -21
  179. data/lib/active_support/locale/en.yml +2 -0
  180. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  181. data/lib/active_support/log_subscriber.rb +13 -10
  182. data/lib/active_support/logger.rb +54 -3
  183. data/lib/active_support/logger_silence.rb +12 -7
  184. data/lib/active_support/logger_thread_safe_level.rb +34 -0
  185. data/lib/active_support/message_encryptor.rb +173 -50
  186. data/lib/active_support/message_verifier.rb +159 -22
  187. data/lib/active_support/messages/metadata.rb +71 -0
  188. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  189. data/lib/active_support/messages/rotator.rb +56 -0
  190. data/lib/active_support/multibyte/chars.rb +38 -26
  191. data/lib/active_support/multibyte/unicode.rb +138 -146
  192. data/lib/active_support/multibyte.rb +4 -2
  193. data/lib/active_support/notifications/fanout.rb +23 -16
  194. data/lib/active_support/notifications/instrumenter.rb +29 -8
  195. data/lib/active_support/notifications.rb +22 -13
  196. data/lib/active_support/number_helper/number_converter.rb +184 -0
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +29 -0
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +59 -0
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +14 -0
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +54 -0
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/number_helper.rb +125 -391
  206. data/lib/active_support/option_merger.rb +3 -1
  207. data/lib/active_support/ordered_hash.rb +6 -4
  208. data/lib/active_support/ordered_options.rb +31 -5
  209. data/lib/active_support/per_thread_registry.rb +19 -11
  210. data/lib/active_support/proxy_object.rb +2 -0
  211. data/lib/active_support/rails.rb +16 -8
  212. data/lib/active_support/railtie.rb +43 -9
  213. data/lib/active_support/reloader.rb +131 -0
  214. data/lib/active_support/rescuable.rb +108 -53
  215. data/lib/active_support/security_utils.rb +31 -0
  216. data/lib/active_support/string_inquirer.rb +11 -3
  217. data/lib/active_support/subscriber.rb +54 -17
  218. data/lib/active_support/tagged_logging.rb +14 -11
  219. data/lib/active_support/test_case.rb +42 -37
  220. data/lib/active_support/testing/assertions.rb +126 -39
  221. data/lib/active_support/testing/autorun.rb +5 -3
  222. data/lib/active_support/testing/constant_lookup.rb +3 -6
  223. data/lib/active_support/testing/declarative.rb +10 -22
  224. data/lib/active_support/testing/deprecation.rb +14 -10
  225. data/lib/active_support/testing/file_fixtures.rb +36 -0
  226. data/lib/active_support/testing/isolation.rb +55 -86
  227. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  228. data/lib/active_support/testing/setup_and_teardown.rb +30 -10
  229. data/lib/active_support/testing/stream.rb +44 -0
  230. data/lib/active_support/testing/tagged_logging.rb +5 -3
  231. data/lib/active_support/testing/time_helpers.rb +200 -0
  232. data/lib/active_support/time.rb +13 -13
  233. data/lib/active_support/time_with_zone.rb +223 -73
  234. data/lib/active_support/values/time_zone.rb +261 -126
  235. data/lib/active_support/values/unicode_tables.dat +0 -0
  236. data/lib/active_support/version.rb +6 -7
  237. data/lib/active_support/xml_mini/jdom.rb +116 -113
  238. data/lib/active_support/xml_mini/libxml.rb +17 -16
  239. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  240. data/lib/active_support/xml_mini/nokogiri.rb +15 -15
  241. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  242. data/lib/active_support/xml_mini/rexml.rb +17 -16
  243. data/lib/active_support/xml_mini.rb +69 -51
  244. data/lib/active_support.rb +29 -3
  245. metadata +84 -54
  246. data/lib/active_support/basic_object.rb +0 -11
  247. data/lib/active_support/buffered_logger.rb +0 -21
  248. data/lib/active_support/concurrency/latch.rb +0 -27
  249. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  250. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  251. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  252. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  253. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  254. data/lib/active_support/core_ext/logger.rb +0 -67
  255. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  256. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  257. data/lib/active_support/core_ext/proc.rb +0 -17
  258. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  259. data/lib/active_support/core_ext/struct.rb +0 -6
  260. data/lib/active_support/core_ext/thread.rb +0 -79
  261. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  262. data/lib/active_support/file_watcher.rb +0 -36
  263. data/lib/active_support/json/variable.rb +0 -18
  264. data/lib/active_support/testing/pending.rb +0 -14
@@ -1,5 +1,7 @@
1
- require 'date'
2
- require 'active_support/core_ext/time/calculations'
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "active_support/core_ext/time/calculations"
3
5
 
4
6
  class String
5
7
  # Converts a string to a Time value.
@@ -14,10 +16,12 @@ class String
14
16
  # "06:12".to_time # => 2012-12-13 06:12:00 +0100
15
17
  # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100
16
18
  # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
17
- # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 05:12:00 UTC
19
+ # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC
20
+ # "12/13/2012".to_time # => ArgumentError: argument out of range
18
21
  def to_time(form = :local)
19
22
  parts = Date._parse(self, false)
20
- return if parts.empty?
23
+ used_keys = %i(year mon mday hour min sec sec_fraction offset)
24
+ return if (parts.keys & used_keys).empty?
21
25
 
22
26
  now = Time.now
23
27
  time = Time.new(
@@ -30,25 +34,25 @@ class String
30
34
  parts.fetch(:offset, form == :utc ? 0 : nil)
31
35
  )
32
36
 
33
- form == :utc ? time.utc : time.getlocal
37
+ form == :utc ? time.utc : time.to_time
34
38
  end
35
39
 
36
40
  # Converts a string to a Date value.
37
41
  #
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
42
+ # "1-1-2012".to_date # => Sun, 01 Jan 2012
43
+ # "01/01/2012".to_date # => Sun, 01 Jan 2012
44
+ # "2012-12-13".to_date # => Thu, 13 Dec 2012
45
+ # "12/13/2012".to_date # => ArgumentError: invalid date
42
46
  def to_date
43
47
  ::Date.parse(self, false) unless blank?
44
48
  end
45
49
 
46
50
  # Converts a string to a DateTime value.
47
51
  #
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
52
+ # "1-1-2012".to_datetime # => Sun, 01 Jan 2012 00:00:00 +0000
53
+ # "01/01/2012 23:59:59".to_datetime # => Sun, 01 Jan 2012 23:59:59 +0000
54
+ # "2012-12-13 12:50".to_datetime # => Thu, 13 Dec 2012 12:50:00 +0000
55
+ # "12/13/2012".to_datetime # => ArgumentError: invalid date
52
56
  def to_datetime
53
57
  ::DateTime.parse(self, false) unless blank?
54
58
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class String
2
4
  # The inverse of <tt>String#include?</tt>. Returns true if the string
3
5
  # does not include the other string.
4
6
  #
5
- # "hello".exclude? "lo" #=> false
6
- # "hello".exclude? "ol" #=> true
7
- # "hello".exclude? ?h #=> false
7
+ # "hello".exclude? "lo" # => false
8
+ # "hello".exclude? "ol" # => true
9
+ # "hello".exclude? ?h # => false
8
10
  def exclude?(string)
9
11
  !include?(string)
10
12
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class String
2
4
  # Returns the string, first removing all whitespace on both ends of
3
5
  # the string, and then changing remaining consecutive whitespace
@@ -13,10 +15,33 @@ class String
13
15
  end
14
16
 
15
17
  # Performs a destructive squish. See String#squish.
18
+ # str = " foo bar \n \t boo"
19
+ # str.squish! # => "foo bar boo"
20
+ # str # => "foo bar boo"
16
21
  def squish!
17
- gsub!(/\A[[:space:]]+/, '')
18
- gsub!(/[[:space:]]+\z/, '')
19
- gsub!(/[[:space:]]+/, ' ')
22
+ gsub!(/[[:space:]]+/, " ")
23
+ strip!
24
+ self
25
+ end
26
+
27
+ # Returns a new string with all occurrences of the patterns removed.
28
+ # str = "foo bar test"
29
+ # str.remove(" test") # => "foo bar"
30
+ # str.remove(" test", /bar/) # => "foo "
31
+ # str # => "foo bar test"
32
+ def remove(*patterns)
33
+ dup.remove!(*patterns)
34
+ end
35
+
36
+ # Alters the string by removing all occurrences of the patterns.
37
+ # str = "foo bar test"
38
+ # str.remove!(" test", /bar/) # => "foo "
39
+ # str # => "foo "
40
+ def remove!(*patterns)
41
+ patterns.each do |pattern|
42
+ gsub! pattern, ""
43
+ end
44
+
20
45
  self
21
46
  end
22
47
 
@@ -41,8 +66,8 @@ class String
41
66
  def truncate(truncate_at, options = {})
42
67
  return dup unless length > truncate_at
43
68
 
44
- options[:omission] ||= '...'
45
- length_with_room_for_omission = truncate_at - options[:omission].length
69
+ omission = options[:omission] || "..."
70
+ length_with_room_for_omission = truncate_at - omission.length
46
71
  stop = \
47
72
  if options[:separator]
48
73
  rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
@@ -50,6 +75,30 @@ class String
50
75
  length_with_room_for_omission
51
76
  end
52
77
 
53
- "#{self[0...stop]}#{options[:omission]}"
78
+ "#{self[0, stop]}#{omission}"
79
+ end
80
+
81
+ # Truncates a given +text+ after a given number of words (<tt>words_count</tt>):
82
+ #
83
+ # 'Once upon a time in a world far far away'.truncate_words(4)
84
+ # # => "Once upon a time..."
85
+ #
86
+ # Pass a string or regexp <tt>:separator</tt> to specify a different separator of words:
87
+ #
88
+ # 'Once<br>upon<br>a<br>time<br>in<br>a<br>world'.truncate_words(5, separator: '<br>')
89
+ # # => "Once<br>upon<br>a<br>time<br>in..."
90
+ #
91
+ # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "..."):
92
+ #
93
+ # 'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)')
94
+ # # => "And they found that many... (continued)"
95
+ def truncate_words(words_count, options = {})
96
+ sep = options[:separator] || /\s+/
97
+ sep = Regexp.escape(sep.to_s) unless Regexp === sep
98
+ if self =~ /\A((?>.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
99
+ $1 + (options[:omission] || "...")
100
+ else
101
+ dup
102
+ end
54
103
  end
55
104
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class String
2
4
  # Same as +indent+, except it indents the receiver in-place.
3
5
  #
4
6
  # Returns the indented string, or +nil+ if there was nothing to indent.
5
- def indent!(amount, indent_string=nil, indent_empty_lines=false)
6
- indent_string = indent_string || self[/^[ \t]/] || ' '
7
+ def indent!(amount, indent_string = nil, indent_empty_lines = false)
8
+ indent_string = indent_string || self[/^[ \t]/] || " "
7
9
  re = indent_empty_lines ? /^/ : /^(?!$)/
8
10
  gsub!(re, indent_string * amount)
9
11
  end
@@ -37,7 +39,7 @@ class String
37
39
  # "foo\n\nbar".indent(2) # => " foo\n\n bar"
38
40
  # "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
39
41
  #
40
- def indent(amount, indent_string=nil, indent_empty_lines=false)
41
- dup.tap {|_| _.indent!(amount, indent_string, indent_empty_lines)}
42
+ def indent(amount, indent_string = nil, indent_empty_lines = false)
43
+ dup.tap { |_| _.indent!(amount, indent_string, indent_empty_lines) }
42
44
  end
43
45
  end
@@ -1,5 +1,7 @@
1
- require 'active_support/inflector/methods'
2
- require 'active_support/inflector/transliterate'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/inflector/methods"
4
+ require "active_support/inflector/transliterate"
3
5
 
4
6
  # String inflections define new methods on the String class to transform names for different purposes.
5
7
  # For instance, you can figure out the name of a table from the name of a class.
@@ -31,7 +33,7 @@ class String
31
33
  def pluralize(count = nil, locale = :en)
32
34
  locale = count if count.is_a?(Symbol)
33
35
  if count == 1
34
- self
36
+ dup
35
37
  else
36
38
  ActiveSupport::Inflector.pluralize(self, locale)
37
39
  end
@@ -67,7 +69,7 @@ class String
67
69
  end
68
70
 
69
71
  # +safe_constantize+ tries to find a declared constant with the name specified
70
- # in the string. It returns nil when the name is not in CamelCase
72
+ # in the string. It returns +nil+ when the name is not in CamelCase
71
73
  # or is not initialized. See ActiveSupport::Inflector.safe_constantize
72
74
  #
73
75
  # 'Module'.safe_constantize # => Module
@@ -92,6 +94,8 @@ class String
92
94
  ActiveSupport::Inflector.camelize(self, true)
93
95
  when :lower
94
96
  ActiveSupport::Inflector.camelize(self, false)
97
+ else
98
+ raise ArgumentError, "Invalid option, use either :upper or :lower."
95
99
  end
96
100
  end
97
101
  alias_method :camelcase, :camelize
@@ -100,12 +104,17 @@ class String
100
104
  # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
101
105
  # used in the Rails internals.
102
106
  #
107
+ # The trailing '_id','Id'.. can be kept and capitalized by setting the
108
+ # optional parameter +keep_id_suffix+ to true.
109
+ # By default, this parameter is false.
110
+ #
103
111
  # +titleize+ is also aliased as +titlecase+.
104
112
  #
105
- # 'man from the boondocks'.titleize # => "Man From The Boondocks"
106
- # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
107
- def titleize
108
- ActiveSupport::Inflector.titleize(self)
113
+ # 'man from the boondocks'.titleize # => "Man From The Boondocks"
114
+ # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
115
+ # 'string_ending_with_id'.titleize(keep_id_suffix: true) # => "String Ending With Id"
116
+ def titleize(keep_id_suffix: false)
117
+ ActiveSupport::Inflector.titleize(self, keep_id_suffix: keep_id_suffix)
109
118
  end
110
119
  alias_method :titlecase, :titleize
111
120
 
@@ -128,8 +137,10 @@ class String
128
137
 
129
138
  # Removes the module part from the constant expression in the string.
130
139
  #
131
- # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
132
- # 'Inflections'.demodulize # => "Inflections"
140
+ # 'ActiveSupport::Inflector::Inflections'.demodulize # => "Inflections"
141
+ # 'Inflections'.demodulize # => "Inflections"
142
+ # '::Inflections'.demodulize # => "Inflections"
143
+ # ''.demodulize # => ''
133
144
  #
134
145
  # See also +deconstantize+.
135
146
  def demodulize
@@ -162,41 +173,72 @@ class String
162
173
  #
163
174
  # <%= link_to(@person.name, person_path) %>
164
175
  # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
165
- def parameterize(sep = '-')
166
- ActiveSupport::Inflector.parameterize(self, sep)
176
+ #
177
+ # To preserve the case of the characters in a string, use the +preserve_case+ argument.
178
+ #
179
+ # class Person
180
+ # def to_param
181
+ # "#{id}-#{name.parameterize(preserve_case: true)}"
182
+ # end
183
+ # end
184
+ #
185
+ # @person = Person.find(1)
186
+ # # => #<Person id: 1, name: "Donald E. Knuth">
187
+ #
188
+ # <%= link_to(@person.name, person_path) %>
189
+ # # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
190
+ def parameterize(separator: "-", preserve_case: false)
191
+ ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
167
192
  end
168
193
 
169
194
  # Creates the name of a table like Rails does for models to table names. This method
170
195
  # uses the +pluralize+ method on the last word in the string.
171
196
  #
172
197
  # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
173
- # 'egg_and_ham'.tableize # => "egg_and_hams"
198
+ # 'ham_and_egg'.tableize # => "ham_and_eggs"
174
199
  # 'fancyCategory'.tableize # => "fancy_categories"
175
200
  def tableize
176
201
  ActiveSupport::Inflector.tableize(self)
177
202
  end
178
203
 
179
- # Create a class name from a plural table name like Rails does for table names to models.
204
+ # Creates a class name from a plural table name like Rails does for table names to models.
180
205
  # Note that this returns a string and not a class. (To convert to an actual class
181
206
  # follow +classify+ with +constantize+.)
182
207
  #
183
- # 'egg_and_hams'.classify # => "EggAndHam"
208
+ # 'ham_and_eggs'.classify # => "HamAndEgg"
184
209
  # 'posts'.classify # => "Post"
185
- #
186
- # Singular names are not handled correctly.
187
- #
188
- # 'business'.classify # => "Busines"
189
210
  def classify
190
211
  ActiveSupport::Inflector.classify(self)
191
212
  end
192
213
 
193
- # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
214
+ # Capitalizes the first word, turns underscores into spaces, and (by default)strips a
215
+ # trailing '_id' if present.
194
216
  # Like +titleize+, this is meant for creating pretty output.
195
217
  #
196
- # 'employee_salary'.humanize # => "Employee salary"
197
- # 'author_id'.humanize # => "Author"
198
- def humanize
199
- ActiveSupport::Inflector.humanize(self)
218
+ # The capitalization of the first word can be turned off by setting the
219
+ # optional parameter +capitalize+ to false.
220
+ # By default, this parameter is true.
221
+ #
222
+ # The trailing '_id' can be kept and capitalized by setting the
223
+ # optional parameter +keep_id_suffix+ to true.
224
+ # By default, this parameter is false.
225
+ #
226
+ # 'employee_salary'.humanize # => "Employee salary"
227
+ # 'author_id'.humanize # => "Author"
228
+ # 'author_id'.humanize(capitalize: false) # => "author"
229
+ # '_id'.humanize # => "Id"
230
+ # 'author_id'.humanize(keep_id_suffix: true) # => "Author Id"
231
+ def humanize(capitalize: true, keep_id_suffix: false)
232
+ ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix)
233
+ end
234
+
235
+ # Converts just the first character to uppercase.
236
+ #
237
+ # 'what a Lovely Day'.upcase_first # => "What a Lovely Day"
238
+ # 'w'.upcase_first # => "W"
239
+ # ''.upcase_first # => ""
240
+ def upcase_first
241
+ ActiveSupport::Inflector.upcase_first(self)
200
242
  end
201
243
 
202
244
  # Creates a foreign key name from a class name.
@@ -1,4 +1,6 @@
1
- require 'active_support/string_inquirer'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/string_inquirer"
2
4
 
3
5
  class String
4
6
  # Wraps the current string in the <tt>ActiveSupport::StringInquirer</tt> class,
@@ -1,5 +1,6 @@
1
- # encoding: utf-8
2
- require 'active_support/multibyte'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/multibyte"
3
4
 
4
5
  class String
5
6
  # == Multibyte proxy
@@ -10,12 +11,12 @@ class String
10
11
  # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
11
12
  # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
12
13
  #
13
- # name = 'Claus Müller'
14
- # name.reverse # => "rell??M sualC"
15
- # name.length # => 13
14
+ # >> "lj".upcase
15
+ # => "lj"
16
+ # >> "lj".mb_chars.upcase.to_s
17
+ # => "LJ"
16
18
  #
17
- # name.mb_chars.reverse.to_s # => "rellüM sualC"
18
- # name.mb_chars.length # => 12
19
+ # NOTE: An above example is useful for pre Ruby 2.4. Ruby 2.4 supports Unicode case mappings.
19
20
  #
20
21
  # == Method chaining
21
22
  #
@@ -36,6 +37,13 @@ class String
36
37
  ActiveSupport::Multibyte.proxy_class.new(self)
37
38
  end
38
39
 
40
+ # Returns +true+ if string has utf_8 encoding.
41
+ #
42
+ # utf_8_str = "some string".encode "UTF-8"
43
+ # iso_str = "some string".encode "ISO-8859-1"
44
+ #
45
+ # utf_8_str.is_utf8? # => true
46
+ # iso_str.is_utf8? # => false
39
47
  def is_utf8?
40
48
  case encoding
41
49
  when Encoding::UTF_8
@@ -1,39 +1,46 @@
1
- require 'erb'
2
- require 'active_support/core_ext/kernel/singleton_class'
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "active_support/core_ext/kernel/singleton_class"
5
+ require "active_support/core_ext/module/redefine_method"
6
+ require "active_support/multibyte/unicode"
3
7
 
4
8
  class ERB
5
9
  module Util
6
- HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
7
- JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
8
- HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
9
- JSON_ESCAPE_REGEXP = /[&"><]/
10
+ HTML_ESCAPE = { "&" => "&amp;", ">" => "&gt;", "<" => "&lt;", '"' => "&quot;", "'" => "&#39;" }
11
+ JSON_ESCAPE = { "&" => '\u0026', ">" => '\u003e', "<" => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
12
+ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
13
+ JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
10
14
 
11
15
  # A utility method for escaping HTML tag characters.
12
16
  # This method is also aliased as <tt>h</tt>.
13
17
  #
14
- # In your ERB templates, use this method to escape any unsafe content. For example:
15
- # <%=h @person.name %>
16
- #
17
18
  # puts html_escape('is a > 0 & a < 10?')
18
19
  # # => is a &gt; 0 &amp; a &lt; 10?
19
20
  def html_escape(s)
20
- s = s.to_s
21
- if s.html_safe?
22
- s
23
- else
24
- s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe
25
- end
21
+ unwrapped_html_escape(s).html_safe
26
22
  end
27
23
 
28
- # Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
29
- remove_method(:h)
24
+ silence_redefinition_of_method :h
30
25
  alias h html_escape
31
26
 
32
27
  module_function :h
33
28
 
34
- singleton_class.send(:remove_method, :html_escape)
29
+ singleton_class.silence_redefinition_of_method :html_escape
35
30
  module_function :html_escape
36
31
 
32
+ # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
33
+ # This method is not for public consumption! Seriously!
34
+ def unwrapped_html_escape(s) # :nodoc:
35
+ s = s.to_s
36
+ if s.html_safe?
37
+ s
38
+ else
39
+ CGI.escapeHTML(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))
40
+ end
41
+ end
42
+ module_function :unwrapped_html_escape
43
+
37
44
  # A utility method for escaping HTML without affecting existing escaped entities.
38
45
  #
39
46
  # html_escape_once('1 < 2 &amp; 3')
@@ -42,23 +49,67 @@ class ERB
42
49
  # html_escape_once('&lt;&lt; Accept & Checkout')
43
50
  # # => "&lt;&lt; Accept &amp; Checkout"
44
51
  def html_escape_once(s)
45
- result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
52
+ result = ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
46
53
  s.html_safe? ? result.html_safe : result
47
54
  end
48
55
 
49
56
  module_function :html_escape_once
50
57
 
51
- # A utility method for escaping HTML entities in JSON strings
52
- # using \uXXXX JavaScript escape sequences for string literals:
58
+ # A utility method for escaping HTML entities in JSON strings. Specifically, the
59
+ # &, > and < characters are replaced with their equivalent unicode escaped form -
60
+ # \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
61
+ # escaped as they are treated as newline characters in some JavaScript engines.
62
+ # These sequences have identical meaning as the original characters inside the
63
+ # context of a JSON string, so assuming the input is a valid and well-formed
64
+ # JSON value, the output will have equivalent meaning when parsed:
65
+ #
66
+ # json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
67
+ # # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
68
+ #
69
+ # json_escape(json)
70
+ # # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
71
+ #
72
+ # JSON.parse(json) == JSON.parse(json_escape(json))
73
+ # # => true
74
+ #
75
+ # The intended use case for this method is to escape JSON strings before including
76
+ # them inside a script tag to avoid XSS vulnerability:
53
77
  #
54
- # json_escape('is a > 0 & a < 10?')
55
- # # => is a \u003E 0 \u0026 a \u003C 10?
78
+ # <script>
79
+ # var currentUser = <%= raw json_escape(current_user.to_json) %>;
80
+ # </script>
56
81
  #
57
- # Note that after this operation is performed the output is not
58
- # valid JSON. In particular double quotes are removed:
82
+ # It is necessary to +raw+ the result of +json_escape+, so that quotation marks
83
+ # don't get converted to <tt>&quot;</tt> entities. +json_escape+ doesn't
84
+ # automatically flag the result as HTML safe, since the raw value is unsafe to
85
+ # use inside HTML attributes.
59
86
  #
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}
87
+ # If your JSON is being used downstream for insertion into the DOM, be aware of
88
+ # whether or not it is being inserted via +html()+. Most jQuery plugins do this.
89
+ # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
90
+ # content returned by your JSON.
91
+ #
92
+ # If you need to output JSON elsewhere in your HTML, you can just do something
93
+ # like this, as any unsafe characters (including quotation marks) will be
94
+ # automatically escaped for you:
95
+ #
96
+ # <div data-user-info="<%= current_user.to_json %>">...</div>
97
+ #
98
+ # WARNING: this helper only works with valid JSON. Using this on non-JSON values
99
+ # will open up serious XSS vulnerabilities. For example, if you replace the
100
+ # +current_user.to_json+ in the example above with user input instead, the browser
101
+ # will happily eval() that string as JavaScript.
102
+ #
103
+ # The escaping performed in this method is identical to those performed in the
104
+ # Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
105
+ # set to true. Because this transformation is idempotent, this helper can be
106
+ # applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
107
+ #
108
+ # Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
109
+ # is enabled, or if you are unsure where your JSON string originated from, it
110
+ # is recommended that you always apply this helper (other libraries, such as the
111
+ # JSON gem, do not provide this kind of protection by default; also some gems
112
+ # might override +to_json+ to bypass Active Support's encoder).
62
113
  def json_escape(s)
63
114
  result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
64
115
  s.html_safe? ? result.html_safe : result
@@ -84,33 +135,32 @@ module ActiveSupport #:nodoc:
84
135
  class SafeBuffer < String
85
136
  UNSAFE_STRING_METHODS = %w(
86
137
  capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
87
- slice squeeze strip sub succ swapcase tr tr_s upcase prepend
138
+ slice squeeze strip sub succ swapcase tr tr_s upcase
88
139
  )
89
140
 
90
141
  alias_method :original_concat, :concat
91
142
  private :original_concat
92
143
 
144
+ # Raised when <tt>ActiveSupport::SafeBuffer#safe_concat</tt> is called on unsafe buffers.
93
145
  class SafeConcatError < StandardError
94
146
  def initialize
95
- super 'Could not concatenate to the buffer because it is not html safe.'
147
+ super "Could not concatenate to the buffer because it is not html safe."
96
148
  end
97
149
  end
98
150
 
99
151
  def [](*args)
100
152
  if args.size < 2
101
153
  super
102
- else
103
- if html_safe?
104
- new_safe_buffer = super
105
-
106
- if new_safe_buffer
107
- new_safe_buffer.instance_eval { @html_safe = true }
108
- end
154
+ elsif html_safe?
155
+ new_safe_buffer = super
109
156
 
110
- new_safe_buffer
111
- else
112
- to_str[*args]
157
+ if new_safe_buffer
158
+ new_safe_buffer.instance_variable_set :@html_safe, true
113
159
  end
160
+
161
+ new_safe_buffer
162
+ else
163
+ to_str[*args]
114
164
  end
115
165
  end
116
166
 
@@ -119,7 +169,7 @@ module ActiveSupport #:nodoc:
119
169
  original_concat(value)
120
170
  end
121
171
 
122
- def initialize(*)
172
+ def initialize(str = "")
123
173
  @html_safe = true
124
174
  super
125
175
  end
@@ -134,28 +184,27 @@ module ActiveSupport #:nodoc:
134
184
  end
135
185
 
136
186
  def concat(value)
137
- if !html_safe? || value.html_safe?
138
- super(value)
139
- else
140
- super(ERB::Util.h(value))
141
- end
187
+ super(html_escape_interpolated_argument(value))
142
188
  end
143
189
  alias << concat
144
190
 
191
+ def prepend(value)
192
+ super(html_escape_interpolated_argument(value))
193
+ end
194
+
145
195
  def +(other)
146
196
  dup.concat(other)
147
197
  end
148
198
 
149
199
  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
200
+ case args
201
+ when Hash
202
+ escaped_args = Hash[args.map { |k, arg| [k, html_escape_interpolated_argument(arg)] }]
203
+ else
204
+ escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
156
205
  end
157
206
 
158
- self.class.new(super(args))
207
+ self.class.new(super(escaped_args))
159
208
  end
160
209
 
161
210
  def html_safe?
@@ -171,11 +220,11 @@ module ActiveSupport #:nodoc:
171
220
  end
172
221
 
173
222
  def encode_with(coder)
174
- coder.represent_scalar nil, to_str
223
+ coder.represent_object nil, to_str
175
224
  end
176
225
 
177
226
  UNSAFE_STRING_METHODS.each do |unsafe_method|
178
- if 'String'.respond_to?(unsafe_method)
227
+ if unsafe_method.respond_to?(unsafe_method)
179
228
  class_eval <<-EOT, __FILE__, __LINE__ + 1
180
229
  def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
181
230
  to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
@@ -188,10 +237,21 @@ module ActiveSupport #:nodoc:
188
237
  EOT
189
238
  end
190
239
  end
240
+
241
+ private
242
+
243
+ def html_escape_interpolated_argument(arg)
244
+ (!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
245
+ end
191
246
  end
192
247
  end
193
248
 
194
249
  class String
250
+ # Marks a string as trusted safe. It will be inserted into HTML with no
251
+ # additional escaping performed. It is your responsibility to ensure that the
252
+ # string contains no malicious content. This method is equivalent to the
253
+ # +raw+ helper in views. It is recommended that you use +sanitize+ instead of
254
+ # this method. It should never be called on user input.
195
255
  def html_safe
196
256
  ActiveSupport::SafeBuffer.new(self)
197
257
  end