activesupport 6.0.6 → 7.0.1

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 (204) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +224 -608
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +2 -2
  6. data/lib/active_support/backtrace_cleaner.rb +3 -3
  7. data/lib/active_support/benchmarkable.rb +3 -3
  8. data/lib/active_support/cache/file_store.rb +16 -10
  9. data/lib/active_support/cache/mem_cache_store.rb +143 -38
  10. data/lib/active_support/cache/memory_store.rb +56 -28
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +62 -87
  13. data/lib/active_support/cache/strategy/local_cache.rb +46 -57
  14. data/lib/active_support/cache.rb +268 -77
  15. data/lib/active_support/callbacks.rb +226 -118
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +49 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  19. data/lib/active_support/concurrency/share_lock.rb +2 -2
  20. data/lib/active_support/configurable.rb +9 -6
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +1 -5
  23. data/lib/active_support/core_ext/array/conversions.rb +9 -7
  24. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  25. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  26. data/lib/active_support/core_ext/array.rb +1 -0
  27. data/lib/active_support/core_ext/benchmark.rb +2 -2
  28. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  29. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  30. data/lib/active_support/core_ext/class/subclasses.rb +11 -24
  31. data/lib/active_support/core_ext/date/blank.rb +1 -1
  32. data/lib/active_support/core_ext/date/calculations.rb +4 -4
  33. data/lib/active_support/core_ext/date/conversions.rb +5 -4
  34. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  35. data/lib/active_support/core_ext/date.rb +1 -0
  36. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  37. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  38. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  39. data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
  40. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  41. data/lib/active_support/core_ext/date_time.rb +1 -0
  42. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  43. data/lib/active_support/core_ext/enumerable.rb +139 -15
  44. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  45. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  46. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  47. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  48. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  49. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  50. data/lib/active_support/core_ext/load_error.rb +1 -1
  51. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  52. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  53. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  54. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  55. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  56. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  57. data/lib/active_support/core_ext/name_error.rb +23 -2
  58. data/lib/active_support/core_ext/numeric/conversions.rb +79 -72
  59. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  60. data/lib/active_support/core_ext/numeric.rb +1 -0
  61. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  62. data/lib/active_support/core_ext/object/blank.rb +2 -2
  63. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  64. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  65. data/lib/active_support/core_ext/object/json.rb +41 -25
  66. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  67. data/lib/active_support/core_ext/object/try.rb +20 -20
  68. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  69. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  70. data/lib/active_support/core_ext/pathname.rb +3 -0
  71. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  72. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  73. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  74. data/lib/active_support/core_ext/range/each.rb +1 -1
  75. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  76. data/lib/active_support/core_ext/range.rb +1 -1
  77. data/lib/active_support/core_ext/regexp.rb +8 -1
  78. data/lib/active_support/core_ext/string/access.rb +5 -24
  79. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  80. data/lib/active_support/core_ext/string/filters.rb +1 -1
  81. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  82. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  83. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  84. data/lib/active_support/core_ext/string/output_safety.rb +62 -67
  85. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  86. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  87. data/lib/active_support/core_ext/symbol.rb +3 -0
  88. data/lib/active_support/core_ext/time/calculations.rb +23 -5
  89. data/lib/active_support/core_ext/time/conversions.rb +6 -3
  90. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  91. data/lib/active_support/core_ext/time/zones.rb +4 -19
  92. data/lib/active_support/core_ext/time.rb +1 -0
  93. data/lib/active_support/core_ext/uri.rb +3 -23
  94. data/lib/active_support/core_ext.rb +2 -1
  95. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  96. data/lib/active_support/current_attributes.rb +39 -16
  97. data/lib/active_support/dependencies/interlock.rb +10 -18
  98. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  99. data/lib/active_support/dependencies.rb +58 -769
  100. data/lib/active_support/deprecation/behaviors.rb +19 -3
  101. data/lib/active_support/deprecation/disallowed.rb +56 -0
  102. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  103. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  104. data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
  105. data/lib/active_support/deprecation/reporting.rb +50 -7
  106. data/lib/active_support/deprecation.rb +6 -1
  107. data/lib/active_support/descendants_tracker.rb +174 -64
  108. data/lib/active_support/digest.rb +5 -3
  109. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  110. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  111. data/lib/active_support/duration.rb +134 -55
  112. data/lib/active_support/encrypted_configuration.rb +11 -1
  113. data/lib/active_support/encrypted_file.rb +20 -3
  114. data/lib/active_support/environment_inquirer.rb +20 -0
  115. data/lib/active_support/error_reporter.rb +117 -0
  116. data/lib/active_support/evented_file_update_checker.rb +70 -134
  117. data/lib/active_support/execution_context/test_helper.rb +13 -0
  118. data/lib/active_support/execution_context.rb +53 -0
  119. data/lib/active_support/execution_wrapper.rb +41 -18
  120. data/lib/active_support/executor/test_helper.rb +7 -0
  121. data/lib/active_support/fork_tracker.rb +71 -0
  122. data/lib/active_support/gem_version.rb +2 -2
  123. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  124. data/lib/active_support/html_safe_translation.rb +43 -0
  125. data/lib/active_support/i18n.rb +1 -0
  126. data/lib/active_support/i18n_railtie.rb +14 -19
  127. data/lib/active_support/inflector/inflections.rb +24 -9
  128. data/lib/active_support/inflector/methods.rb +29 -49
  129. data/lib/active_support/inflector/transliterate.rb +4 -4
  130. data/lib/active_support/isolated_execution_state.rb +56 -0
  131. data/lib/active_support/json/decoding.rb +4 -4
  132. data/lib/active_support/json/encoding.rb +8 -4
  133. data/lib/active_support/key_generator.rb +19 -2
  134. data/lib/active_support/locale/en.yml +8 -4
  135. data/lib/active_support/log_subscriber.rb +21 -3
  136. data/lib/active_support/logger.rb +1 -1
  137. data/lib/active_support/logger_silence.rb +2 -26
  138. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  139. data/lib/active_support/message_encryptor.rb +12 -10
  140. data/lib/active_support/message_verifier.rb +50 -18
  141. data/lib/active_support/messages/metadata.rb +2 -2
  142. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  143. data/lib/active_support/messages/rotator.rb +6 -5
  144. data/lib/active_support/multibyte/chars.rb +13 -52
  145. data/lib/active_support/multibyte/unicode.rb +1 -87
  146. data/lib/active_support/multibyte.rb +1 -1
  147. data/lib/active_support/notifications/fanout.rb +110 -69
  148. data/lib/active_support/notifications/instrumenter.rb +37 -29
  149. data/lib/active_support/notifications.rb +47 -26
  150. data/lib/active_support/number_helper/number_converter.rb +2 -4
  151. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  152. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  153. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  154. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  155. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  156. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  157. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  158. data/lib/active_support/number_helper.rb +29 -16
  159. data/lib/active_support/option_merger.rb +9 -16
  160. data/lib/active_support/ordered_hash.rb +1 -1
  161. data/lib/active_support/ordered_options.rb +8 -2
  162. data/lib/active_support/parameter_filter.rb +21 -11
  163. data/lib/active_support/per_thread_registry.rb +6 -1
  164. data/lib/active_support/rails.rb +1 -4
  165. data/lib/active_support/railtie.rb +77 -5
  166. data/lib/active_support/reloader.rb +1 -1
  167. data/lib/active_support/rescuable.rb +6 -6
  168. data/lib/active_support/ruby_features.rb +7 -0
  169. data/lib/active_support/secure_compare_rotator.rb +51 -0
  170. data/lib/active_support/security_utils.rb +19 -12
  171. data/lib/active_support/string_inquirer.rb +2 -2
  172. data/lib/active_support/subscriber.rb +19 -25
  173. data/lib/active_support/tagged_logging.rb +31 -6
  174. data/lib/active_support/test_case.rb +9 -21
  175. data/lib/active_support/testing/assertions.rb +49 -12
  176. data/lib/active_support/testing/deprecation.rb +52 -1
  177. data/lib/active_support/testing/isolation.rb +2 -2
  178. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  179. data/lib/active_support/testing/parallelization/server.rb +82 -0
  180. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  181. data/lib/active_support/testing/parallelization.rb +16 -95
  182. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  183. data/lib/active_support/testing/stream.rb +3 -5
  184. data/lib/active_support/testing/tagged_logging.rb +1 -1
  185. data/lib/active_support/testing/time_helpers.rb +53 -5
  186. data/lib/active_support/time_with_zone.rb +120 -55
  187. data/lib/active_support/values/time_zone.rb +49 -18
  188. data/lib/active_support/xml_mini/jdom.rb +1 -1
  189. data/lib/active_support/xml_mini/libxml.rb +5 -5
  190. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  191. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  192. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  193. data/lib/active_support/xml_mini/rexml.rb +9 -2
  194. data/lib/active_support/xml_mini.rb +5 -4
  195. data/lib/active_support.rb +29 -1
  196. metadata +45 -45
  197. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  198. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  199. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  200. data/lib/active_support/core_ext/marshal.rb +0 -24
  201. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  202. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  203. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  204. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -44,7 +44,7 @@ class String
