activesupport 7.0.8 → 7.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +142 -428
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +3 -1
  7. data/lib/active_support/backtrace_cleaner.rb +39 -7
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/broadcast_logger.rb +251 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +49 -17
  14. data/lib/active_support/cache/mem_cache_store.rb +94 -128
  15. data/lib/active_support/cache/memory_store.rb +80 -25
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +165 -152
  18. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +29 -14
  20. data/lib/active_support/cache.rb +363 -291
  21. data/lib/active_support/callbacks.rb +118 -134
  22. data/lib/active_support/code_generator.rb +15 -10
  23. data/lib/active_support/concern.rb +4 -2
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/configurable.rb +10 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +1 -2
  28. data/lib/active_support/core_ext/array.rb +0 -1
  29. data/lib/active_support/core_ext/class/subclasses.rb +17 -34
  30. data/lib/active_support/core_ext/date/blank.rb +4 -0
  31. data/lib/active_support/core_ext/date/conversions.rb +1 -2
  32. data/lib/active_support/core_ext/date.rb +0 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  34. data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
  35. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  36. data/lib/active_support/core_ext/date_time/conversions.rb +2 -2
  37. data/lib/active_support/core_ext/date_time.rb +0 -1
  38. data/lib/active_support/core_ext/digest/uuid.rb +7 -10
  39. data/lib/active_support/core_ext/enumerable.rb +3 -75
  40. data/lib/active_support/core_ext/erb/util.rb +201 -0
  41. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  42. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  43. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  44. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  45. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  46. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  47. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  48. data/lib/active_support/core_ext/module/delegation.rb +20 -119
  49. data/lib/active_support/core_ext/module/deprecation.rb +12 -12
  50. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  51. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  52. data/lib/active_support/core_ext/numeric/conversions.rb +5 -3
  53. data/lib/active_support/core_ext/numeric.rb +0 -1
  54. data/lib/active_support/core_ext/object/blank.rb +45 -1
  55. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  56. data/lib/active_support/core_ext/object/duplicable.rb +24 -15
  57. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  58. data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
  59. data/lib/active_support/core_ext/object/json.rb +17 -7
  60. data/lib/active_support/core_ext/object/with.rb +46 -0
  61. data/lib/active_support/core_ext/object/with_options.rb +4 -4
  62. data/lib/active_support/core_ext/object.rb +1 -0
  63. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  64. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  65. data/lib/active_support/core_ext/pathname.rb +1 -0
  66. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  67. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  68. data/lib/active_support/core_ext/range.rb +1 -2
  69. data/lib/active_support/core_ext/securerandom.rb +1 -5
  70. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  71. data/lib/active_support/core_ext/string/filters.rb +21 -15
  72. data/lib/active_support/core_ext/string/indent.rb +1 -1
  73. data/lib/active_support/core_ext/string/inflections.rb +16 -5
  74. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  75. data/lib/active_support/core_ext/string/output_safety.rb +34 -177
  76. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  77. data/lib/active_support/core_ext/time/calculations.rb +36 -30
  78. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  79. data/lib/active_support/core_ext/time/conversions.rb +1 -3
  80. data/lib/active_support/core_ext/time/zones.rb +4 -4
  81. data/lib/active_support/core_ext/time.rb +0 -1
  82. data/lib/active_support/core_ext.rb +0 -1
  83. data/lib/active_support/current_attributes.rb +53 -46
  84. data/lib/active_support/deep_mergeable.rb +53 -0
  85. data/lib/active_support/delegation.rb +202 -0
  86. data/lib/active_support/dependencies/autoload.rb +9 -16
  87. data/lib/active_support/deprecation/behaviors.rb +65 -42
  88. data/lib/active_support/deprecation/constant_accessor.rb +47 -25
  89. data/lib/active_support/deprecation/deprecators.rb +104 -0
  90. data/lib/active_support/deprecation/disallowed.rb +3 -5
  91. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  92. data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
  93. data/lib/active_support/deprecation/reporting.rb +49 -27
  94. data/lib/active_support/deprecation.rb +39 -9
  95. data/lib/active_support/deprecator.rb +7 -0
  96. data/lib/active_support/descendants_tracker.rb +66 -172
  97. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  98. data/lib/active_support/duration/iso8601_serializer.rb +1 -4
  99. data/lib/active_support/duration.rb +13 -7
  100. data/lib/active_support/encrypted_configuration.rb +30 -9
  101. data/lib/active_support/encrypted_file.rb +9 -4
  102. data/lib/active_support/environment_inquirer.rb +22 -2
  103. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  104. data/lib/active_support/error_reporter.rb +160 -36
  105. data/lib/active_support/evented_file_update_checker.rb +0 -1
  106. data/lib/active_support/execution_wrapper.rb +4 -5
  107. data/lib/active_support/file_update_checker.rb +5 -3
  108. data/lib/active_support/fork_tracker.rb +4 -32
  109. data/lib/active_support/gem_version.rb +3 -3
  110. data/lib/active_support/gzip.rb +2 -0
  111. data/lib/active_support/hash_with_indifferent_access.rb +41 -25
  112. data/lib/active_support/html_safe_translation.rb +19 -6
  113. data/lib/active_support/i18n.rb +1 -1
  114. data/lib/active_support/i18n_railtie.rb +20 -13
  115. data/lib/active_support/inflector/inflections.rb +2 -0
  116. data/lib/active_support/inflector/methods.rb +23 -11
  117. data/lib/active_support/inflector/transliterate.rb +3 -1
  118. data/lib/active_support/isolated_execution_state.rb +26 -22
  119. data/lib/active_support/json/decoding.rb +2 -1
  120. data/lib/active_support/json/encoding.rb +25 -43
  121. data/lib/active_support/key_generator.rb +9 -1
  122. data/lib/active_support/lazy_load_hooks.rb +6 -4
  123. data/lib/active_support/locale/en.yml +2 -0
  124. data/lib/active_support/log_subscriber.rb +74 -34
  125. data/lib/active_support/logger.rb +22 -60
  126. data/lib/active_support/logger_thread_safe_level.rb +10 -32
  127. data/lib/active_support/message_encryptor.rb +197 -53
  128. data/lib/active_support/message_encryptors.rb +141 -0
  129. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  130. data/lib/active_support/message_pack/extensions.rb +305 -0
  131. data/lib/active_support/message_pack/serializer.rb +63 -0
  132. data/lib/active_support/message_pack.rb +50 -0
  133. data/lib/active_support/message_verifier.rb +220 -89
  134. data/lib/active_support/message_verifiers.rb +135 -0
  135. data/lib/active_support/messages/codec.rb +65 -0
  136. data/lib/active_support/messages/metadata.rb +111 -45
  137. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  138. data/lib/active_support/messages/rotator.rb +34 -32
  139. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  140. data/lib/active_support/multibyte/chars.rb +4 -2
  141. data/lib/active_support/multibyte/unicode.rb +9 -37
  142. data/lib/active_support/notifications/fanout.rb +248 -87
  143. data/lib/active_support/notifications/instrumenter.rb +93 -25
  144. data/lib/active_support/notifications.rb +29 -28
  145. data/lib/active_support/number_helper/number_converter.rb +16 -7
  146. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  147. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  148. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  149. data/lib/active_support/number_helper.rb +379 -318
  150. data/lib/active_support/option_merger.rb +2 -2
  151. data/lib/active_support/ordered_hash.rb +3 -3
  152. data/lib/active_support/ordered_options.rb +67 -15
  153. data/lib/active_support/parameter_filter.rb +84 -69
  154. data/lib/active_support/proxy_object.rb +8 -3
  155. data/lib/active_support/railtie.rb +25 -20
  156. data/lib/active_support/reloader.rb +12 -4
  157. data/lib/active_support/rescuable.rb +2 -0
  158. data/lib/active_support/secure_compare_rotator.rb +16 -9
  159. data/lib/active_support/string_inquirer.rb +4 -2
  160. data/lib/active_support/subscriber.rb +10 -27
  161. data/lib/active_support/syntax_error_proxy.rb +60 -0
  162. data/lib/active_support/tagged_logging.rb +64 -25
  163. data/lib/active_support/test_case.rb +156 -7
  164. data/lib/active_support/testing/assertions.rb +28 -12
  165. data/lib/active_support/testing/autorun.rb +0 -2
  166. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  167. data/lib/active_support/testing/deprecation.rb +20 -27
  168. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  169. data/lib/active_support/testing/isolation.rb +21 -9
  170. data/lib/active_support/testing/method_call_assertions.rb +7 -8
  171. data/lib/active_support/testing/parallelization/server.rb +3 -0
  172. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  173. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  174. data/lib/active_support/testing/stream.rb +1 -1
  175. data/lib/active_support/testing/strict_warnings.rb +43 -0
  176. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  177. data/lib/active_support/testing/time_helpers.rb +38 -16
  178. data/lib/active_support/time_with_zone.rb +12 -18
  179. data/lib/active_support/values/time_zone.rb +25 -14
  180. data/lib/active_support/version.rb +1 -1
  181. data/lib/active_support/xml_mini/jdom.rb +3 -10
  182. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  183. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  184. data/lib/active_support/xml_mini/rexml.rb +1 -1
  185. data/lib/active_support/xml_mini.rb +12 -3
  186. data/lib/active_support.rb +15 -3
  187. metadata +145 -24
  188. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  189. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
  190. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
  191. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  192. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
  193. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
  194. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  195. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
  196. data/lib/active_support/core_ext/uri.rb +0 -5
  197. data/lib/active_support/deprecation/instance_delegator.rb +0 -38
  198. data/lib/active_support/per_thread_registry.rb +0 -65
  199. data/lib/active_support/ruby_features.rb +0 -7
