activesupport 7.0.8.1 → 7.2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +143 -429
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_support/actionable_error.rb +3 -1
- data/lib/active_support/array_inquirer.rb +3 -1
- data/lib/active_support/backtrace_cleaner.rb +39 -7
- data/lib/active_support/benchmarkable.rb +1 -0
- data/lib/active_support/broadcast_logger.rb +251 -0
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +49 -17
- data/lib/active_support/cache/mem_cache_store.rb +94 -128
- data/lib/active_support/cache/memory_store.rb +80 -25
- data/lib/active_support/cache/null_store.rb +6 -0
- data/lib/active_support/cache/redis_cache_store.rb +165 -152
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +29 -14
- data/lib/active_support/cache.rb +363 -291
- data/lib/active_support/callbacks.rb +118 -134
- data/lib/active_support/code_generator.rb +15 -10
- data/lib/active_support/concern.rb +4 -2
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/configurable.rb +10 -0
- data/lib/active_support/core_ext/array/conversions.rb +1 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/class/subclasses.rb +17 -34
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +1 -2
- data/lib/active_support/core_ext/date.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -2
- data/lib/active_support/core_ext/date_time.rb +0 -1
- data/lib/active_support/core_ext/digest/uuid.rb +7 -10
- data/lib/active_support/core_ext/enumerable.rb +3 -75
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -119
- data/lib/active_support/core_ext/module/deprecation.rb +12 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +5 -3
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
- data/lib/active_support/core_ext/object/duplicable.rb +24 -15
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
- data/lib/active_support/core_ext/object/json.rb +17 -7
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +4 -4
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +2 -0
- data/lib/active_support/core_ext/pathname.rb +1 -0
- data/lib/active_support/core_ext/range/conversions.rb +28 -7
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +1 -5
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +16 -5
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +34 -177
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +36 -30
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +1 -3
- data/lib/active_support/core_ext/time/zones.rb +4 -4
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +53 -46
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +9 -16
- data/lib/active_support/deprecation/behaviors.rb +65 -42
- data/lib/active_support/deprecation/constant_accessor.rb +47 -25
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +3 -5
- data/lib/active_support/deprecation/method_wrappers.rb +6 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
- data/lib/active_support/deprecation/reporting.rb +49 -27
- data/lib/active_support/deprecation.rb +39 -9
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +66 -172
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -4
- data/lib/active_support/duration.rb +13 -7
- data/lib/active_support/encrypted_configuration.rb +30 -9
- data/lib/active_support/encrypted_file.rb +9 -4
- data/lib/active_support/environment_inquirer.rb +22 -2
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +160 -36
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/execution_wrapper.rb +4 -5
- data/lib/active_support/file_update_checker.rb +5 -3
- data/lib/active_support/fork_tracker.rb +4 -32
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +41 -25
- data/lib/active_support/html_safe_translation.rb +19 -6
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +20 -13
- data/lib/active_support/inflector/inflections.rb +2 -0
- data/lib/active_support/inflector/methods.rb +23 -11
- data/lib/active_support/inflector/transliterate.rb +3 -1
- data/lib/active_support/isolated_execution_state.rb +26 -22
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +25 -43
- data/lib/active_support/key_generator.rb +9 -1
- data/lib/active_support/lazy_load_hooks.rb +6 -4
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber.rb +74 -34
- data/lib/active_support/logger.rb +22 -60
- data/lib/active_support/logger_thread_safe_level.rb +10 -32
- data/lib/active_support/message_encryptor.rb +197 -53
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +305 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +220 -89
- data/lib/active_support/message_verifiers.rb +135 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +111 -45
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +4 -2
- data/lib/active_support/multibyte/unicode.rb +9 -37
- data/lib/active_support/notifications/fanout.rb +248 -87
- data/lib/active_support/notifications/instrumenter.rb +93 -25
- data/lib/active_support/notifications.rb +29 -28
- data/lib/active_support/number_helper/number_converter.rb +16 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
- data/lib/active_support/number_helper.rb +379 -318
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +67 -15
- data/lib/active_support/parameter_filter.rb +84 -69
- data/lib/active_support/proxy_object.rb +8 -3
- data/lib/active_support/railtie.rb +25 -20
- data/lib/active_support/reloader.rb +12 -4
- data/lib/active_support/rescuable.rb +2 -0
- data/lib/active_support/secure_compare_rotator.rb +16 -9
- data/lib/active_support/string_inquirer.rb +4 -2
- data/lib/active_support/subscriber.rb +10 -27
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +64 -25
- data/lib/active_support/test_case.rb +156 -7
- data/lib/active_support/testing/assertions.rb +28 -12
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/deprecation.rb +20 -27
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +21 -9
- data/lib/active_support/testing/method_call_assertions.rb +7 -8
- data/lib/active_support/testing/parallelization/server.rb +3 -0
- data/lib/active_support/testing/parallelize_executor.rb +8 -3
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/strict_warnings.rb +43 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +38 -16
- data/lib/active_support/time_with_zone.rb +12 -18
- data/lib/active_support/values/time_zone.rb +25 -14
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -10
- data/lib/active_support/xml_mini/nokogiri.rb +1 -1
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +1 -1
- data/lib/active_support/xml_mini.rb +12 -3
- data/lib/active_support.rb +15 -3
- metadata +145 -24
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
- data/lib/active_support/core_ext/uri.rb +0 -5
- data/lib/active_support/deprecation/instance_delegator.rb +0 -38
- data/lib/active_support/per_thread_registry.rb +0 -65
- 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 = { "&" => "&", ">" => ">", "<" => "<", '"' => """, "'" => "'" }
|
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 > 0 & a < 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 & 3')
|
54
|
-
# # => "1 < 2 & 3"
|
55
|
-
#
|
56
|
-
# html_escape_once('<< Accept & Checkout')
|
57
|
-
# # => "<< Accept & 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>"</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
|
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
|
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
|
-
|
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 []=(
|
239
|
-
if
|
240
|
-
super(
|
104
|
+
def []=(arg1, arg2, arg3 = nil)
|
105
|
+
if arg3
|
106
|
+
super(arg1, arg2, implicit_html_escape_interpolated_argument(arg3))
|
241
107
|
else
|
242
|
-
super(
|
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
|
-
|
269
|
-
|
270
|
-
|
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
|
-
|
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
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
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(
|
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
|
-
|
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
|
-
|
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
|
25
|
-
# * An identifier for a
|
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,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
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
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
|
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,
|
172
|
-
|
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
|
-
|
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(
|
203
|
-
|
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
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|