44
44
  # str.from(0).to(-1) # => "hello"
45
45
  # str.from(1).to(-2) # => "ell"
46
46
  def from(position)
47
- self[position..-1]
47
+ self[position, length]
48
48
  end
49
49
 
50
50
  # Returns a substring from the beginning of the string to the given position.
@@ -61,7 +61,8 @@ class String
61
61
  # str.from(0).to(-1) # => "hello"
62
62
  # str.from(1).to(-2) # => "ell"
63
63
  def to(position)
64
- self[0..position]
64
+ position += size if position < 0
65
+ self[0, position + 1] || +""
65
66
  end
66
67
 
67
68
  # Returns the first character. If a limit is supplied, returns a substring
@@ -75,17 +76,7 @@ class String
75
76
  # str.first(0) # => ""
76
77
  # str.first(6) # => "hello"
77
78
  def first(limit = 1)
78
- ActiveSupport::Deprecation.warn(
79
- "Calling String#first with a negative integer limit " \
80
- "will raise an ArgumentError in Rails 6.1."
81
- ) if limit < 0
82
- if limit == 0
83
- ""
84
- elsif limit >= size
85
- dup
86
- else
87
- to(limit - 1)
88
- end
79
+ self[0, limit] || raise(ArgumentError, "negative limit")
89
80
  end