@@ -1,151 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "erb"
4
- require "active_support/core_ext/module/redefine_method"
3
+ require "active_support/core_ext/erb/util"
5
4
  require "active_support/multibyte/unicode"
6
5
 
7
- class ERB
8
- module Util
9
- HTML_ESCAPE = { "&" => "&amp;", ">" => "&gt;", "<" => "&lt;", '"' => "&quot;", "'" => "&#39;" }
10
- JSON_ESCAPE = { "&" => '\u0026', ">" => '\u003e', "<" => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
11
- HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
12
- JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
13
-
14
- # Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name
15
- 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}" \
16
- "\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \
17
- "\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}"
18
- TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_REGEXP_SET}]/
19
- TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_START_REGEXP_SET}\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}]/
20
- TAG_NAME_REPLACEMENT_CHAR = "_"
21
-
22
- # A utility method for escaping HTML tag characters.
23
- # This method is also aliased as <tt>h</tt>.
24
- #
25
- # puts html_escape('is a > 0 & a < 10?')
26
- # # => is a &gt; 0 &amp; a &lt; 10?
27
- def html_escape(s)
28
- unwrapped_html_escape(s).html_safe
29
- end
30
-
31
- silence_redefinition_of_method :h
32
- alias h html_escape
33
-
34
- module_function :h
35
-
36
- singleton_class.silence_redefinition_of_method :html_escape
37
- module_function :html_escape
38
-
39
- # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
40
- # This method is not for public consumption! Seriously!
41
- def unwrapped_html_escape(s) # :nodoc:
42
- s = s.to_s
43
- if s.html_safe?
44
- s
45
- else
46
- CGI.escapeHTML(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))
47
- end
48
- end
49
- module_function :unwrapped_html_escape
50
-
51
- # A utility method for escaping HTML without affecting existing escaped entities.
52
- #
53
- # html_escape_once('1 < 2 &amp; 3')
54
- # # => "1 &lt; 2 &amp; 3"
55
- #
56
- # html_escape_once('&lt;&lt; Accept & Checkout')
57
- # # => "&lt;&lt; Accept &amp; Checkout"
58
- def html_escape_once(s)
59
- result = ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
60
- s.html_safe? ? result.html_safe : result
61
- end
62
-
63
- module_function :html_escape_once
64
-
65
- # A utility method for escaping HTML entities in JSON strings. Specifically, the
66
- # &, > and < characters are replaced with their equivalent unicode escaped form -
67
- # \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
68
- # escaped as they are treated as newline characters in some JavaScript engines.
69
- # These sequences have identical meaning as the original characters inside the
70
- # context of a JSON string, so assuming the input is a valid and well-formed
71
- # JSON value, the output will have equivalent meaning when parsed:
72
- #
73
- # json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
74
- # # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
75
- #
76
- # json_escape(json)
77
- # # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
78
- #
79
- # JSON.parse(json) == JSON.parse(json_escape(json))
80
- # # => true
81
- #
82
- # The intended use case for this method is to escape JSON strings before including
83
- # them inside a script tag to avoid XSS vulnerability:
84
- #
85
- # <script>
86
- # var currentUser = <%= raw json_escape(current_user.to_json) %>;
87
- # </script>
88
- #
89
- # It is necessary to +raw+ the result of +json_escape+, so that quotation marks
90
- # don't get converted to <tt>&quot;</tt> entities. +json_escape+ doesn't
91
- # automatically flag the result as HTML safe, since the raw value is unsafe to
92
- # use inside HTML attributes.
93
- #
94
- # If your JSON is being used downstream for insertion into the DOM, be aware of
95
- # whether or not it is being inserted via <tt>html()</tt>. Most jQuery plugins do this.
96
- # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
97
- # content returned by your JSON.
98
- #
99
- # If you need to output JSON elsewhere in your HTML, you can just do something
100
- # like this, as any unsafe characters (including quotation marks) will be
101
- # automatically escaped for you:
102
- #
103
- # <div data-user-info="<%= current_user.to_json %>">...</div>
104
- #
105
- # WARNING: this helper only works with valid JSON. Using this on non-JSON values
106
- # will open up serious XSS vulnerabilities. For example, if you replace the
107
- # +current_user.to_json+ in the example above with user input instead, the browser
108
- # will happily <tt>eval()</tt> that string as JavaScript.
109
- #
110
- # The escaping performed in this method is identical to those performed in the
111
- # Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
112
- # set to true. Because this transformation is idempotent, this helper can be
113
- # applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
114
- #
115
- # Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
116
- # is enabled, or if you are unsure where your JSON string originated from, it
117
- # is recommended that you always apply this helper (other libraries, such as the
118
- # JSON gem, do not provide this kind of protection by default; also some gems
119
- # might override +to_json+ to bypass Active Support's encoder).
120
- def json_escape(s)
121
- result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
122
- s.html_safe? ? result.html_safe : result
123
- end
124
-
125
- module_function :json_escape
126
-
127
- # A utility method for escaping XML names of tags and names of attributes.
128
- #
129
- # xml_name_escape('1 < 2 & 3')
130
- # # => "1___2___3"
131
- #
132
- # It follows the requirements of the specification: https://www.w3.org/TR/REC-xml/#NT-Name
133
- def xml_name_escape(name)
134
- name = name.to_s
135
- return "" if name.blank?
136
-
137
- starting_char = name[0].gsub(TAG_NAME_START_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
138
-
139
- return starting_char if name.size == 1
140
-
141
- following_chars = name[1..-1].gsub(TAG_NAME_FOLLOWING_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
142
-
143
- starting_char + following_chars
144
- end
145
- module_function :xml_name_escape
146
- end
147
- end
148
-
149
6
  class Object
150
7
  def html_safe?
151
8
  false
@@ -162,7 +19,7 @@ module ActiveSupport # :nodoc:
162
19
  class SafeBuffer < String
163
20
  UNSAFE_STRING_METHODS = %w(
164
21
  capitalize chomp chop delete delete_prefix delete_suffix
165
- downcase lstrip next reverse rstrip scrub slice squeeze strip
22
+ downcase lstrip next reverse rstrip scrub squeeze strip
166
23
  succ swapcase tr tr_s unicode_normalize upcase
167
24
  )
168
25
 
@@ -174,7 +31,7 @@ module ActiveSupport # :nodoc:
174
31
  # Raised when ActiveSupport::SafeBuffer#safe_concat is called on unsafe buffers.
175
32
  class SafeConcatError < StandardError
176
33
  def initialize
177
- super "Could not concatenate to the buffer because it is not html safe."
34
+ super "Could not concatenate to the buffer because it is not HTML safe."
178
35
  end
179
36
  end
180
37
 
@@ -184,13 +41,26 @@ module ActiveSupport # :nodoc:
184
41
 
185
42
  return unless new_string
186
43
 
187
- new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
188
- new_safe_buffer.instance_variable_set :@html_safe, true
189
- new_safe_buffer
44
+ string_into_safe_buffer(new_string, true)
190
45
  else
191
46
  to_str[*args]
192
47
  end
193
48
  end
49
+ alias_method :slice, :[]
50
+
51
+ def slice!(*args)
52
+ new_string = super
53
+
54
+ return new_string if !html_safe? || new_string.nil?
55
+
56
+ string_into_safe_buffer(new_string, true)
57
+ end
58
+
59
+ def chr
60
+ return super unless html_safe?
61
+
62
+ string_into_safe_buffer(super, true)
63
+ end
194
64
 
195
65
  def safe_concat(value)
196
66
  raise SafeConcatError unless html_safe?
@@ -207,10 +77,6 @@ module ActiveSupport # :nodoc:
207
77
  @html_safe = other.html_safe?
208
78
  end
209
79
 
210
- def clone_empty
211
- self[0, 0]
212
- end
213
-
214
80
  def concat(value)
215
81
  unless value.nil?
216
82
  super(implicit_html_escape_interpolated_argument(value))
@@ -235,11 +101,11 @@ module ActiveSupport # :nodoc:
235
101
  super(implicit_html_escape_interpolated_argument(value))
236
102
  end
237
103
 
238
- def []=(*args)
239
- if args.length == 3
240
- super(args[0], args[1], implicit_html_escape_interpolated_argument(args[2]))
104
+ def []=(arg1, arg2, arg3 = nil)
105
+ if arg3
106
+ super(arg1, arg2, implicit_html_escape_interpolated_argument(arg3))
241
107
  else
242
- super(args[0], implicit_html_escape_interpolated_argument(args[1]))
108
+ super(arg1, implicit_html_escape_interpolated_argument(arg2))
243
109
  end
244
110
  end
245
111
 
@@ -247,7 +113,7 @@ module ActiveSupport # :nodoc:
247
113
  dup.concat(other)
248
114
  end
249
115
 
250
- def *(*)
116
+ def *(_)
251
117
  new_string = super
252
118
  new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
253
119
  new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
@@ -265,9 +131,9 @@ module ActiveSupport # :nodoc:
265
131
  self.class.new(super(escaped_args))
266
132
  end
267
133
 
268
- def html_safe?
269
- defined?(@html_safe) && @html_safe
270
- end
134
+ attr_reader :html_safe
135
+ alias_method :html_safe?, :html_safe
136
+ remove_method :html_safe
271
137
 
272
138
  def to_s
273
139
  self
@@ -332,22 +198,7 @@ module ActiveSupport # :nodoc:
332
198
  if !html_safe? || arg.html_safe?
333
199
  arg
334
200
  else
335
- arg_string = begin
336
- arg.to_str
337
- rescue NoMethodError => error
338
- if error.name == :to_str
339
- str = arg.to_s
340
- ActiveSupport::Deprecation.warn <<~MSG.squish
341
- Implicit conversion of #{arg.class} into String by ActiveSupport::SafeBuffer
342
- is deprecated and will be removed in Rails 7.1.
343
- You must explicitly cast it to a String.
344
- MSG
345
- str
346
- else
347
- raise
348
- end
349
- end
350
- CGI.escapeHTML(arg_string)
201
+ CGI.escapeHTML(arg.to_str)
351
202
  end
352
203
  end
353
204
 
@@ -356,6 +207,12 @@ module ActiveSupport # :nodoc:
356
207
  rescue ArgumentError
357
208
  # Can't create binding from C level Proc
358
209
  end
210
+
211
+ def string_into_safe_buffer(new_string, is_html_safe)
212
+ new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
213
+ new_safe_buffer.instance_variable_set :@html_safe, is_html_safe
214
+ new_safe_buffer
215
+ end
359
216
  end
360
217
  end
361
218
 
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Thread::Backtrace::Location # :nodoc:
4
+ if defined?(ErrorHighlight) && Gem::Version.new(ErrorHighlight::VERSION) >= Gem::Version.new("0.4.0")
5
+ def spot(ex)
6
+ ErrorHighlight.spot(ex, backtrace_location: self)
7
+ end
8
+ else
9
+ def spot(ex)
10
+ end
11
+ end
12
+ end
@@ -42,20 +42,20 @@ class Time
42
42
 
43
43
  # Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
44
44
  # instances can be used when called with a single argument
45
- def at_with_coercion(*args, **kwargs)
46
- return at_without_coercion(*args, **kwargs) if args.size != 1 || !kwargs.empty?
47
-
48
- # Time.at can be called with a time or numerical value
49
- time_or_number = args.first
50
-
51
- if time_or_number.is_a?(ActiveSupport::TimeWithZone)
52
- at_without_coercion(time_or_number.to_r).getlocal
53
- elsif time_or_number.is_a?(DateTime)
54
- at_without_coercion(time_or_number.to_f).getlocal
45
+ def at_with_coercion(time_or_number, *args)
46
+ if args.empty?
47
+ if time_or_number.is_a?(ActiveSupport::TimeWithZone)
48
+ at_without_coercion(time_or_number.to_r).getlocal
49
+ elsif time_or_number.is_a?(DateTime)
50
+ at_without_coercion(time_or_number.to_f).getlocal
51
+ else
52
+ at_without_coercion(time_or_number)
53
+ end
55
54
  else
56
- at_without_coercion(time_or_number)
55
+ at_without_coercion(time_or_number, *args)
57
56
  end
58
57
  end
58
+ ruby2_keywords :at_with_coercion
59
59
  alias_method :at_without_coercion, :at
60
60
  alias_method :at, :at_with_coercion
61
61
 
@@ -108,21 +108,6 @@ class Time
108
108
  subsec
109
109
  end
110
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
-
126
111
  # Returns a new Time where one or more of the elements have been changed according
127
112
  # to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
128
113
  # <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only
@@ -159,10 +144,26 @@ class Time
159
144
  ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, new_offset)
160
145
  elsif utc?
161
146
  ::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)
