activesupport 7.1.6 → 8.1.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 +256 -1133
- data/README.rdoc +1 -1
- data/lib/active_support/array_inquirer.rb +1 -1
- data/lib/active_support/backtrace_cleaner.rb +81 -3
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +65 -78
- data/lib/active_support/cache/file_store.rb +29 -14
- data/lib/active_support/cache/mem_cache_store.rb +42 -102
- data/lib/active_support/cache/memory_store.rb +11 -6
- data/lib/active_support/cache/null_store.rb +2 -2
- data/lib/active_support/cache/redis_cache_store.rb +58 -46
- data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
- data/lib/active_support/cache/strategy/local_cache.rb +72 -27
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
- data/lib/active_support/cache.rb +146 -86
- data/lib/active_support/callbacks.rb +102 -126
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +9 -0
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/concurrency/thread_monitor.rb +55 -0
- data/lib/active_support/configurable.rb +34 -0
- data/lib/active_support/configuration_file.rb +15 -6
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/array/conversions.rb +3 -5
- data/lib/active_support/core_ext/array.rb +7 -7
- data/lib/active_support/core_ext/benchmark.rb +4 -14
- data/lib/active_support/core_ext/big_decimal.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +26 -19
- data/lib/active_support/core_ext/class/subclasses.rb +15 -35
- data/lib/active_support/core_ext/class.rb +2 -2
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +2 -2
- data/lib/active_support/core_ext/date.rb +5 -5
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -9
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -6
- data/lib/active_support/core_ext/date_time.rb +5 -5
- data/lib/active_support/core_ext/digest/uuid.rb +6 -0
- data/lib/active_support/core_ext/digest.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +25 -8
- data/lib/active_support/core_ext/erb/util.rb +10 -5
- data/lib/active_support/core_ext/file.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
- data/lib/active_support/core_ext/hash/except.rb +0 -12
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/hash.rb +8 -8
- data/lib/active_support/core_ext/integer.rb +3 -3
- data/lib/active_support/core_ext/kernel.rb +3 -3
- data/lib/active_support/core_ext/module/attr_internal.rb +16 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -163
- data/lib/active_support/core_ext/module/deprecation.rb +1 -4
- data/lib/active_support/core_ext/module/introspection.rb +3 -0
- data/lib/active_support/core_ext/module.rb +11 -11
- data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
- data/lib/active_support/core_ext/numeric.rb +3 -3
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
- data/lib/active_support/core_ext/object/json.rb +24 -11
- data/lib/active_support/core_ext/object/to_query.rb +7 -1
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/object/with.rb +5 -3
- data/lib/active_support/core_ext/object.rb +13 -13
- data/lib/active_support/core_ext/pathname/blank.rb +4 -0
- data/lib/active_support/core_ext/pathname.rb +2 -2
- data/lib/active_support/core_ext/range/overlap.rb +4 -4
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +4 -4
- data/lib/active_support/core_ext/securerandom.rb +4 -4
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +4 -4
- data/lib/active_support/core_ext/string/multibyte.rb +13 -4
- data/lib/active_support/core_ext/string/output_safety.rb +19 -19
- data/lib/active_support/core_ext/string.rb +13 -13
- data/lib/active_support/core_ext/symbol.rb +1 -1
- data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
- data/lib/active_support/core_ext/time/calculations.rb +25 -30
- data/lib/active_support/core_ext/time/compatibility.rb +2 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -2
- data/lib/active_support/core_ext/time/zones.rb +1 -1
- data/lib/active_support/core_ext/time.rb +5 -5
- data/lib/active_support/core_ext.rb +1 -2
- data/lib/active_support/current_attributes/test_helper.rb +2 -2
- data/lib/active_support/current_attributes.rb +58 -50
- data/lib/active_support/delegation.rb +200 -0
- data/lib/active_support/dependencies/autoload.rb +0 -12
- data/lib/active_support/dependencies/interlock.rb +11 -5
- data/lib/active_support/dependencies.rb +6 -2
- data/lib/active_support/deprecation/constant_accessor.rb +47 -26
- data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
- data/lib/active_support/deprecation/reporting.rb +5 -17
- data/lib/active_support/deprecation.rb +8 -5
- data/lib/active_support/descendants_tracker.rb +9 -87
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -2
- data/lib/active_support/duration.rb +25 -16
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/encrypted_file.rb +1 -1
- data/lib/active_support/error_reporter.rb +121 -6
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +592 -0
- data/lib/active_support/evented_file_update_checker.rb +5 -3
- data/lib/active_support/execution_context.rb +64 -7
- data/lib/active_support/execution_wrapper.rb +1 -2
- data/lib/active_support/file_update_checker.rb +9 -7
- data/lib/active_support/fork_tracker.rb +2 -38
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/gzip.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +66 -45
- data/lib/active_support/html_safe_translation.rb +3 -0
- data/lib/active_support/i18n_railtie.rb +19 -11
- data/lib/active_support/inflector/inflections.rb +31 -15
- data/lib/active_support/inflector/transliterate.rb +6 -8
- data/lib/active_support/isolated_execution_state.rb +12 -17
- data/lib/active_support/json/decoding.rb +6 -4
- data/lib/active_support/json/encoding.rb +157 -21
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber.rb +2 -18
- data/lib/active_support/logger.rb +15 -2
- data/lib/active_support/logger_thread_safe_level.rb +4 -9
- data/lib/active_support/message_encryptors.rb +54 -2
- data/lib/active_support/message_pack/extensions.rb +20 -2
- data/lib/active_support/message_verifier.rb +21 -0
- data/lib/active_support/message_verifiers.rb +57 -3
- data/lib/active_support/messages/rotation_coordinator.rb +9 -0
- data/lib/active_support/messages/rotator.rb +10 -0
- data/lib/active_support/multibyte/chars.rb +14 -4
- data/lib/active_support/multibyte.rb +4 -0
- data/lib/active_support/notifications/fanout.rb +68 -50
- data/lib/active_support/notifications/instrumenter.rb +22 -19
- data/lib/active_support/notifications.rb +28 -27
- data/lib/active_support/number_helper/number_converter.rb +2 -2
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_options.rb +53 -15
- data/lib/active_support/railtie.rb +36 -20
- data/lib/active_support/string_inquirer.rb +1 -1
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +1 -5
- data/lib/active_support/syntax_error_proxy.rb +3 -0
- data/lib/active_support/tagged_logging.rb +5 -1
- data/lib/active_support/test_case.rb +63 -6
- data/lib/active_support/testing/assertions.rb +113 -27
- data/lib/active_support/testing/constant_stubbing.rb +30 -8
- data/lib/active_support/testing/deprecation.rb +5 -12
- data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/isolation.rb +19 -9
- data/lib/active_support/testing/method_call_assertions.rb +2 -16
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/server.rb +18 -2
- data/lib/active_support/testing/parallelization/worker.rb +4 -2
- data/lib/active_support/testing/parallelization.rb +25 -1
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +11 -6
- data/lib/active_support/time_with_zone.rb +39 -26
- data/lib/active_support/values/time_zone.rb +26 -17
- data/lib/active_support/xml_mini.rb +14 -4
- data/lib/active_support.rb +22 -9
- metadata +31 -17
- data/lib/active_support/core_ext/range/each.rb +0 -24
- data/lib/active_support/deprecation/instance_delegator.rb +0 -65
- data/lib/active_support/proxy_object.rb +0 -17
- data/lib/active_support/ruby_features.rb +0 -7
- data/lib/active_support/testing/strict_warnings.rb +0 -39
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "fiber"
|
|
4
|
-
|
|
5
3
|
module ActiveSupport
|
|
6
4
|
module IsolatedExecutionState # :nodoc:
|
|
7
5
|
@isolation_level = nil
|
|
@@ -30,45 +28,42 @@ module ActiveSupport
|
|
|
30
28
|
@isolation_level = level
|
|
31
29
|
end
|
|
32
30
|
|
|
33
|
-
def unique_id
|
|
34
|
-
self[:__id__] ||= Object.new
|
|
35
|
-
end
|
|
36
|
-
|
|
37
31
|
def [](key)
|
|
38
|
-
state
|
|
32
|
+
if state = @scope.current.active_support_execution_state
|
|
33
|
+
state[key]
|
|
34
|
+
end
|
|
39
35
|
end
|
|
40
36
|
|
|
41
37
|
def []=(key, value)
|
|
38
|
+
state = (@scope.current.active_support_execution_state ||= {})
|
|
42
39
|
state[key] = value
|
|
43
40
|
end
|
|
44
41
|
|
|
45
42
|
def key?(key)
|
|
46
|
-
|
|
43
|
+
@scope.current.active_support_execution_state&.key?(key)
|
|
47
44
|
end
|
|
48
45
|
|
|
49
46
|
def delete(key)
|
|
50
|
-
|
|
47
|
+
@scope.current.active_support_execution_state&.delete(key)
|
|
51
48
|
end
|
|
52
49
|
|
|
53
50
|
def clear
|
|
54
|
-
|
|
51
|
+
@scope.current.active_support_execution_state&.clear
|
|
55
52
|
end
|
|
56
53
|
|
|
57
54
|
def context
|
|
58
55
|
scope.current
|
|
59
56
|
end
|
|
60
57
|
|
|
61
|
-
def share_with(other)
|
|
58
|
+
def share_with(other, &block)
|
|
62
59
|
# Action Controller streaming spawns a new thread and copy thread locals.
|
|
63
60
|
# We do the same here for backward compatibility, but this is very much a hack
|
|
64
61
|
# and streaming should be rethought.
|
|
65
|
-
context.active_support_execution_state = other.active_support_execution_state.dup
|
|
62
|
+
old_state, context.active_support_execution_state = context.active_support_execution_state, other.active_support_execution_state.dup
|
|
63
|
+
block.call
|
|
64
|
+
ensure
|
|
65
|
+
context.active_support_execution_state = old_state
|
|
66
66
|
end
|
|
67
|
-
|
|
68
|
-
private
|
|
69
|
-
def state
|
|
70
|
-
context.active_support_execution_state ||= {}
|
|
71
|
-
end
|
|
72
67
|
end
|
|
73
68
|
|
|
74
69
|
self.isolation_level = :thread
|
|
@@ -14,13 +14,15 @@ module ActiveSupport
|
|
|
14
14
|
DATETIME_REGEX = /\A(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)\z/
|
|
15
15
|
|
|
16
16
|
class << self
|
|
17
|
-
# Parses a JSON string (JavaScript Object Notation) into a
|
|
17
|
+
# Parses a JSON string (JavaScript Object Notation) into a Ruby object.
|
|
18
18
|
# See http://www.json.org for more info.
|
|
19
19
|
#
|
|
20
20
|
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
|
|
21
|
-
# => {"team" => "rails", "players" => "36"}
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
# # => {"team" => "rails", "players" => "36"}
|
|
22
|
+
# ActiveSupport::JSON.decode("2.39")
|
|
23
|
+
# # => 2.39
|
|
24
|
+
def decode(json, options = {})
|
|
25
|
+
data = ::JSON.parse(json, options)
|
|
24
26
|
|
|
25
27
|
if ActiveSupport.parse_json_times
|
|
26
28
|
convert_dates_from(data)
|
|
@@ -8,24 +8,70 @@ module ActiveSupport
|
|
|
8
8
|
delegate :use_standard_json_time_format, :use_standard_json_time_format=,
|
|
9
9
|
:time_precision, :time_precision=,
|
|
10
10
|
:escape_html_entities_in_json, :escape_html_entities_in_json=,
|
|
11
|
+
:escape_js_separators_in_json, :escape_js_separators_in_json=,
|
|
11
12
|
:json_encoder, :json_encoder=,
|
|
12
13
|
to: :'ActiveSupport::JSON::Encoding'
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
module JSON
|
|
16
|
-
# Dumps objects in JSON (JavaScript Object Notation).
|
|
17
|
-
# See http://www.json.org for more info.
|
|
18
|
-
#
|
|
19
|
-
# ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
|
|
20
|
-
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
|
|
21
17
|
class << self
|
|
18
|
+
# Dumps objects in JSON (JavaScript Object Notation).
|
|
19
|
+
# See http://www.json.org for more info.
|
|
20
|
+
#
|
|
21
|
+
# ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
|
|
22
|
+
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
|
|
23
|
+
#
|
|
24
|
+
# By default, it generates JSON that is safe to include in JavaScript, as
|
|
25
|
+
# it escapes U+2028 (Line Separator) and U+2029 (Paragraph Separator):
|
|
26
|
+
#
|
|
27
|
+
# ActiveSupport::JSON.encode({ key: "\u2028" })
|
|
28
|
+
# # => "{\"key\":\"\\u2028\"}"
|
|
29
|
+
#
|
|
30
|
+
# By default, it also generates JSON that is safe to include in HTML, as
|
|
31
|
+
# it escapes <tt><</tt>, <tt>></tt>, and <tt>&</tt>:
|
|
32
|
+
#
|
|
33
|
+
# ActiveSupport::JSON.encode({ key: "<>&" })
|
|
34
|
+
# # => "{\"key\":\"\\u003c\\u003e\\u0026\"}"
|
|
35
|
+
#
|
|
36
|
+
# This behavior can be changed with the +escape_html_entities+ option, or the
|
|
37
|
+
# global escape_html_entities_in_json configuration option.
|
|
38
|
+
#
|
|
39
|
+
# ActiveSupport::JSON.encode({ key: "<>&" }, escape_html_entities: false)
|
|
40
|
+
# # => "{\"key\":\"<>&\"}"
|
|
41
|
+
#
|
|
42
|
+
# For performance reasons, you can set the +escape+ option to false,
|
|
43
|
+
# which will skip all escaping:
|
|
44
|
+
#
|
|
45
|
+
# ActiveSupport::JSON.encode({ key: "\u2028<>&" }, escape: false)
|
|
46
|
+
# # => "{\"key\":\"\u2028<>&\"}"
|
|
22
47
|
def encode(value, options = nil)
|
|
23
|
-
|
|
48
|
+
if options.nil? || options.empty?
|
|
49
|
+
Encoding.encode_without_options(value)
|
|
50
|
+
elsif options == { escape: false }.freeze
|
|
51
|
+
Encoding.encode_without_escape(value)
|
|
52
|
+
else
|
|
53
|
+
Encoding.json_encoder.new(options).encode(value)
|
|
54
|
+
end
|
|
24
55
|
end
|
|
25
56
|
alias_method :dump, :encode
|
|
26
57
|
end
|
|
27
58
|
|
|
28
59
|
module Encoding # :nodoc:
|
|
60
|
+
U2028 = -"\u2028".b
|
|
61
|
+
U2029 = -"\u2029".b
|
|
62
|
+
|
|
63
|
+
ESCAPED_CHARS = {
|
|
64
|
+
U2028 => '\u2028'.b,
|
|
65
|
+
U2029 => '\u2029'.b,
|
|
66
|
+
">".b => '\u003e'.b,
|
|
67
|
+
"<".b => '\u003c'.b,
|
|
68
|
+
"&".b => '\u0026'.b,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
HTML_ENTITIES_REGEX = Regexp.union(*(ESCAPED_CHARS.keys - [U2028, U2029]))
|
|
72
|
+
FULL_ESCAPE_REGEX = Regexp.union(*ESCAPED_CHARS.keys)
|
|
73
|
+
JS_SEPARATORS_REGEX = Regexp.union(U2028, U2029)
|
|
74
|
+
|
|
29
75
|
class JSONGemEncoder # :nodoc:
|
|
30
76
|
attr_reader :options
|
|
31
77
|
|
|
@@ -36,21 +82,23 @@ module ActiveSupport
|
|
|
36
82
|
# Encode the given object into a JSON string
|
|
37
83
|
def encode(value)
|
|
38
84
|
unless options.empty?
|
|
39
|
-
value = value.as_json(options.dup)
|
|
85
|
+
value = value.as_json(options.dup.freeze)
|
|
40
86
|
end
|
|
41
87
|
json = stringify(jsonify(value))
|
|
42
88
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if Encoding.escape_html_entities_in_json
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
89
|
+
return json unless @options.fetch(:escape, true)
|
|
90
|
+
|
|
91
|
+
json.force_encoding(::Encoding::BINARY)
|
|
92
|
+
if @options.fetch(:escape_html_entities, Encoding.escape_html_entities_in_json)
|
|
93
|
+
if Encoding.escape_js_separators_in_json
|
|
94
|
+
json.gsub!(FULL_ESCAPE_REGEX, ESCAPED_CHARS)
|
|
95
|
+
else
|
|
96
|
+
json.gsub!(HTML_ENTITIES_REGEX, ESCAPED_CHARS)
|
|
97
|
+
end
|
|
98
|
+
elsif Encoding.escape_js_separators_in_json
|
|
99
|
+
json.gsub!(JS_SEPARATORS_REGEX, ESCAPED_CHARS)
|
|
50
100
|
end
|
|
51
|
-
json.
|
|
52
|
-
json.gsub!("\u2029", '\u2029')
|
|
53
|
-
json
|
|
101
|
+
json.force_encoding(::Encoding::UTF_8)
|
|
54
102
|
end
|
|
55
103
|
|
|
56
104
|
private
|
|
@@ -83,14 +131,75 @@ module ActiveSupport
|
|
|
83
131
|
when Array
|
|
84
132
|
value.map { |v| jsonify(v) }
|
|
85
133
|
else
|
|
86
|
-
|
|
134
|
+
if defined?(::JSON::Fragment) && ::JSON::Fragment === value
|
|
135
|
+
value
|
|
136
|
+
else
|
|
137
|
+
jsonify value.as_json
|
|
138
|
+
end
|
|
87
139
|
end
|
|
88
140
|
end
|
|
89
141
|
|
|
90
142
|
# Encode a "jsonified" Ruby data structure using the JSON gem
|
|
91
143
|
def stringify(jsonified)
|
|
92
|
-
::JSON.generate(jsonified
|
|
144
|
+
::JSON.generate(jsonified)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# ruby/json 2.14.x yields non-String keys but doesn't let us know it's a key
|
|
149
|
+
if defined?(::JSON::Coder) && Gem::Version.new(::JSON::VERSION) >= Gem::Version.new("2.15.2")
|
|
150
|
+
class JSONGemCoderEncoder # :nodoc:
|
|
151
|
+
JSON_NATIVE_TYPES = [Hash, Array, Float, String, Symbol, Integer, NilClass, TrueClass, FalseClass, ::JSON::Fragment].freeze
|
|
152
|
+
CODER = ::JSON::Coder.new do |value, is_key|
|
|
153
|
+
json_value = value.as_json
|
|
154
|
+
# Keep compatibility by calling to_s on non-String keys
|
|
155
|
+
next json_value.to_s if is_key
|
|
156
|
+
# Handle objects returning self from as_json
|
|
157
|
+
if json_value.equal?(value)
|
|
158
|
+
next ::JSON::Fragment.new(::JSON.generate(json_value))
|
|
159
|
+
end
|
|
160
|
+
# Handle objects not returning JSON-native types from as_json
|
|
161
|
+
count = 5
|
|
162
|
+
until JSON_NATIVE_TYPES.include?(json_value.class)
|
|
163
|
+
raise SystemStackError if count == 0
|
|
164
|
+
json_value = json_value.as_json
|
|
165
|
+
count -= 1
|
|
166
|
+
end
|
|
167
|
+
json_value
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def initialize(options = nil)
|
|
172
|
+
if options
|
|
173
|
+
options = options.dup
|
|
174
|
+
@escape = options.delete(:escape) { true }
|
|
175
|
+
@options = options.freeze
|
|
176
|
+
else
|
|
177
|
+
@escape = true
|
|
178
|
+
@options = {}.freeze
|
|
179
|
+
end
|
|
93
180
|
end
|
|
181
|
+
|
|
182
|
+
# Encode the given object into a JSON string
|
|
183
|
+
def encode(value)
|
|
184
|
+
value = value.as_json(@options) unless @options.empty?
|
|
185
|
+
|
|
186
|
+
json = CODER.dump(value)
|
|
187
|
+
|
|
188
|
+
return json unless @escape
|
|
189
|
+
|
|
190
|
+
json.force_encoding(::Encoding::BINARY)
|
|
191
|
+
if @options.fetch(:escape_html_entities, Encoding.escape_html_entities_in_json)
|
|
192
|
+
if Encoding.escape_js_separators_in_json
|
|
193
|
+
json.gsub!(FULL_ESCAPE_REGEX, ESCAPED_CHARS)
|
|
194
|
+
else
|
|
195
|
+
json.gsub!(HTML_ENTITIES_REGEX, ESCAPED_CHARS)
|
|
196
|
+
end
|
|
197
|
+
elsif Encoding.escape_js_separators_in_json
|
|
198
|
+
json.gsub!(JS_SEPARATORS_REGEX, ESCAPED_CHARS)
|
|
199
|
+
end
|
|
200
|
+
json.force_encoding(::Encoding::UTF_8)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
94
203
|
end
|
|
95
204
|
|
|
96
205
|
class << self
|
|
@@ -102,18 +211,45 @@ module ActiveSupport
|
|
|
102
211
|
# as a safety measure.
|
|
103
212
|
attr_accessor :escape_html_entities_in_json
|
|
104
213
|
|
|
214
|
+
# If true, encode LINE SEPARATOR (U+2028) and PARAGRAPH SEPARATOR (U+2029)
|
|
215
|
+
# as escaped unicode sequences ('\u2028' and '\u2029').
|
|
216
|
+
# Historically these characters were not valid inside JavaScript strings
|
|
217
|
+
# but that changed in ECMAScript 2019. As such it's no longer a concern in
|
|
218
|
+
# modern browsers: https://caniuse.com/mdn-javascript_builtins_json_json_superset.
|
|
219
|
+
attr_accessor :escape_js_separators_in_json
|
|
220
|
+
|
|
105
221
|
# Sets the precision of encoded time values.
|
|
106
222
|
# Defaults to 3 (equivalent to millisecond precision)
|
|
107
223
|
attr_accessor :time_precision
|
|
108
224
|
|
|
109
225
|
# Sets the encoder used by \Rails to encode Ruby objects into JSON strings
|
|
110
226
|
# in +Object#to_json+ and +ActiveSupport::JSON.encode+.
|
|
111
|
-
|
|
227
|
+
attr_reader :json_encoder
|
|
228
|
+
|
|
229
|
+
def json_encoder=(encoder)
|
|
230
|
+
@json_encoder = encoder
|
|
231
|
+
@encoder_without_options = encoder.new
|
|
232
|
+
@encoder_without_escape = encoder.new(escape: false)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def encode_without_options(value) # :nodoc:
|
|
236
|
+
@encoder_without_options.encode(value)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def encode_without_escape(value) # :nodoc:
|
|
240
|
+
@encoder_without_escape.encode(value)
|
|
241
|
+
end
|
|
112
242
|
end
|
|
113
243
|
|
|
114
244
|
self.use_standard_json_time_format = true
|
|
115
245
|
self.escape_html_entities_in_json = true
|
|
116
|
-
self.
|
|
246
|
+
self.escape_js_separators_in_json = true
|
|
247
|
+
self.json_encoder =
|
|
248
|
+
if defined?(JSONGemCoderEncoder)
|
|
249
|
+
JSONGemCoderEncoder
|
|
250
|
+
else
|
|
251
|
+
JSONGemEncoder
|
|
252
|
+
end
|
|
117
253
|
self.time_precision = 3
|
|
118
254
|
end
|
|
119
255
|
end
|
|
@@ -53,7 +53,7 @@ module ActiveSupport
|
|
|
53
53
|
# loaded. If the component has already loaded, the block is executed
|
|
54
54
|
# immediately.
|
|
55
55
|
#
|
|
56
|
-
# Options
|
|
56
|
+
# ==== Options
|
|
57
57
|
#
|
|
58
58
|
# * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+.
|
|
59
59
|
# * <tt>:run_once</tt> - Given +block+ will run only once.
|
|
@@ -62,10 +62,6 @@ module ActiveSupport
|
|
|
62
62
|
# that all logs are flushed, and it is called in Rails::Rack::Logger after a
|
|
63
63
|
# request finishes.
|
|
64
64
|
class LogSubscriber < Subscriber
|
|
65
|
-
# Embed in a String to clear all previous ANSI sequences.
|
|
66
|
-
CLEAR = ActiveSupport::Deprecation::DeprecatedObjectProxy.new("\e[0m", "CLEAR is deprecated! Use MODES[:clear] instead.", ActiveSupport.deprecator)
|
|
67
|
-
BOLD = ActiveSupport::Deprecation::DeprecatedObjectProxy.new("\e[1m", "BOLD is deprecated! Use MODES[:bold] instead.", ActiveSupport.deprecator)
|
|
68
|
-
|
|
69
65
|
# ANSI sequence modes
|
|
70
66
|
MODES = {
|
|
71
67
|
clear: 0,
|
|
@@ -153,12 +149,6 @@ module ActiveSupport
|
|
|
153
149
|
log_exception(event.name, e)
|
|
154
150
|
end
|
|
155
151
|
|
|
156
|
-
def publish_event(event)
|
|
157
|
-
super if logger
|
|
158
|
-
rescue => e
|
|
159
|
-
log_exception(event.name, e)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
152
|
attr_writer :event_levels # :nodoc:
|
|
163
153
|
|
|
164
154
|
private
|
|
@@ -182,20 +172,14 @@ module ActiveSupport
|
|
|
182
172
|
end
|
|
183
173
|
|
|
184
174
|
def mode_from(options)
|
|
185
|
-
if options.is_a?(TrueClass) || options.is_a?(FalseClass)
|
|
186
|
-
ActiveSupport.deprecator.warn(<<~MSG.squish)
|
|
187
|
-
Bolding log text with a positional boolean is deprecated and will be removed
|
|
188
|
-
in Rails 7.2. Use an option hash instead (eg. `color("my text", :red, bold: true)`).
|
|
189
|
-
MSG
|
|
190
|
-
options = { bold: options }
|
|
191
|
-
end
|
|
192
|
-
|
|
193
175
|
modes = MODES.values_at(*options.compact_blank.keys)
|
|
194
176
|
|
|
195
177
|
"\e[#{modes.join(";")}m" if modes.any?
|
|
196
178
|
end
|
|
197
179
|
|
|
198
180
|
def log_exception(name, e)
|
|
181
|
+
ActiveSupport.error_reporter.report(e, source: name)
|
|
182
|
+
|
|
199
183
|
if logger
|
|
200
184
|
logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
|
|
201
185
|
end
|
|
@@ -13,6 +13,10 @@ module ActiveSupport
|
|
|
13
13
|
# logger = Logger.new(STDOUT)
|
|
14
14
|
# ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
|
|
15
15
|
# # => true
|
|
16
|
+
#
|
|
17
|
+
# logger = Logger.new('/var/log/rails.log')
|
|
18
|
+
# ActiveSupport::Logger.logger_outputs_to?(logger, '/var/log/rails.log')
|
|
19
|
+
# # => true
|
|
16
20
|
def self.logger_outputs_to?(logger, *sources)
|
|
17
21
|
loggers = if logger.is_a?(BroadcastLogger)
|
|
18
22
|
logger.broadcasts
|
|
@@ -21,9 +25,9 @@ module ActiveSupport
|
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
logdevs = loggers.map { |logger| logger.instance_variable_get(:@logdev) }
|
|
24
|
-
logger_sources = logdevs.filter_map { |logdev| logdev.
|
|
28
|
+
logger_sources = logdevs.filter_map { |logdev| logdev.try(:filename) || logdev.try(:dev) }
|
|
25
29
|
|
|
26
|
-
(sources
|
|
30
|
+
normalize_sources(sources).intersect?(normalize_sources(logger_sources))
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def initialize(*args, **kwargs)
|
|
@@ -38,5 +42,14 @@ module ActiveSupport
|
|
|
38
42
|
"#{String === msg ? msg : msg.inspect}\n"
|
|
39
43
|
end
|
|
40
44
|
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
def self.normalize_sources(sources)
|
|
48
|
+
sources.map do |source|
|
|
49
|
+
source = source.path if source.respond_to?(:path)
|
|
50
|
+
source = File.realpath(source) if source.is_a?(String) && File.exist?(source)
|
|
51
|
+
source
|
|
52
|
+
end
|
|
53
|
+
end
|
|
41
54
|
end
|
|
42
55
|
end
|
|
@@ -7,12 +7,9 @@ module ActiveSupport
|
|
|
7
7
|
module LoggerThreadSafeLevel # :nodoc:
|
|
8
8
|
extend ActiveSupport::Concern
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Logger::#{severity} >= level # DEBUG >= level
|
|
14
|
-
end # end
|
|
15
|
-
EOT
|
|
10
|
+
def initialize(...)
|
|
11
|
+
super
|
|
12
|
+
@local_level_key = :"logger_thread_safe_level_#{object_id}"
|
|
16
13
|
end
|
|
17
14
|
|
|
18
15
|
def local_level
|
|
@@ -48,8 +45,6 @@ module ActiveSupport
|
|
|
48
45
|
end
|
|
49
46
|
|
|
50
47
|
private
|
|
51
|
-
|
|
52
|
-
@local_level_key ||= :"logger_thread_safe_level_#{object_id}"
|
|
53
|
-
end
|
|
48
|
+
attr_reader :local_level_key
|
|
54
49
|
end
|
|
55
50
|
end
|
|
@@ -26,10 +26,13 @@ module ActiveSupport
|
|
|
26
26
|
# as the first rotation and <tt>transitional = true</tt>. Then, after all
|
|
27
27
|
# servers have been updated, perform a second rolling deploy with
|
|
28
28
|
# <tt>transitional = false</tt>.
|
|
29
|
+
#
|
|
30
|
+
#--
|
|
31
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#transitional
|
|
29
32
|
|
|
30
33
|
##
|
|
31
|
-
# :method:
|
|
32
|
-
# :call-seq:
|
|
34
|
+
# :singleton-method: new
|
|
35
|
+
# :call-seq: new(&secret_generator)
|
|
33
36
|
#
|
|
34
37
|
# Initializes a new instance. +secret_generator+ must accept a salt and a
|
|
35
38
|
# +secret_length+ kwarg, and return a suitable secret (string) or secrets
|
|
@@ -43,6 +46,9 @@ module ActiveSupport
|
|
|
43
46
|
# end
|
|
44
47
|
#
|
|
45
48
|
# encryptors.rotate(base: "...")
|
|
49
|
+
#
|
|
50
|
+
#--
|
|
51
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#initialize
|
|
46
52
|
|
|
47
53
|
##
|
|
48
54
|
# :method: []
|
|
@@ -51,12 +57,18 @@ module ActiveSupport
|
|
|
51
57
|
# Returns a MessageEncryptor configured with a secret derived from the
|
|
52
58
|
# given +salt+, and options from #rotate. MessageEncryptor instances will
|
|
53
59
|
# be memoized, so the same +salt+ will return the same instance.
|
|
60
|
+
#
|
|
61
|
+
#--
|
|
62
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#[]
|
|
54
63
|
|
|
55
64
|
##
|
|
56
65
|
# :method: []=
|
|
57
66
|
# :call-seq: []=(salt, encryptor)
|
|
58
67
|
#
|
|
59
68
|
# Overrides a MessageEncryptor instance associated with a given +salt+.
|
|
69
|
+
#
|
|
70
|
+
#--
|
|
71
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#[]=
|
|
60
72
|
|
|
61
73
|
##
|
|
62
74
|
# :method: rotate
|
|
@@ -106,18 +118,55 @@ module ActiveSupport
|
|
|
106
118
|
#
|
|
107
119
|
# # Uses `serializer: Marshal, url_safe: false`.
|
|
108
120
|
# encryptors[:baz]
|
|
121
|
+
#
|
|
122
|
+
#--
|
|
123
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#rotate
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# :method: prepend
|
|
127
|
+
# :call-seq:
|
|
128
|
+
# prepend(**options)
|
|
129
|
+
# prepend(&block)
|
|
130
|
+
#
|
|
131
|
+
# Just like #rotate, but prepends the given options or block to the list of
|
|
132
|
+
# option sets.
|
|
133
|
+
#
|
|
134
|
+
# This can be useful when you have an already-configured +MessageEncryptors+
|
|
135
|
+
# instance, but you want to override the way messages are encrypted.
|
|
136
|
+
#
|
|
137
|
+
# module ThirdParty
|
|
138
|
+
# ENCRYPTORS = ActiveSupport::MessageEncryptors.new { ... }.
|
|
139
|
+
# rotate(serializer: Marshal, url_safe: true).
|
|
140
|
+
# rotate(serializer: Marshal, url_safe: false)
|
|
141
|
+
# end
|
|
142
|
+
#
|
|
143
|
+
# ThirdParty.ENCRYPTORS.prepend(serializer: JSON, url_safe: true)
|
|
144
|
+
#
|
|
145
|
+
# # Uses `serializer: JSON, url_safe: true`.
|
|
146
|
+
# # Falls back to `serializer: Marshal, url_safe: true` or
|
|
147
|
+
# # `serializer: Marshal, url_safe: false`.
|
|
148
|
+
# ThirdParty.ENCRYPTORS[:foo]
|
|
149
|
+
#
|
|
150
|
+
#--
|
|
151
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#prepend
|
|
109
152
|
|
|
110
153
|
##
|
|
111
154
|
# :method: rotate_defaults
|
|
112
155
|
# :call-seq: rotate_defaults
|
|
113
156
|
#
|
|
114
157
|
# Invokes #rotate with the default options.
|
|
158
|
+
#
|
|
159
|
+
#--
|
|
160
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#rotate_defaults
|
|
115
161
|
|
|
116
162
|
##
|
|
117
163
|
# :method: clear_rotations
|
|
118
164
|
# :call-seq: clear_rotations
|
|
119
165
|
#
|
|
120
166
|
# Clears the list of option sets.
|
|
167
|
+
#
|
|
168
|
+
#--
|
|
169
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#clear_rotations
|
|
121
170
|
|
|
122
171
|
##
|
|
123
172
|
# :method: on_rotation
|
|
@@ -129,6 +178,9 @@ module ActiveSupport
|
|
|
129
178
|
# For example, this callback could log each time it is called, and thus
|
|
130
179
|
# indicate whether old option sets are still in use or can be removed from
|
|
131
180
|
# rotation.
|
|
181
|
+
#
|
|
182
|
+
#--
|
|
183
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#on_rotation
|
|
132
184
|
|
|
133
185
|
##
|
|
134
186
|
private
|
|
@@ -7,6 +7,7 @@ require "pathname"
|
|
|
7
7
|
require "uri/generic"
|
|
8
8
|
require "msgpack/bigint"
|
|
9
9
|
require "active_support/hash_with_indifferent_access"
|
|
10
|
+
require "active_support/core_ext/string/output_safety"
|
|
10
11
|
require "active_support/time"
|
|
11
12
|
|
|
12
13
|
module ActiveSupport
|
|
@@ -86,8 +87,9 @@ module ActiveSupport
|
|
|
86
87
|
unpacker: URI.method(:parse)
|
|
87
88
|
|
|
88
89
|
registry.register_type 14, IPAddr,
|
|
89
|
-
packer: :
|
|
90
|
-
unpacker: :
|
|
90
|
+
packer: method(:write_ipaddr),
|
|
91
|
+
unpacker: method(:read_ipaddr),
|
|
92
|
+
recursive: true
|
|
91
93
|
|
|
92
94
|
registry.register_type 15, Pathname,
|
|
93
95
|
packer: :to_s,
|
|
@@ -101,6 +103,10 @@ module ActiveSupport
|
|
|
101
103
|
packer: method(:write_hash_with_indifferent_access),
|
|
102
104
|
unpacker: method(:read_hash_with_indifferent_access),
|
|
103
105
|
recursive: true
|
|
106
|
+
|
|
107
|
+
registry.register_type 18, ActiveSupport::SafeBuffer,
|
|
108
|
+
packer: :to_s,
|
|
109
|
+
unpacker: :new
|
|
104
110
|
end
|
|
105
111
|
|
|
106
112
|
def install_unregistered_type_error(registry)
|
|
@@ -221,6 +227,18 @@ module ActiveSupport
|
|
|
221
227
|
Set.new(unpacker.read)
|
|
222
228
|
end
|
|
223
229
|
|
|
230
|
+
def write_ipaddr(ipaddr, packer)
|
|
231
|
+
if ipaddr.prefix < 32 || (ipaddr.ipv6? && ipaddr.prefix < 128)
|
|
232
|
+
packer.write("#{ipaddr}/#{ipaddr.prefix}")
|
|
233
|
+
else
|
|
234
|
+
packer.write(ipaddr.to_s)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def read_ipaddr(unpacker)
|
|
239
|
+
IPAddr.new(unpacker.read)
|
|
240
|
+
end
|
|
241
|
+
|
|
224
242
|
def write_hash_with_indifferent_access(hwia, packer)
|
|
225
243
|
packer.write(hwia.to_h)
|
|
226
244
|
end
|
|
@@ -30,6 +30,18 @@ module ActiveSupport
|
|
|
30
30
|
# self.current_user = User.find(id)
|
|
31
31
|
# end
|
|
32
32
|
#
|
|
33
|
+
# === Signing is not encryption
|
|
34
|
+
#
|
|
35
|
+
# The signed messages are not encrypted. The payload is merely encoded (Base64 by default) and can be decoded by
|
|
36
|
+
# anyone. The signature is just assuring that the message wasn't tampered with. For example:
|
|
37
|
+
#
|
|
38
|
+
# message = Rails.application.message_verifier('my_purpose').generate('never put secrets here')
|
|
39
|
+
# # => "BAhJIhtuZXZlciBwdXQgc2VjcmV0cyBoZXJlBjoGRVQ=--a0c1c0827919da5e949e989c971249355735e140"
|
|
40
|
+
# Base64.decode64(message.split("--").first) # no key needed
|
|
41
|
+
# # => 'never put secrets here'
|
|
42
|
+
#
|
|
43
|
+
# If you also need to encrypt the contents, you must use ActiveSupport::MessageEncryptor instead.
|
|
44
|
+
#
|
|
33
45
|
# === Confine messages to a specific purpose
|
|
34
46
|
#
|
|
35
47
|
# It's not recommended to use the same verifier for different purposes in your application.
|
|
@@ -142,6 +154,8 @@ module ActiveSupport
|
|
|
142
154
|
# not URL-safe. In other words, they can contain "+" and "/". If you want to
|
|
143
155
|
# generate URL-safe strings (in compliance with "Base 64 Encoding with URL
|
|
144
156
|
# and Filename Safe Alphabet" in RFC 4648), you can pass +true+.
|
|
157
|
+
# Note that MessageVerifier will always accept both URL-safe and URL-unsafe
|
|
158
|
+
# encoded messages, to allow a smooth transition between the two settings.
|
|
145
159
|
#
|
|
146
160
|
# [+:force_legacy_metadata_serializer+]
|
|
147
161
|
# Whether to use the legacy metadata serializer, which serializes the
|
|
@@ -306,6 +320,13 @@ module ActiveSupport
|
|
|
306
320
|
end
|
|
307
321
|
|
|
308
322
|
private
|
|
323
|
+
def decode(encoded, url_safe: @url_safe)
|
|
324
|
+
catch :invalid_message_format do
|
|
325
|
+
return super
|
|
326
|
+
end
|
|
327
|
+
super(encoded, url_safe: !url_safe)
|
|
328
|
+
end
|
|
329
|
+
|
|
309
330
|
def sign_encoded(encoded)
|
|
310
331
|
digest = generate_digest(encoded)
|
|
311
332
|
encoded << SEPARATOR << digest
|