90
81
 
91
82
  # Returns the last character of the string. If a limit is supplied, returns a substring
@@ -99,16 +90,6 @@ class String
99
90
  # str.last(0) # => ""
100
91
  # str.last(6) # => "hello"
101
92
  def last(limit = 1)
102
- ActiveSupport::Deprecation.warn(
103
- "Calling String#last with a negative integer limit " \
104
- "will raise an ArgumentError in Rails 6.1."
105
- ) if limit < 0
106
- if limit == 0
107
- ""
108
- elsif limit >= size
109
- dup
110
- else
111
- from(-limit)
112
- end
93
+ self[[length - limit, 0].max, limit] || raise(ArgumentError, "negative limit")
113
94
  end
114
95
  end
@@ -18,6 +18,7 @@ class String
18
18
  # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
19
19
  # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC
20
20
  # "12/13/2012".to_time # => ArgumentError: argument out of range
21
+ # "1604326192".to_time # => ArgumentError: argument out of range
21
22
  def to_time(form = :local)
22
23
  parts = Date._parse(self, false)
23
24
  used_keys = %i(year mon mday hour min sec sec_fraction offset)
@@ -106,7 +106,7 @@ class String
106
106
  self.class.new.tap do |cut|
107
107
  cut_at = truncate_at - omission.bytesize
108
108
 
109
- scan(/\X/) do |grapheme|
109
+ each_grapheme_cluster do |grapheme|
110
110
  if cut.bytesize + grapheme.bytesize <= cut_at
111
111
  cut << grapheme
112
112
  else
@@ -30,6 +30,8 @@ class String
30
30
  # 'apple'.pluralize(2) # => "apples"
31
31
  # 'ley'.pluralize(:es) # => "leyes"
32
32
  # 'ley'.pluralize(1, :es) # => "ley"
33
+ #
34
+ # See ActiveSupport::Inflector.pluralize.
33
35
  def pluralize(count = nil, locale = :en)
34
36
  locale = count if count.is_a?(Symbol)
35
37
  if count == 1
@@ -53,28 +55,34 @@ class String
53
55
  # 'the blue mailmen'.singularize # => "the blue mailman"
54
56
  # 'CamelOctopi'.singularize # => "CamelOctopus"
55
57
  # 'leyes'.singularize(:es) # => "ley"
58
+ #
59
+ # See ActiveSupport::Inflector.singularize.
56
60
  def singularize(locale = :en)
57
61
  ActiveSupport::Inflector.singularize(self, locale)
58
62
  end
59
63
 
60
64
  # +constantize+ tries to find a declared constant with the name specified
61
65
  # in the string. It raises a NameError when the name is not in CamelCase
62
- # or is not initialized. See ActiveSupport::Inflector.constantize
66
+ # or is not initialized.
63
67
  #
64
68
  # 'Module'.constantize # => Module
65
69
  # 'Class'.constantize # => Class
66
70
  # 'blargle'.constantize # => NameError: wrong constant name blargle