147
+ elsif zone.respond_to?(:utc_to_local)
148
+ new_time = ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, zone)
149
+
150
+ # When there are two occurrences of a nominal time due to DST ending,
151
+ # `Time.new` chooses the first chronological occurrence (the one with a
152
+ # larger UTC offset). However, for `change`, we want to choose the
153
+ # occurrence that matches this time's UTC offset.
154
+ #
155
+ # If the new time's UTC offset is larger than this time's UTC offset, the
156
+ # new time might be a first chronological occurrence. So we add the offset
157
+ # difference to fast-forward the new time, and check if the result has the
158
+ # desired UTC offset (i.e. is the second chronological occurrence).
159
+ offset_difference = new_time.utc_offset - utc_offset
160
+ if offset_difference > 0 && (new_time_2 = new_time + offset_difference).utc_offset == utc_offset
161
+ new_time_2
162
+ else
163
+ new_time
164
+ end
164
165
  elsif zone
165
- ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec)
166
+ ::Time.local(new_sec, new_min, new_hour, new_day, new_month, new_year, nil, nil, isdst, nil)
166
167
  else
167
168
  ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, utc_offset)
168
169
  end
@@ -318,7 +319,12 @@ class Time
318
319
  if other.class == Time
319
320
  compare_without_coercion(other)
