activesupport 6.1.0 → 7.1.5.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 +1075 -325
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -7
- data/lib/active_support/actionable_error.rb +4 -2
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +32 -7
- data/lib/active_support/benchmarkable.rb +3 -2
- 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 +53 -20
- data/lib/active_support/cache/mem_cache_store.rb +201 -62
- data/lib/active_support/cache/memory_store.rb +86 -24
- data/lib/active_support/cache/null_store.rb +16 -2
- data/lib/active_support/cache/redis_cache_store.rb +186 -193
- data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
- data/lib/active_support/cache/strategy/local_cache.rb +63 -71
- data/lib/active_support/cache.rb +487 -249
- data/lib/active_support/callbacks.rb +227 -105
- data/lib/active_support/code_generator.rb +70 -0
- data/lib/active_support/concern.rb +9 -7
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +18 -5
- data/lib/active_support/configuration_file.rb +7 -2
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +15 -13
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/subclasses.rb +37 -26
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +24 -9
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
- data/lib/active_support/core_ext/digest/uuid.rb +30 -13
- data/lib/active_support/core_ext/enumerable.rb +85 -83
- data/lib/active_support/core_ext/erb/util.rb +196 -0
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +1 -2
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/integer/inflections.rb +12 -12
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +8 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +49 -22
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +81 -43
- data/lib/active_support/core_ext/module/deprecation.rb +15 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +2 -8
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +82 -77
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
- data/lib/active_support/core_ext/object/duplicable.rb +31 -11
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
- data/lib/active_support/core_ext/object/json.rb +49 -27
- data/lib/active_support/core_ext/object/to_query.rb +2 -4
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/object/with.rb +44 -0
- data/lib/active_support/core_ext/object/with_options.rb +25 -6
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +16 -0
- data/lib/active_support/core_ext/pathname/existence.rb +23 -0
- data/lib/active_support/core_ext/pathname.rb +4 -0
- data/lib/active_support/core_ext/range/compare_range.rb +0 -25
- data/lib/active_support/core_ext/range/conversions.rb +34 -13
- data/lib/active_support/core_ext/range/each.rb +1 -1
- 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 +25 -13
- data/lib/active_support/core_ext/string/conversions.rb +2 -2
- 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 +17 -10
- data/lib/active_support/core_ext/string/inquiry.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +85 -165
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +30 -8
- data/lib/active_support/core_ext/time/conversions.rb +15 -13
- data/lib/active_support/core_ext/time/zones.rb +12 -28
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes.rb +47 -20
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/dependencies/autoload.rb +17 -12
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +58 -788
- data/lib/active_support/deprecation/behaviors.rb +66 -40
- data/lib/active_support/deprecation/constant_accessor.rb +5 -4
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +6 -8
- data/lib/active_support/deprecation/instance_delegator.rb +31 -4
- data/lib/active_support/deprecation/method_wrappers.rb +9 -26
- data/lib/active_support/deprecation/proxy_wrappers.rb +38 -23
- data/lib/active_support/deprecation/reporting.rb +43 -26
- data/lib/active_support/deprecation.rb +32 -5
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +150 -72
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +9 -3
- data/lib/active_support/duration.rb +83 -52
- data/lib/active_support/encrypted_configuration.rb +72 -9
- data/lib/active_support/encrypted_file.rb +29 -13
- data/lib/active_support/environment_inquirer.rb +23 -3
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +203 -0
- data/lib/active_support/evented_file_update_checker.rb +20 -7
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +44 -22
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/file_update_checker.rb +4 -2
- data/lib/active_support/fork_tracker.rb +28 -11
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +44 -19
- data/lib/active_support/html_safe_translation.rb +53 -0
- data/lib/active_support/i18n.rb +2 -1
- data/lib/active_support/i18n_railtie.rb +21 -14
- data/lib/active_support/inflector/inflections.rb +25 -7
- data/lib/active_support/inflector/methods.rb +50 -64
- data/lib/active_support/inflector/transliterate.rb +4 -2
- data/lib/active_support/isolated_execution_state.rb +76 -0
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +27 -45
- data/lib/active_support/key_generator.rb +31 -6
- data/lib/active_support/lazy_load_hooks.rb +33 -7
- data/lib/active_support/locale/en.yml +4 -2
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +97 -35
- data/lib/active_support/logger.rb +9 -60
- data/lib/active_support/logger_thread_safe_level.rb +11 -34
- data/lib/active_support/message_encryptor.rb +206 -56
- 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 +292 -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 +235 -84
- 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 +112 -46
- 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 +12 -11
- data/lib/active_support/multibyte/unicode.rb +9 -49
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +304 -114
- data/lib/active_support/notifications/instrumenter.rb +117 -35
- data/lib/active_support/notifications.rb +25 -25
- data/lib/active_support/number_helper/number_converter.rb +14 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -4
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
- data/lib/active_support/number_helper/rounding_helper.rb +2 -6
- data/lib/active_support/number_helper.rb +379 -319
- data/lib/active_support/option_merger.rb +10 -18
- data/lib/active_support/ordered_hash.rb +4 -4
- data/lib/active_support/ordered_options.rb +15 -1
- data/lib/active_support/parameter_filter.rb +105 -81
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/railtie.rb +83 -21
- data/lib/active_support/reloader.rb +13 -5
- data/lib/active_support/rescuable.rb +18 -16
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +18 -11
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/string_inquirer.rb +3 -3
- data/lib/active_support/subscriber.rb +11 -40
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +65 -25
- data/lib/active_support/test_case.rb +166 -27
- data/lib/active_support/testing/assertions.rb +61 -15
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +32 -0
- data/lib/active_support/testing/deprecation.rb +53 -2
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +30 -29
- data/lib/active_support/testing/method_call_assertions.rb +24 -11
- data/lib/active_support/testing/parallelization/server.rb +4 -0
- data/lib/active_support/testing/parallelization/worker.rb +3 -0
- data/lib/active_support/testing/parallelization.rb +4 -0
- data/lib/active_support/testing/parallelize_executor.rb +81 -0
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +4 -6
- data/lib/active_support/testing/strict_warnings.rb +39 -0
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +49 -16
- data/lib/active_support/time_with_zone.rb +39 -28
- data/lib/active_support/values/time_zone.rb +50 -18
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +4 -11
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +5 -5
- data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
- data/lib/active_support/xml_mini/rexml.rb +2 -2
- data/lib/active_support/xml_mini.rb +7 -6
- data/lib/active_support.rb +28 -1
- metadata +150 -18
- data/lib/active_support/core_ext/marshal.rb +0 -26
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -28
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/uri.rb +0 -29
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
- data/lib/active_support/per_thread_registry.rb +0 -60
@@ -1,80 +1,146 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "time"
|
4
|
+
require "active_support/json"
|
5
|
+
require_relative "serializer_with_fallback"
|
4
6
|
|
5
7
|
module ActiveSupport
|
6
|
-
module Messages
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
module Messages # :nodoc:
|
9
|
+
module Metadata # :nodoc:
|
10
|
+
singleton_class.attr_accessor :use_message_serializer_for_metadata
|
11
|
+
|
12
|
+
ENVELOPE_SERIALIZERS = [
|
13
|
+
*SerializerWithFallback::SERIALIZERS.values,
|
14
|
+
ActiveSupport::JSON,
|
15
|
+
::JSON,
|
16
|
+
Marshal,
|
17
|
+
]
|
18
|
+
|
19
|
+
TIMESTAMP_SERIALIZERS = [
|
20
|
+
SerializerWithFallback::SERIALIZERS.fetch(:message_pack),
|
21
|
+
SerializerWithFallback::SERIALIZERS.fetch(:message_pack_allow_marshal),
|
22
|
+
]
|
12
23
|
|
13
|
-
|
14
|
-
|
24
|
+
ActiveSupport.on_load(:message_pack) do
|
25
|
+
ENVELOPE_SERIALIZERS << ActiveSupport::MessagePack
|
26
|
+
TIMESTAMP_SERIALIZERS << ActiveSupport::MessagePack
|
15
27
|
end
|
16
28
|
|
17
|
-
|
18
|
-
def
|
19
|
-
|
20
|
-
|
29
|
+
private
|
30
|
+
def serialize_with_metadata(data, **metadata)
|
31
|
+
has_metadata = metadata.any? { |k, v| v }
|
32
|
+
|
33
|
+
if has_metadata && !use_message_serializer_for_metadata?
|
34
|
+
data_string = serialize_to_json_safe_string(data)
|
35
|
+
envelope = wrap_in_metadata_legacy_envelope({ "message" => data_string }, **metadata)
|
36
|
+
serialize_to_json(envelope)
|
21
37
|
else
|
22
|
-
|
38
|
+
data = wrap_in_metadata_envelope({ "data" => data }, **metadata) if has_metadata
|
39
|
+
serialize(data)
|
23
40
|
end
|
24
41
|
end
|
25
42
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
43
|
+
def deserialize_with_metadata(message, **expected_metadata)
|
44
|
+
if dual_serialized_metadata_envelope_json?(message)
|
45
|
+
envelope = deserialize_from_json(message)
|
46
|
+
extracted = extract_from_metadata_envelope(envelope, **expected_metadata)
|
47
|
+
deserialize_from_json_safe_string(extracted["message"])
|
48
|
+
else
|
49
|
+
deserialized = deserialize(message)
|
50
|
+
if metadata_envelope?(deserialized)
|
51
|
+
extract_from_metadata_envelope(deserialized, **expected_metadata)["data"]
|
52
|
+
elsif expected_metadata.none? { |k, v| v }
|
53
|
+
deserialized
|
54
|
+
else
|
55
|
+
throw :invalid_message_content, "missing metadata"
|
36
56
|
end
|
37
57
|
end
|
58
|
+
end
|
38
59
|
|
39
|
-
|
40
|
-
|
60
|
+
def use_message_serializer_for_metadata?
|
61
|
+
Metadata.use_message_serializer_for_metadata && Metadata::ENVELOPE_SERIALIZERS.include?(serializer)
|
62
|
+
end
|
41
63
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
64
|
+
def wrap_in_metadata_envelope(hash, expires_at: nil, expires_in: nil, purpose: nil)
|
65
|
+
expiry = pick_expiry(expires_at, expires_in)
|
66
|
+
hash["exp"] = expiry if expiry
|
67
|
+
hash["pur"] = purpose.to_s if purpose
|
68
|
+
{ "_rails" => hash }
|
69
|
+
end
|
70
|
+
|
71
|
+
def wrap_in_metadata_legacy_envelope(hash, expires_at: nil, expires_in: nil, purpose: nil)
|
72
|
+
expiry = pick_expiry(expires_at, expires_in)
|
73
|
+
hash["exp"] = expiry
|
74
|
+
hash["pur"] = purpose
|
75
|
+
{ "_rails" => hash }
|
76
|
+
end
|
77
|
+
|
78
|
+
def extract_from_metadata_envelope(envelope, purpose: nil)
|
79
|
+
hash = envelope["_rails"]
|
48
80
|
|
49
|
-
|
50
|
-
|
81
|
+
if hash["exp"] && Time.now.utc >= parse_expiry(hash["exp"])
|
82
|
+
throw :invalid_message_content, "expired"
|
51
83
|
end
|
52
84
|
|
53
|
-
|
54
|
-
|
85
|
+
if hash["pur"].to_s != purpose.to_s
|
86
|
+
throw :invalid_message_content, "mismatched purpose"
|
55
87
|
end
|
56
|
-
end
|
57
88
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
89
|
+
hash
|
90
|
+
end
|
61
91
|
|
62
|
-
|
63
|
-
|
64
|
-
@purpose.to_s == purpose.to_s
|
92
|
+
def metadata_envelope?(object)
|
93
|
+
object.is_a?(Hash) && object.key?("_rails")
|
65
94
|
end
|
66
95
|
|
67
|
-
def
|
68
|
-
|
96
|
+
def dual_serialized_metadata_envelope_json?(string)
|
97
|
+
string.start_with?('{"_rails":{"message":')
|
98
|
+
end
|
99
|
+
|
100
|
+
def pick_expiry(expires_at, expires_in)
|
101
|
+
expiry = if expires_at
|
102
|
+
expires_at.utc
|
103
|
+
elsif expires_in
|
104
|
+
Time.now.utc.advance(seconds: expires_in)
|
105
|
+
end
|
106
|
+
|
107
|
+
unless Metadata::TIMESTAMP_SERIALIZERS.include?(serializer)
|
108
|
+
expiry = expiry&.iso8601(3)
|
109
|
+
end
|
110
|
+
|
111
|
+
expiry
|
69
112
|
end
|
70
113
|
|
71
|
-
def
|
72
|
-
if
|
114
|
+
def parse_expiry(expires_at)
|
115
|
+
if !expires_at.is_a?(String)
|
116
|
+
expires_at
|
117
|
+
elsif ActiveSupport.use_standard_json_time_format
|
73
118
|
Time.iso8601(expires_at)
|
74
119
|
else
|
75
120
|
Time.parse(expires_at)
|
76
121
|
end
|
77
122
|
end
|
123
|
+
|
124
|
+
def serialize_to_json(data)
|
125
|
+
ActiveSupport::JSON.encode(data)
|
126
|
+
end
|
127
|
+
|
128
|
+
def deserialize_from_json(serialized)
|
129
|
+
ActiveSupport::JSON.decode(serialized)
|
130
|
+
rescue ::JSON::ParserError => error
|
131
|
+
# Throw :invalid_message_format instead of :invalid_message_serialization
|
132
|
+
# because here a parse error is due to a bad message rather than an
|
133
|
+
# incompatible `self.serializer`.
|
134
|
+
throw :invalid_message_format, error
|
135
|
+
end
|
136
|
+
|
137
|
+
def serialize_to_json_safe_string(data)
|
138
|
+
encode(serialize(data), url_safe: false)
|
139
|
+
end
|
140
|
+
|
141
|
+
def deserialize_from_json_safe_string(string)
|
142
|
+
deserialize(decode(string, url_safe: false))
|
143
|
+
end
|
78
144
|
end
|
79
145
|
end
|
80
146
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/slice"
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
module Messages
|
7
|
+
class RotationCoordinator # :nodoc:
|
8
|
+
attr_accessor :transitional
|
9
|
+
|
10
|
+
def initialize(&secret_generator)
|
11
|
+
raise ArgumentError, "A secret generator block is required" unless secret_generator
|
12
|
+
@secret_generator = secret_generator
|
13
|
+
@rotate_options = []
|
14
|
+
@on_rotation = nil
|
15
|
+
@codecs = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](salt)
|
19
|
+
@codecs[salt] ||= build_with_rotations(salt)
|
20
|
+
end
|
21
|
+
|
22
|
+
def []=(salt, codec)
|
23
|
+
@codecs[salt] = codec
|
24
|
+
end
|
25
|
+
|
26
|
+
def rotate(**options, &block)
|
27
|
+
raise ArgumentError, "Options cannot be specified when using a block" if block && !options.empty?
|
28
|
+
changing_configuration!
|
29
|
+
|
30
|
+
@rotate_options << (block || options)
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def rotate_defaults
|
36
|
+
rotate()
|
37
|
+
end
|
38
|
+
|
39
|
+
def clear_rotations
|
40
|
+
changing_configuration!
|
41
|
+
@rotate_options.clear
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_rotation(&callback)
|
46
|
+
changing_configuration!
|
47
|
+
@on_rotation = callback
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def changing_configuration!
|
52
|
+
if @codecs.any?
|
53
|
+
raise <<~MESSAGE
|
54
|
+
Cannot change #{self.class} configuration after it has already been applied.
|
55
|
+
|
56
|
+
The configuration has been applied with the following salts:
|
57
|
+
#{@codecs.keys.map { |salt| "- #{salt.inspect}" }.join("\n")}
|
58
|
+
MESSAGE
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def normalize_options(options)
|
63
|
+
options = options.dup
|
64
|
+
|
65
|
+
options[:secret_generator] ||= @secret_generator
|
66
|
+
|
67
|
+
secret_generator_kwargs = options[:secret_generator].parameters.
|
68
|
+
filter_map { |type, name| name if type == :key || type == :keyreq }
|
69
|
+
options[:secret_generator_options] = options.extract!(*secret_generator_kwargs)
|
70
|
+
|
71
|
+
options[:on_rotation] = @on_rotation
|
72
|
+
|
73
|
+
options
|
74
|
+
end
|
75
|
+
|
76
|
+
def build_with_rotations(salt)
|
77
|
+
rotate_options = @rotate_options.map { |options| options.is_a?(Proc) ? options.(salt) : options }
|
78
|
+
transitional = self.transitional && rotate_options.first
|
79
|
+
rotate_options.compact!
|
80
|
+
rotate_options[0..1] = rotate_options[0..1].reverse if transitional
|
81
|
+
rotate_options = rotate_options.map { |options| normalize_options(options) }.uniq
|
82
|
+
|
83
|
+
raise "No options have been configured for #{salt}" if rotate_options.empty?
|
84
|
+
|
85
|
+
rotate_options.map { |options| build(salt.to_s, **options) }.reduce(&:fall_back_to)
|
86
|
+
end
|
87
|
+
|
88
|
+
def build(salt, secret_generator:, secret_generator_options:, **options)
|
89
|
+
raise NotImplementedError
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -3,54 +3,56 @@
|
|
3
3
|
module ActiveSupport
|
4
4
|
module Messages
|
5
5
|
module Rotator # :nodoc:
|
6
|
-
def initialize(*
|
7
|
-
super(*
|
8
|
-
|
9
|
-
@options
|
6
|
+
def initialize(*args, on_rotation: nil, **options)
|
7
|
+
super(*args, **options)
|
8
|
+
@args = args
|
9
|
+
@options = options
|
10
10
|
@rotations = []
|
11
11
|
@on_rotation = on_rotation
|
12
12
|
end
|
13
13
|
|
14
|
-
def rotate(*
|
15
|
-
|
14
|
+
def rotate(*args, **options)
|
15
|
+
fall_back_to build_rotation(*args, **options)
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
super
|
23
|
-
rescue MessageEncryptor::InvalidMessage, MessageVerifier::InvalidSignature
|
24
|
-
run_rotations(on_rotation) { |encryptor| encryptor.decrypt_and_verify(*args, **options) } || raise
|
25
|
-
end
|
18
|
+
def fall_back_to(fallback)
|
19
|
+
@rotations << fallback
|
20
|
+
self
|
21
|
+
end
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
def read_message(message, on_rotation: @on_rotation, **options)
|
24
|
+
if @rotations.empty?
|
25
|
+
super(message, **options)
|
26
|
+
else
|
27
|
+
thrown, error = catch_rotation_error do
|
28
|
+
return super(message, **options)
|
30
29
|
end
|
31
|
-
end
|
32
30
|
|
33
|
-
|
34
|
-
|
31
|
+
@rotations.each do |rotation|
|
32
|
+
catch_rotation_error do
|
33
|
+
value = rotation.read_message(message, **options)
|
34
|
+
on_rotation&.call
|
35
|
+
return value
|
36
|
+
end
|
37
|
+
end
|
35
38
|
|
36
|
-
|
37
|
-
super || run_rotations(on_rotation) { |verifier| verifier.verified(*args, **options) }
|
39
|
+
throw thrown, error
|
38
40
|
end
|
39
|
-
|
40
|
-
private
|
41
|
-
def build_rotation(secret = @secret, options)
|
42
|
-
self.class.new(secret, **options)
|
43
|
-
end
|
44
41
|
end
|
45
42
|
|
46
43
|
private
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
44
|
+
def build_rotation(*args, **options)
|
45
|
+
self.class.new(*args, *@args.drop(args.length), **@options, **options)
|
46
|
+
end
|
47
|
+
|
48
|
+
def catch_rotation_error(&block)
|
49
|
+
error = catch :invalid_message_format do
|
50
|
+
error = catch :invalid_message_serialization do
|
51
|
+
return [nil, block.call]
|
52
52
|
end
|
53
|
+
return [:invalid_message_serialization, error]
|
53
54
|
end
|
55
|
+
[:invalid_message_format, error]
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/kernel/reporting"
|
4
|
+
require "active_support/notifications"
|
5
|
+
|
6
|
+
module ActiveSupport
|
7
|
+
module Messages # :nodoc:
|
8
|
+
module SerializerWithFallback # :nodoc:
|
9
|
+
def self.[](format)
|
10
|
+
if format.to_s.include?("message_pack") && !defined?(ActiveSupport::MessagePack)
|
11
|
+
require "active_support/message_pack"
|
12
|
+
end
|
13
|
+
|
14
|
+
SERIALIZERS.fetch(format)
|
15
|
+
end
|
16
|
+
|
17
|
+
def load(dumped)
|
18
|
+
format = detect_format(dumped)
|
19
|
+
|
20
|
+
if format == self.format
|
21
|
+
_load(dumped)
|
22
|
+
elsif format && fallback?(format)
|
23
|
+
payload = { serializer: SERIALIZERS.key(self), fallback: format, serialized: dumped }
|
24
|
+
ActiveSupport::Notifications.instrument("message_serializer_fallback.active_support", payload) do
|
25
|
+
payload[:deserialized] = SERIALIZERS[format]._load(dumped)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
raise "Unsupported serialization format"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def detect_format(dumped)
|
34
|
+
case
|
35
|
+
when MessagePackWithFallback.dumped?(dumped)
|
36
|
+
:message_pack
|
37
|
+
when MarshalWithFallback.dumped?(dumped)
|
38
|
+
:marshal
|
39
|
+
when JsonWithFallback.dumped?(dumped)
|
40
|
+
:json
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def fallback?(format)
|
45
|
+
format != :marshal
|
46
|
+
end
|
47
|
+
|
48
|
+
module AllowMarshal
|
49
|
+
private
|
50
|
+
def fallback?(format)
|
51
|
+
super || format == :marshal
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module MarshalWithFallback
|
56
|
+
include SerializerWithFallback
|
57
|
+
extend self
|
58
|
+
|
59
|
+
def format
|
60
|
+
:marshal
|
61
|
+
end
|
62
|
+
|
63
|
+
def dump(object)
|
64
|
+
Marshal.dump(object)
|
65
|
+
end
|
66
|
+
|
67
|
+
def _load(dumped)
|
68
|
+
Marshal.load(dumped)
|
69
|
+
end
|
70
|
+
|
71
|
+
MARSHAL_SIGNATURE = "\x04\x08"
|
72
|
+
|
73
|
+
def dumped?(dumped)
|
74
|
+
dumped.start_with?(MARSHAL_SIGNATURE)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module JsonWithFallback
|
79
|
+
include SerializerWithFallback
|
80
|
+
extend self
|
81
|
+
|
82
|
+
def format
|
83
|
+
:json
|
84
|
+
end
|
85
|
+
|
86
|
+
def dump(object)
|
87
|
+
ActiveSupport::JSON.encode(object)
|
88
|
+
end
|
89
|
+
|
90
|
+
def _load(dumped)
|
91
|
+
ActiveSupport::JSON.decode(dumped)
|
92
|
+
end
|
93
|
+
|
94
|
+
JSON_START_WITH = /\A(?:[{\["]|-?\d|true|false|null)/
|
95
|
+
|
96
|
+
def dumped?(dumped)
|
97
|
+
JSON_START_WITH.match?(dumped)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def detect_format(dumped)
|
102
|
+
# Assume JSON format if format could not be determined.
|
103
|
+
super || :json
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module JsonWithFallbackAllowMarshal
|
108
|
+
include JsonWithFallback
|
109
|
+
include AllowMarshal
|
110
|
+
extend self
|
111
|
+
end
|
112
|
+
|
113
|
+
module MessagePackWithFallback
|
114
|
+
include SerializerWithFallback
|
115
|
+
extend self
|
116
|
+
|
117
|
+
def format
|
118
|
+
:message_pack
|
119
|
+
end
|
120
|
+
|
121
|
+
def dump(object)
|
122
|
+
ActiveSupport::MessagePack.dump(object)
|
123
|
+
end
|
124
|
+
|
125
|
+
def _load(dumped)
|
126
|
+
ActiveSupport::MessagePack.load(dumped)
|
127
|
+
end
|
128
|
+
|
129
|
+
def dumped?(dumped)
|
130
|
+
available? && ActiveSupport::MessagePack.signature?(dumped)
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
def available?
|
135
|
+
return @available if defined?(@available)
|
136
|
+
silence_warnings { require "active_support/message_pack" }
|
137
|
+
@available = true
|
138
|
+
rescue LoadError
|
139
|
+
@available = false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
module MessagePackWithFallbackAllowMarshal
|
144
|
+
include MessagePackWithFallback
|
145
|
+
include AllowMarshal
|
146
|
+
extend self
|
147
|
+
end
|
148
|
+
|
149
|
+
SERIALIZERS = {
|
150
|
+
marshal: MarshalWithFallback,
|
151
|
+
json: JsonWithFallback,
|
152
|
+
json_allow_marshal: JsonWithFallbackAllowMarshal,
|
153
|
+
message_pack: MessagePackWithFallback,
|
154
|
+
message_pack_allow_marshal: MessagePackWithFallbackAllowMarshal,
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -3,11 +3,12 @@
|
|
3
3
|
require "active_support/json"
|
4
4
|
require "active_support/core_ext/string/access"
|
5
5
|
require "active_support/core_ext/string/behavior"
|
6
|
-
require "active_support/core_ext/symbol/starts_ends_with"
|
7
6
|
require "active_support/core_ext/module/delegation"
|
8
7
|
|
9
|
-
module ActiveSupport
|
10
|
-
module Multibyte
|
8
|
+
module ActiveSupport # :nodoc:
|
9
|
+
module Multibyte # :nodoc:
|
10
|
+
# = Active Support \Multibyte \Chars
|
11
|
+
#
|
11
12
|
# Chars enables you to work transparently with UTF-8 encoding in the Ruby
|
12
13
|
# String class without having extensive knowledge about the encoding. A
|
13
14
|
# Chars object accepts a string upon initialization and proxies String
|
@@ -103,7 +104,7 @@ module ActiveSupport #:nodoc:
|
|
103
104
|
#
|
104
105
|
# 'Café'.mb_chars.reverse.to_s # => 'éfaC'
|
105
106
|
def reverse
|
106
|
-
chars(@wrapped_string.
|
107
|
+
chars(@wrapped_string.grapheme_clusters.reverse.join)
|
107
108
|
end
|
108
109
|
|
109
110
|
# Limits the byte size of the string to a number of bytes without breaking
|
@@ -126,16 +127,16 @@ module ActiveSupport #:nodoc:
|
|
126
127
|
|
127
128
|
# Performs canonical decomposition on all the characters.
|
128
129
|
#
|
129
|
-
# 'é'.length # =>
|
130
|
-
# 'é'.mb_chars.decompose.to_s.length # =>
|
130
|
+
# 'é'.length # => 1
|
131
|
+
# 'é'.mb_chars.decompose.to_s.length # => 2
|
131
132
|
def decompose
|
132
133
|
chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack("U*"))
|
133
134
|
end
|
134
135
|
|
135
136
|
# Performs composition on all the characters.
|
136
137
|
#
|
137
|
-
# 'é'.length # =>
|
138
|
-
# 'é'.mb_chars.compose.to_s.length # =>
|
138
|
+
# 'é'.length # => 1
|
139
|
+
# 'é'.mb_chars.compose.to_s.length # => 1
|
139
140
|
def compose
|
140
141
|
chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack("U*"))
|
141
142
|
end
|
@@ -143,9 +144,9 @@ module ActiveSupport #:nodoc:
|
|
143
144
|
# Returns the number of grapheme clusters in the string.
|
144
145
|
#
|
145
146
|
# 'क्षि'.mb_chars.length # => 4
|
146
|
-
# 'क्षि'.mb_chars.grapheme_length # =>
|
147
|
+
# 'क्षि'.mb_chars.grapheme_length # => 2
|
147
148
|
def grapheme_length
|
148
|
-
@wrapped_string.
|
149
|
+
@wrapped_string.grapheme_clusters.length
|
149
150
|
end
|
150
151
|
|
151
152
|
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
|
@@ -157,7 +158,7 @@ module ActiveSupport #:nodoc:
|
|
157
158
|
chars(Unicode.tidy_bytes(@wrapped_string, force))
|
158
159
|
end
|
159
160
|
|
160
|
-
def as_json(options = nil)
|
161
|
+
def as_json(options = nil) # :nodoc:
|
161
162
|
to_s.as_json(options)
|
162
163
|
end
|
163
164
|
|
@@ -8,18 +8,6 @@ module ActiveSupport
|
|
8
8
|
# The Unicode version that is supported by the implementation
|
9
9
|
UNICODE_VERSION = RbConfig::CONFIG["UNICODE_VERSION"]
|
10
10
|
|
11
|
-
def default_normalization_form
|
12
|
-
ActiveSupport::Deprecation.warn(
|
13
|
-
"ActiveSupport::Multibyte::Unicode.default_normalization_form is deprecated and will be removed in Rails 6.2."
|
14
|
-
)
|
15
|
-
end
|
16
|
-
|
17
|
-
def default_normalization_form=(_)
|
18
|
-
ActiveSupport::Deprecation.warn(
|
19
|
-
"ActiveSupport::Multibyte::Unicode.default_normalization_form= is deprecated and will be removed in Rails 6.2."
|
20
|
-
)
|
21
|
-
end
|
22
|
-
|
23
11
|
# Decompose composed characters to the decomposed form.
|
24
12
|
def decompose(type, codepoints)
|
25
13
|
if type == :compatibility
|
@@ -34,43 +22,15 @@ module ActiveSupport
|
|
34
22
|
codepoints.pack("U*").unicode_normalize(:nfc).codepoints
|
35
23
|
end
|
36
24
|
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
return recode_windows1252_chars(string) if force
|
47
|
-
string.scrub { |bad| recode_windows1252_chars(bad) }
|
48
|
-
end
|
49
|
-
else
|
50
|
-
def tidy_bytes(string, force = false)
|
51
|
-
return string if string.empty?
|
52
|
-
return recode_windows1252_chars(string) if force
|
53
|
-
|
54
|
-
# We can't transcode to the same format, so we choose a nearly-identical encoding.
|
55
|
-
# We're going to 'transcode' bytes from UTF-8 when possible, then fall back to
|
56
|
-
# CP1252 when we get errors. The final string will be 'converted' back to UTF-8
|
57
|
-
# before returning.
|
58
|
-
reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_16LE)
|
59
|
-
|
60
|
-
source = string.dup
|
61
|
-
out = "".force_encoding(Encoding::UTF_16LE)
|
62
|
-
|
63
|
-
loop do
|
64
|
-
reader.primitive_convert(source, out)
|
65
|
-
_, _, _, error_bytes, _ = reader.primitive_errinfo
|
66
|
-
break if error_bytes.nil?
|
67
|
-
out << error_bytes.encode(Encoding::UTF_16LE, Encoding::Windows_1252, invalid: :replace, undef: :replace)
|
68
|
-
end
|
69
|
-
|
70
|
-
reader.finish
|
71
|
-
|
72
|
-
out.encode!(Encoding::UTF_8)
|
73
|
-
end
|
25
|
+
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
|
26
|
+
# resulting in a valid UTF-8 string.
|
27
|
+
#
|
28
|
+
# Passing +true+ will forcibly tidy all bytes, assuming that the string's
|
29
|
+
# encoding is entirely CP1252 or ISO-8859-1.
|
30
|
+
def tidy_bytes(string, force = false)
|
31
|
+
return string if string.empty? || string.ascii_only?
|
32
|
+
return recode_windows1252_chars(string) if force
|
33
|
+
string.scrub { |bad| recode_windows1252_chars(bad) }
|
74
34
|
end
|
75
35
|
|
76
36
|
private
|