71
+ #
72
+ # See ActiveSupport::Inflector.constantize.
67
73
  def constantize
68
74
  ActiveSupport::Inflector.constantize(self)
69
75
  end
70
76
 
71
77
  # +safe_constantize+ tries to find a declared constant with the name specified
72
78
  # in the string. It returns +nil+ when the name is not in CamelCase
73
- # or is not initialized. See ActiveSupport::Inflector.safe_constantize
79
+ # or is not initialized.
74
80
  #
75
81
  # 'Module'.safe_constantize # => Module
76
82
  # 'Class'.safe_constantize # => Class
77
83
  # 'blargle'.safe_constantize # => nil
84
+ #
85
+ # See ActiveSupport::Inflector.safe_constantize.
78
86
  def safe_constantize
79
87
  ActiveSupport::Inflector.safe_constantize(self)
80
88
  end
@@ -88,6 +96,10 @@ class String
88
96
  # 'active_record'.camelize(:lower) # => "activeRecord"
89
97
  # 'active_record/errors'.camelize # => "ActiveRecord::Errors"
90
98
  # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
99
+ #
100
+ # +camelize+ is also aliased as +camelcase+.
101
+ #
102
+ # See ActiveSupport::Inflector.camelize.
91
103
  def camelize(first_letter = :upper)
92
104
  case first_letter
93
105
  when :upper
@@ -108,11 +120,13 @@ class String
108
120
  # optional parameter +keep_id_suffix+ to true.
109
121
  # By default, this parameter is false.
110
122
  #
111
- # +titleize+ is also aliased as +titlecase+.
112
- #
113
123
  # 'man from the boondocks'.titleize # => "Man From The Boondocks"
114
124
  # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
115
125
  # 'string_ending_with_id'.titleize(keep_id_suffix: true) # => "String Ending With Id"
126
+ #
127
+ # +titleize+ is also aliased as +titlecase+.
128
+ #
129
+ # See ActiveSupport::Inflector.titleize.
116
130
  def titleize(keep_id_suffix: false)
117
131
  ActiveSupport::Inflector.titleize(self, keep_id_suffix: keep_id_suffix)
118
132
  end
@@ -124,6 +138,8 @@ class String
124
138
  #
125
139
  # 'ActiveModel'.underscore # => "active_model"
126
140
  # 'ActiveModel::Errors'.underscore # => "active_model/errors"
141
+ #
142
+ # See ActiveSupport::Inflector.underscore.
127
143
  def underscore
128
144
  ActiveSupport::Inflector.underscore(self)
129
145
  end
@@ -131,6 +147,8 @@ class String
131
147
  # Replaces underscores with dashes in the string.
132
148
  #
133
149
  # 'puni_puni'.dasherize # => "puni-puni"
150
+ #
151
+ # See ActiveSupport::Inflector.dasherize.
134
152
  def dasherize
135
153
  ActiveSupport::Inflector.dasherize(self)
136
154
  end
@@ -142,6 +160,8 @@ class String
142
160
  # '::Inflections'.demodulize # => "Inflections"
143
161
  # ''.demodulize # => ''
144
162
  #
163
+ # See ActiveSupport::Inflector.demodulize.
164
+ #
145
165
  # See also +deconstantize+.
146
166
  def demodulize
147
167
  ActiveSupport::Inflector.demodulize(self)
@@ -155,6 +175,8 @@ class String
155
175
  # '::String'.deconstantize # => ""
156
176
  # ''.deconstantize # => ""
157
177
  #
178
+ # See ActiveSupport::Inflector.deconstantize.
179
+ #
158
180
  # See also +demodulize+.
159
181
  def deconstantize
160
182
  ActiveSupport::Inflector.deconstantize(self)
@@ -192,6 +214,8 @@ class String
192
214
  #
193
215
  # <%= link_to(@person.name, person_path) %>
194
216
  # # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
217
+ #
218
+ # See ActiveSupport::Inflector.parameterize.
195
219
  def parameterize(separator: "-", preserve_case: false, locale: nil)
196
220
  ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
197
221
  end
@@ -202,6 +226,8 @@ class String
202
226
  # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
203
227
  # 'ham_and_egg'.tableize # => "ham_and_eggs"
204
228
  # 'fancyCategory'.tableize # => "fancy_categories"
229
+ #
230
+ # See ActiveSupport::Inflector.tableize.
205
231
  def tableize
206
232
  ActiveSupport::Inflector.tableize(self)
207
233
  end
@@ -212,6 +238,8 @@ class String
212
238
  #