320
321
  elsif other.is_a?(Time)
321
- compare_without_coercion(other.to_time)
322
+ # also avoid ActiveSupport::TimeWithZone#to_time before Rails 8.0
323
+ if other.respond_to?(:comparable_time)
324
+ compare_without_coercion(other.comparable_time)
325
+ else
326
+ compare_without_coercion(other.to_time)
327
+ end
322
328
  else
323
329
  to_datetime <=> other
324
330
  end
@@ -13,4 +13,20 @@ class Time
13
13
  def to_time
14
14
  preserve_timezone ? self : getlocal
15
15
  end
16
+
17
+ def preserve_timezone # :nodoc:
18
+ active_support_local_zone == zone || super
19
+ end
20
+
21
+ private
22
+ @@active_support_local_tz = nil
23
+
24
+ def active_support_local_zone
25
+ @@active_support_local_zone = nil if @@active_support_local_tz != ENV["TZ"]
26
+ @@active_support_local_zone ||=
27
+ begin
28
+ @@active_support_local_tz = ENV["TZ"]
29
+ Time.new.zone
30
+ end
31
+ end
16
32
  end
@@ -54,12 +54,10 @@ class Time
54
54
  if formatter = DATE_FORMATS[format]
55
55
  formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
56
56
  else
57
- # Change to `to_s` when deprecation is gone. Also deprecate `to_default_s`.
58
- to_default_s
57
+ to_s
59
58
  end
60
59
  end
61
60
  alias_method :to_formatted_s, :to_fs
62
- alias_method :to_default_s, :to_s
63
61
 
64
62
  # Returns a formatted string of the offset from UTC, or an alternative
65
63
  # string if the time zone is already UTC.
@@ -19,10 +19,10 @@ class Time
19
19
  #
20
20
  # This method accepts any of the following:
21
21
  #
22
- # * A Rails TimeZone object.
23
- # * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
24
- # * A <tt>TZInfo::Timezone</tt> object.
25
- # * An identifier for a <tt>TZInfo::Timezone</tt> object (e.g., "America/New_York").
22
+ # * A \Rails TimeZone object.
23
+ # * An identifier for a \Rails TimeZone object (e.g., "Eastern \Time (US & Canada)", <tt>-5.hours</tt>).
24
+ # * A +TZInfo::Timezone+ object.
25
+ # * An identifier for a +TZInfo::Timezone+ object (e.g., "America/New_York").
26
26
  #
27
27
  # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis and reset it when the request is done.
28
28
  # <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone:
@@ -4,5 +4,4 @@ require "active_support/core_ext/time/acts_like"
4
4
  require "active_support/core_ext/time/calculations"
5
5
  require "active_support/core_ext/time/compatibility"
6
6
  require "active_support/core_ext/time/conversions"