213
239
  # 'ham_and_eggs'.classify # => "HamAndEgg"
214
240
  # 'posts'.classify # => "Post"
241
+ #
242
+ # See ActiveSupport::Inflector.classify.
215
243
  def classify
216
244
  ActiveSupport::Inflector.classify(self)
217
245
  end
@@ -232,7 +260,9 @@ class String
232
260
  # 'author_id'.humanize # => "Author"
233
261
  # 'author_id'.humanize(capitalize: false) # => "author"
234
262
  # '_id'.humanize # => "Id"
235
- # 'author_id'.humanize(keep_id_suffix: true) # => "Author Id"
263
+ # 'author_id'.humanize(keep_id_suffix: true) # => "Author id"
264
+ #
265
+ # See ActiveSupport::Inflector.humanize.
236
266
  def humanize(capitalize: true, keep_id_suffix: false)
237
267
  ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix)
238
268
  end
@@ -242,6 +272,8 @@ class String
242
272
  # 'what a Lovely Day'.upcase_first # => "What a Lovely Day"
243
273
  # 'w'.upcase_first # => "W"
244
274
  # ''.upcase_first # => ""
275
+ #
276
+ # See ActiveSupport::Inflector.upcase_first.
245
277
  def upcase_first
246
278
  ActiveSupport::Inflector.upcase_first(self)
247
279
  end
@@ -253,6 +285,8 @@ class String
253
285
  # 'Message'.foreign_key # => "message_id"
254
286
  # 'Message'.foreign_key(false) # => "messageid"
255
287
  # 'Admin::Post'.foreign_key # => "post_id"
288
+ #
289
+ # See ActiveSupport::Inflector.foreign_key.
256
290
  def foreign_key(separate_class_name_and_id_with_underscore = true)
257
291
  ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
258
292
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/string_inquirer"
4
+ require "active_support/environment_inquirer"
4
5
 
5
6
  class String
6
7
  # Wraps the current string in the <tt>ActiveSupport::StringInquirer</tt> class,
@@ -47,9 +47,9 @@ class String
47
47
  # iso_str.is_utf8? # => false
48
48
  def is_utf8?
49
49
  case encoding
50
- when Encoding::UTF_8
50
+ when Encoding::UTF_8, Encoding::US_ASCII
51
51
  valid_encoding?
52
- when Encoding::ASCII_8BIT, Encoding::US_ASCII
52
+ when Encoding::ASCII_8BIT
53
53
  dup.force_encoding(Encoding::UTF_8).valid_encoding?
54
54
  else
55
55
  false
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
- require "active_support/core_ext/kernel/singleton_class"
5
4
  require "active_support/core_ext/module/redefine_method"
6
5
  require "active_support/multibyte/unicode"
7
6
 
@@ -12,14 +11,6 @@ class ERB
12
11
  HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
13
12
  JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
14
13
 
15
- # Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name
16
- TAG_NAME_START_REGEXP_SET = "@:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}" \
17
- "\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \
18
- "\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}"
19
- TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_REGEXP_SET}]/
20
- TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_START_REGEXP_SET}\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}]/
21
- TAG_NAME_REPLACEMENT_CHAR = "_"
22
-
23
14
  # A utility method for escaping HTML tag characters.
24
15
  # This method is also aliased as <tt>h</tt>.
25
16
  #
@@ -93,7 +84,7 @@ class ERB
93
84
  # use inside HTML attributes.
94
85
  #
95
86
  # If your JSON is being used downstream for insertion into the DOM, be aware of
96
- # whether or not it is being inserted via +html()+. Most jQuery plugins do this.
87
+ # whether or not it is being inserted via <tt>html()</tt>. Most jQuery plugins do this.
97
88
  # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
98
89
  # content returned by your JSON.
99
90
  #
@@ -124,26 +115,6 @@ class ERB
124
115
  end
125
116
 
126
117
  module_function :json_escape