7
- require "active_support/core_ext/time/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
8
7
  require "active_support/core_ext/time/zones"
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Dir.glob(File.expand_path("core_ext/*.rb", __dir__)).sort.each do |path|
4
- next if path.end_with?("core_ext/uri.rb")
5
4
  require path
6
5
  end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/callbacks"
4
+ require "active_support/core_ext/object/with"
4
5
  require "active_support/core_ext/enumerable"
5
6
  require "active_support/core_ext/module/delegation"
6
7
 
7
8
  module ActiveSupport
9
+ # = Current Attributes
10
+ #
8
11
  # Abstract super class that provides a thread-isolated attributes singleton, which resets automatically
9
12
  # before and after each request. This allows you to keep all the per-request attributes easily
10
13
  # available to the whole system.
@@ -90,6 +93,10 @@ module ActiveSupport
90
93
  include ActiveSupport::Callbacks
91
94
  define_callbacks :reset
92
95
 
96
+ INVALID_ATTRIBUTE_NAMES = [:set, :reset, :resets, :instance, :before_reset, :after_reset, :reset_all, :clear_all] # :nodoc:
97
+
98
+ NOT_SET = Object.new.freeze # :nodoc:
99
+
93
100
  class << self
94
101
  # Returns singleton instance for this class in this thread. If none exists, one is created.
95
102
  def instance
@@ -97,7 +104,19 @@ module ActiveSupport
97
104
  end
98
105
 
99
106
  # Declares one or more attributes that will be given both class and instance accessor methods.
100
- def attribute(*names)
107
+ #
108
+ # ==== Options
109
+ #
110
+ # * <tt>:default</tt> - The default value for the attributes. If the value
111
+ # is a proc or lambda, it will be called whenever an instance is
112
+ # constructed. Otherwise, the value will be duplicated with +#dup+.
113
+ # Default values are re-assigned when the attributes are reset.
114
+ def attribute(*names, default: NOT_SET)
115
+ invalid_attribute_names = names.map(&:to_sym) & INVALID_ATTRIBUTE_NAMES
116
+ if invalid_attribute_names.any?
117
+ raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}"
118
+ end
119
+
101
120
  ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
102
121
  names.each do |name|
103
122
  owner.define_cached_method(name, namespace: :current_attributes) do |batch|
@@ -115,32 +134,20 @@ module ActiveSupport
115
134
  end
116
135
  end
117
136
 
118
- ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
119
- names.each do |name|
120
- owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch|
121
- batch <<
122
- "def #{name}" <<
123
- "instance.#{name}" <<
124
- "end"
125
- end
126
- owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch|
127
- batch <<
128
- "def #{name}=(value)" <<
129
- "instance.#{name} = value" <<
130
- "end"
131
- end
132
- end
133
- end
137
+ Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
138
+ Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
139
+
140
+ self.defaults = defaults.merge(names.index_with { default })
134
141
  end
135
142
 
136
- # Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
137
- def before_reset(&block)
138
- set_callback :reset, :before, &block
143
+ # Calls this callback before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
144
+ def before_reset(*methods, &block)
145
+ set_callback :reset, :before, *methods, &block
139
146
  end
140
147
 
141
- # Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
142
- def resets(&block)
143
- set_callback :reset, :after, &block
148
+ # Calls this callback after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
149
+ def resets(*methods, &block)
150
+ set_callback :reset, :after, *methods, &block
144
151
  end
145
152
  alias_method :after_reset, :resets
146
153
 
@@ -168,25 +175,29 @@ module ActiveSupport
168
175
  @current_instances_key ||= name.to_sym
169
176
  end
170
177
 
171
- def method_missing(name, *args, &block)
172
- # Caches the method definition as a singleton method of the receiver.
173
- #
174
- # By letting #delegate handle it, we avoid an enclosure that'll capture args.
175
- singleton_class.delegate name, to: :instance
176
-
177
- send(name, *args, &block)
178
+ def method_missing(name, ...)
179
+ instance.public_send(name, ...)
178
180
  end