127
-
128
- # A utility method for escaping XML names of tags and names of attributes.
129
- #
130
- # xml_name_escape('1 < 2 & 3')
131
- # # => "1___2___3"
132
- #
133
- # It follows the requirements of the specification: https://www.w3.org/TR/REC-xml/#NT-Name
134
- def xml_name_escape(name)
135
- name = name.to_s
136
- return "" if name.blank?
137
-
138
- starting_char = name[0].gsub(TAG_NAME_START_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
139
-
140
- return starting_char if name.size == 1
141
-
142
- following_chars = name[1..-1].gsub(TAG_NAME_FOLLOWING_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
143
-
144
- starting_char + following_chars
145
- end
146
- module_function :xml_name_escape
147
118
  end
148
119
  end
149
120
 
@@ -159,7 +130,7 @@ class Numeric
159
130
  end
160
131
  end
161
132
 
162
- module ActiveSupport #:nodoc:
133
+ module ActiveSupport # :nodoc:
163
134
  class SafeBuffer < String
164
135
  UNSAFE_STRING_METHODS = %w(
165
136
  capitalize chomp chop delete delete_prefix delete_suffix
@@ -213,27 +184,30 @@ module ActiveSupport #:nodoc:
213
184
  end
214
185
 
215
186
  def concat(value)
216
- super(html_escape_interpolated_argument(value))
187
+ unless value.nil?
188
+ super(implicit_html_escape_interpolated_argument(value))
189
+ end
190
+ self
217
191
  end
218
192
  alias << concat
219
193
 
220
194
  def insert(index, value)
221
- super(index, html_escape_interpolated_argument(value))
195
+ super(index, implicit_html_escape_interpolated_argument(value))
222
196
  end
223
197
 
224
198
  def prepend(value)
225
- super(html_escape_interpolated_argument(value))
199
+ super(implicit_html_escape_interpolated_argument(value))
226
200
  end
227
201
 
228
202
  def replace(value)
229
- super(html_escape_interpolated_argument(value))
203
+ super(implicit_html_escape_interpolated_argument(value))
230
204
  end
231
205
 
232
206
  def []=(*args)
233
- if args.count == 3
234
- super(args[0], args[1], html_escape_interpolated_argument(args[2]))
207
+ if args.length == 3
208
+ super(args[0], args[1], implicit_html_escape_interpolated_argument(args[2]))
235
209
  else
236
- super(args[0], html_escape_interpolated_argument(args[1]))
210
+ super(args[0], implicit_html_escape_interpolated_argument(args[1]))
237
211
  end
238
212
  end
239
213
 
@@ -251,9 +225,9 @@ module ActiveSupport #:nodoc:
251
225
  def %(args)
252
226
  case args
253
227
  when Hash
254
- escaped_args = Hash[args.map { |k, arg| [k, html_escape_interpolated_argument(arg)] }]
228
+ escaped_args = args.transform_values { |arg| explicit_html_escape_interpolated_argument(arg) }
255
229
  else
256
- escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
230
+ escaped_args = Array(args).map { |arg| explicit_html_escape_interpolated_argument(arg) }
257
231
  end
258
232
 
259
233
  self.class.new(super(escaped_args))
@@ -291,39 +265,60 @@ module ActiveSupport #:nodoc:
291
265
  end
292
266
 
293
267
  UNSAFE_STRING_METHODS_WITH_BACKREF.each do |unsafe_method|
294
- if unsafe_method.respond_to?(unsafe_method)
295
- class_eval <<-EOT, __FILE__, __LINE__ + 1
296
- def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
297
- if block # if block
298
- to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params|
299
- set_block_back_references(block, $~) # set_block_back_references(block, $~)
300
- block.call(*params) # block.call(*params)
301
- } # }
302
- else # else
303
- to_str.#{unsafe_method}(*args) # to_str.gsub(*args)
304
- end # end
305
- end # end
306
-
307
- def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
308
- @html_safe = false # @html_safe = false
309
- if block # if block
310
- super(*args) { |*params| # super(*args) { |*params|
311
- set_block_back_references(block, $~) # set_block_back_references(block, $~)
312
- block.call(*params) # block.call(*params)
313
- } # }
314
- else # else
315
- super # super
316
- end # end
317
- end # end
318
- EOT
319
- end
268
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
269
+ def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
270
+ if block # if block
271
+ to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params|
272
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
273
+ block.call(*params) # block.call(*params)
274
+ } # }
275
+ else # else
276
+ to_str.#{unsafe_method}(*args) # to_str.gsub(*args)
277
+ end # end
278
+ end # end
279
+
280
+ def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
281
+ @html_safe = false # @html_safe = false
282
+ if block # if block
283
+ super(*args) { |*params| # super(*args) { |*params|
284
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
285
+ block.call(*params) # block.call(*params)
286
+ } # }
287
+ else # else
288
+ super # super
289
+ end # end
290
+ end # end
291
+ EOT
320
292
  end
321
293
 
322
294
  private
323
- def html_escape_interpolated_argument(arg)
295
+ def explicit_html_escape_interpolated_argument(arg)
324
296
  (!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
325
297
  end
326
298
 
299
+ def implicit_html_escape_interpolated_argument(arg)
300
+ if !html_safe? || arg.html_safe?
301
+ arg
302
+ else
303
+ arg_string = begin
304
+ arg.to_str
305
+ rescue NoMethodError => error
306
+ if error.name == :to_str
307
+ str = arg.to_s
308
+ ActiveSupport::Deprecation.warn <<~MSG.squish
309
+ Implicit conversion of #{arg.class} into String by ActiveSupport::SafeBuffer
310
+ is deprecated and will be removed in Rails 7.1.
311
+ You must explicitly cast it to a String.
312
+ MSG
313
+ str
314
+ else
315
+ raise
316
+ end
317
+ end
318
+ CGI.escapeHTML(arg_string)
319
+ end
320
+ end
321
+
327
322
  def set_block_back_references(block, match_data)
328
323
  block.binding.eval("proc { |m| $~ = m }").call(match_data)
329
324
  rescue ArgumentError
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class String
4
- alias_method :starts_with?, :start_with?
5
- alias_method :ends_with?, :end_with?
4
+ alias :starts_with? :start_with?
5
+ alias :ends_with? :end_with?
6
6
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Symbol
4
+ alias :starts_with? :start_with?
5
+ alias :ends_with? :end_with?
6
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/symbol/starts_ends_with"
@@ -6,6 +6,7 @@ require "active_support/time_with_zone"
6
6
  require "active_support/core_ext/time/zones"
7
7
  require "active_support/core_ext/date_and_time/calculations"
8
8
  require "active_support/core_ext/date/calculations"
9
+ require "active_support/core_ext/module/remove_method"
9
10
 
10
11
  class Time
11
12
  include DateAndTime::Calculations
@@ -41,8 +42,8 @@ class Time
41
42
 
42
43
  # Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
43
44
  # instances can be used when called with a single argument
44
- def at_with_coercion(*args)
45
- return at_without_coercion(*args) if args.size != 1
45
+ def at_with_coercion(*args, **kwargs)
46
+ return at_without_coercion(*args, **kwargs) if args.size != 1 || !kwargs.empty?
46
47
 
47
48
  # Time.at can be called with a time or numerical value
48
49
  time_or_number = args.first
@@ -107,6 +108,21 @@ class Time
107
108
  subsec
108
109
  end
109
110
 
111
+ unless Time.method_defined?(:floor)
112
+ def floor(precision = 0)
113
+ change(nsec: 0) + subsec.floor(precision)
114
+ end
115
+ end
116
+
117
+ # Restricted Ruby version due to a bug in `Time#ceil`
118
+ # See https://bugs.ruby-lang.org/issues/17025 for more details
119
+ if RUBY_VERSION <= "2.8"
120
+ remove_possible_method :ceil
121
+ def ceil(precision = 0)
122
+ change(nsec: 0) + subsec.ceil(precision)
123
+ end
124
+ end
125
+
110
126
  # Returns a new Time where one or more of the elements have been changed according
111
127
  # to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
112
128
  # <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only
@@ -143,6 +159,8 @@ class Time
143
159
  ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, new_offset)
144
160
  elsif utc?
145
161
  ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec)