179
- ruby2_keywords(:method_missing)
180
181
 
181
182
  def respond_to_missing?(name, _)
182
- super || instance.respond_to?(name)
183
+ instance.respond_to?(name) || super
184
+ end
185
+
186
+ def method_added(name)
187
+ super
188
+ return if name == :initialize
189
+ return unless public_method_defined?(name)
190
+ return if respond_to?(name, true)
191
+ Delegation.generate(singleton_class, [name], to: :instance, as: self, nilable: false)
183
192
  end
184
193
  end
185
194
 
195
+ class_attribute :defaults, instance_writer: false, default: {}.freeze
196
+
186
197
  attr_accessor :attributes
187
198
 
188
199
  def initialize
189
- @attributes = {}
200
+ @attributes = resolve_defaults
190
201
  end
191
202
 
192
203
  # Expose one or more attributes within a block. Old values are returned after the block concludes.
@@ -199,28 +210,24 @@ module ActiveSupport
199
210
  # end
200
211
  # end
201
212
  # end
202
- def set(set_attributes)
203
- old_attributes = compute_attributes(set_attributes.keys)
204
- assign_attributes(set_attributes)
205
- yield
206
- ensure
207
- assign_attributes(old_attributes)
213
+ def set(attributes, &block)
214
+ with(**attributes, &block)
208
215
  end
209
216
 
210
217
  # Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
211
218
  def reset
212
219
  run_callbacks :reset do
213
- self.attributes = {}
220
+ self.attributes = resolve_defaults
214
221
  end
215
222
  end
216
223
 
217
224
  private
218
- def assign_attributes(new_attributes)
219
- new_attributes.each { |key, value| public_send("#{key}=", value) }
220
- end
221
-
222
- def compute_attributes(keys)
223
- keys.index_with { |key| public_send(key) }
225
+ def resolve_defaults
226
+ defaults.each_with_object({}) do |(key, value), result|
227
+ if value != NOT_SET
228
+ result[key] = Proc === value ? value.call : value.dup
229
+ end
230
+ end
224
231
  end
225
232
  end
226
233
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # Provides +deep_merge+ and +deep_merge!+ methods. Expects the including class
5
+ # to provide a <tt>merge!(other, &block)</tt> method.
6
+ module DeepMergeable # :nodoc:
7
+ # Returns a new instance with the values from +other+ merged recursively.
8
+ #
9
+ # class Hash
10
+ # include ActiveSupport::DeepMergeable
11
+ # end
12
+ #
13
+ # hash_1 = { a: true, b: { c: [1, 2, 3] } }
14
+ # hash_2 = { a: false, b: { x: [3, 4, 5] } }
15
+ #
16
+ # hash_1.deep_merge(hash_2)
17
+ # # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
18
+ #
19
+ # A block can be provided to merge non-<tt>DeepMergeable</tt> values:
20
+ #
21
+ # hash_1 = { a: 100, b: 200, c: { c1: 100 } }
22
+ # hash_2 = { b: 250, c: { c1: 200 } }
23
+ #
24
+ # hash_1.deep_merge(hash_2) do |key, this_val, other_val|
25
+ # this_val + other_val
26
+ # end
27
+ # # => { a: 100, b: 450, c: { c1: 300 } }
28
+ #
29
+ def deep_merge(other, &block)
30
+ dup.deep_merge!(other, &block)
31
+ end
32
+
33
+ # Same as #deep_merge, but modifies +self+.
34
+ def deep_merge!(other, &block)
35
+ merge!(other) do |key, this_val, other_val|
36
+ if this_val.is_a?(DeepMergeable) && this_val.deep_merge?(other_val)
37
+ this_val.deep_merge(other_val, &block)
38
+ elsif block_given?
39
+ block.call(key, this_val, other_val)
40
+ else
41
+ other_val
42
+ end
43
+ end
44
+ end
45
+
46
+ # Returns true if +other+ can be deep merged into +self+. Classes may
47
+ # override this method to restrict or expand the domain of deep mergeable
48
+ # values. Defaults to checking that +other+ is of type +self.class+.
49
+ def deep_merge?(other)
50
+ other.is_a?(self.class)
51
+ end
52
+ end
53
+ end