162
+ elsif zone&.respond_to?(:utc_to_local)
163
+ ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, zone)
146
164
  elsif zone
147
165
  ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec)
148
166
  else
@@ -259,7 +277,7 @@ class Time
259
277
  end
260
278
  alias :at_end_of_minute :end_of_minute
261
279
 
262
- def plus_with_duration(other) #:nodoc:
280
+ def plus_with_duration(other) # :nodoc:
263
281
  if ActiveSupport::Duration === other
264
282
  other.since(self)
265
283
  else
@@ -269,7 +287,7 @@ class Time
269
287
  alias_method :plus_without_duration, :+
270
288
  alias_method :+, :plus_with_duration
271
289
 
272
- def minus_with_duration(other) #:nodoc:
290
+ def minus_with_duration(other) # :nodoc:
273
291
  if ActiveSupport::Duration === other
274
292
  other.until(self)
275
293
  else
@@ -287,7 +305,7 @@ class Time
287
305
  other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
288
306
  end
289
307
  alias_method :minus_without_coercion, :-
290
- alias_method :-, :minus_with_coercion
308
+ alias_method :-, :minus_with_coercion # rubocop:disable Lint/DuplicateMethods
291
309
 
292
310
  # Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
293
311
  # can be chronologically compared with a Time
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "time"
3
4
  require "active_support/inflector/methods"
4
5
  require "active_support/values/time_zone"
5
6
 
6
7
  class Time
7
8
  DATE_FORMATS = {
8
9
  db: "%Y-%m-%d %H:%M:%S",
10
+ inspect: "%Y-%m-%d %H:%M:%S.%9N %z",
9
11
  number: "%Y%m%d%H%M%S",
10
12
  nsec: "%Y%m%d%H%M%S%9N",
11
13
  usec: "%Y%m%d%H%M%S%6N",
@@ -25,12 +27,12 @@ class Time
25
27
 
26
28
  # Converts to a formatted string. See DATE_FORMATS for built-in formats.
27
29
  #
28
- # This method is aliased to <tt>to_s</tt>.
30
+ # This method is aliased to <tt>to_fs</tt>.
29
31
  #
30
32
  # time = Time.now # => 2007-01-18 06:10:17 -06:00
31
33
  #
32
34
  # time.to_formatted_s(:time) # => "06:10"
33
- # time.to_s(:time) # => "06:10"
35
+ # time.to_fs(:time) # => "06:10"
34
36
  #
35
37
  # time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
36
38
  # time.to_formatted_s(:number) # => "20070118061017"
@@ -52,11 +54,12 @@ class Time
52
54
  if formatter = DATE_FORMATS[format]
53
55
  formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
54
56
  else
57
+ # Change to `to_s` when deprecation is gone. Also deprecate `to_default_s`.
55
58
  to_default_s
56
59
  end
57
60
  end
61
+ alias_method :to_fs, :to_formatted_s
58
62
  alias_method :to_default_s, :to_s
59
- alias_method :to_s, :to_formatted_s
60
63
 
61
64
  # Returns a formatted string of the offset from UTC, or an alternative
62
65
  # string if the time zone is already UTC.
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ class Time
6
+ NOT_SET = Object.new # :nodoc:
7
+ def to_s(format = NOT_SET) # :nodoc:
8
+ if formatter = DATE_FORMATS[format]
9
+ ActiveSupport::Deprecation.warn(
10
+ "Time#to_s(#{format.inspect}) is deprecated. Please use Time#to_formatted_s(#{format.inspect}) instead."
11
+ )
12
+ formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
13
+ elsif format == NOT_SET
14
+ to_default_s
15
+ else
16
+ ActiveSupport::Deprecation.warn(
17
+ "Time#to_s(#{format.inspect}) is deprecated. Please use Time#to_formatted_s(#{format.inspect}) instead."
18
+ )
19
+ to_default_s
20
+ end
21
+ end
22
+ end
@@ -12,7 +12,7 @@ class Time
12
12
  # Returns the TimeZone for the current request, if this has been set (via Time.zone=).
13
13
  # If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
14
14
  def zone
15
- Thread.current[:time_zone] || zone_default
15
+ ::ActiveSupport::IsolatedExecutionState[:time_zone] || zone_default
16
16
  end
17
17
 
18
18
  # Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
@@ -39,7 +39,7 @@ class Time
39
39
  # end
40
40
  # end
41
41
  def zone=(time_zone)
42
- Thread.current[:time_zone] = find_zone!(time_zone)
42
+ ::ActiveSupport::IsolatedExecutionState[:time_zone] = find_zone!(time_zone)
43
43
  end
44
44
 
45
45
  # Allows override of <tt>Time.zone</tt> locally inside supplied block;
@@ -80,24 +80,9 @@ class Time
80
80
  # Time.find_zone! false # => false
81
81
  # Time.find_zone! "NOT-A-TIMEZONE" # => ArgumentError: Invalid Timezone: NOT-A-TIMEZONE
82
82
  def find_zone!(time_zone)
83
- if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone)
84
- time_zone
85
- else
86
- # Look up the timezone based on the identifier (unless we've been
87
- # passed a TZInfo::Timezone)
88
- unless time_zone.respond_to?(:period_for_local)
89
- time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
90
- end
83
+ return time_zone unless time_zone
91
84
 
92
- # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
93
- if time_zone.is_a?(ActiveSupport::TimeZone)
94
- time_zone
95
- else
96
- ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
97
- end
98
- end
99
- rescue TZInfo::InvalidTimezoneIdentifier
100
- raise ArgumentError, "Invalid Timezone: #{time_zone}"
85
+ ActiveSupport::TimeZone[time_zone] || raise(ArgumentError, "Invalid Timezone: #{time_zone}")
101
86
  end
102
87
 
103
88
  # Returns a TimeZone instance matching the time zone provided.