activesupport 6.0.6.1 → 7.1.3.2
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 +865 -438
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/active_support/actionable_error.rb +4 -2
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +30 -10
- data/lib/active_support/benchmarkable.rb +4 -3
- data/lib/active_support/broadcast_logger.rb +250 -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 +208 -63
- data/lib/active_support/cache/memory_store.rb +120 -38
- data/lib/active_support/cache/null_store.rb +16 -2
- data/lib/active_support/cache/redis_cache_store.rb +201 -208
- data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
- data/lib/active_support/cache/strategy/local_cache.rb +73 -66
- data/lib/active_support/cache.rb +539 -261
- data/lib/active_support/callbacks.rb +273 -142
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +53 -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 +19 -6
- data/lib/active_support/configuration_file.rb +51 -0
- 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/benchmark.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +19 -29
- 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 +18 -16
- data/lib/active_support/core_ext/date_and_time/calculations.rb +27 -4
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- 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 +146 -72
- 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 +3 -4
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +4 -4
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +5 -5
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- 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/load_error.rb +1 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +31 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +51 -20
- data/lib/active_support/core_ext/module/concerning.rb +14 -8
- data/lib/active_support/core_ext/module/delegation.rb +75 -42
- data/lib/active_support/core_ext/module/deprecation.rb +15 -12
- data/lib/active_support/core_ext/module/introspection.rb +1 -26
- data/lib/active_support/core_ext/name_error.rb +23 -2
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +82 -73
- 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 +15 -4
- 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 +52 -28
- 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 +6 -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/regexp.rb +8 -1
- data/lib/active_support/core_ext/securerandom.rb +25 -13
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +3 -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 +51 -10
- data/lib/active_support/core_ext/string/inquiry.rb +2 -1
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +85 -194
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +46 -8
- data/lib/active_support/core_ext/time/conversions.rb +16 -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/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +54 -22
- 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 -769
- data/lib/active_support/deprecation/behaviors.rb +77 -38
- 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 +54 -0
- data/lib/active_support/deprecation/instance_delegator.rb +31 -5
- data/lib/active_support/deprecation/method_wrappers.rb +12 -28
- data/lib/active_support/deprecation/proxy_wrappers.rb +40 -25
- data/lib/active_support/deprecation/reporting.rb +76 -16
- data/lib/active_support/deprecation.rb +36 -4
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +150 -68
- 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 +24 -12
- data/lib/active_support/duration.rb +136 -56
- data/lib/active_support/encrypted_configuration.rb +72 -9
- data/lib/active_support/encrypted_file.rb +46 -13
- data/lib/active_support/environment_inquirer.rb +40 -0
- 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 +86 -137
- 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 +31 -12
- 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 +79 -0
- data/lib/active_support/gem_version.rb +5 -5
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +86 -42
- 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 +29 -27
- data/lib/active_support/inflector/inflections.rb +26 -9
- data/lib/active_support/inflector/methods.rb +54 -64
- data/lib/active_support/inflector/transliterate.rb +7 -5
- data/lib/active_support/isolated_execution_state.rb +76 -0
- data/lib/active_support/json/decoding.rb +6 -5
- data/lib/active_support/json/encoding.rb +31 -45
- data/lib/active_support/key_generator.rb +32 -7
- data/lib/active_support/lazy_load_hooks.rb +33 -7
- data/lib/active_support/locale/en.yml +10 -4
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +101 -32
- data/lib/active_support/logger.rb +9 -60
- data/lib/active_support/logger_silence.rb +2 -26
- data/lib/active_support/logger_thread_safe_level.rb +24 -25
- data/lib/active_support/message_encryptor.rb +205 -58
- 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 +237 -86
- 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_configuration.rb +2 -1
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +35 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +15 -52
- data/lib/active_support/multibyte/unicode.rb +8 -122
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +310 -105
- data/lib/active_support/notifications/instrumenter.rb +113 -48
- data/lib/active_support/notifications.rb +56 -29
- data/lib/active_support/number_helper/number_converter.rb +15 -8
- 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_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -5
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
- data/lib/active_support/number_helper/rounding_helper.rb +12 -32
- data/lib/active_support/number_helper.rb +379 -304
- data/lib/active_support/option_merger.rb +11 -18
- data/lib/active_support/ordered_hash.rb +4 -4
- data/lib/active_support/ordered_options.rb +23 -3
- data/lib/active_support/parameter_filter.rb +104 -75
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +90 -6
- data/lib/active_support/reloader.rb +12 -4
- 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 +58 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +5 -3
- data/lib/active_support/subscriber.rb +23 -47
- data/lib/active_support/syntax_error_proxy.rb +70 -0
- data/lib/active_support/tagged_logging.rb +84 -23
- data/lib/active_support/test_case.rb +166 -27
- data/lib/active_support/testing/assertions.rb +73 -20
- 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 +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +16 -95
- data/lib/active_support/testing/parallelize_executor.rb +81 -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 +89 -19
- data/lib/active_support/time_with_zone.rb +105 -70
- data/lib/active_support/values/time_zone.rb +59 -26
- 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 +9 -2
- data/lib/active_support/xml_mini.rb +7 -6
- data/lib/active_support.rb +40 -1
- metadata +127 -40
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
- data/lib/active_support/core_ext/hash/compact.rb +0 -5
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
- data/lib/active_support/core_ext/marshal.rb +0 -24
- data/lib/active_support/core_ext/module/reachable.rb +0 -6
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
- data/lib/active_support/core_ext/range/include_range.rb +0 -9
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -23
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/uri.rb +0 -25
- 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,53 +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
|
+
@on_rotation = on_rotation
|
11
12
|
end
|
12
13
|
|
13
|
-
def rotate(*
|
14
|
-
|
14
|
+
def rotate(*args, **options)
|
15
|
+
fall_back_to build_rotation(*args, **options)
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
super
|
22
|
-
rescue MessageEncryptor::InvalidMessage, MessageVerifier::InvalidSignature
|
23
|
-
run_rotations(on_rotation) { |encryptor| encryptor.decrypt_and_verify(*args, **options) } || raise
|
24
|
-
end
|
18
|
+
def fall_back_to(fallback)
|
19
|
+
@rotations << fallback
|
20
|
+
self
|
21
|
+
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
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)
|
29
29
|
end
|
30
|
-
end
|
31
30
|
|
32
|
-
|
33
|
-
|
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
|
34
38
|
|
35
|
-
|
36
|
-
super || run_rotations(on_rotation) { |verifier| verifier.verified(*args, **options) }
|
39
|
+
throw thrown, error
|
37
40
|
end
|
38
|
-
|
39
|
-
private
|
40
|
-
def build_rotation(secret = @secret, options)
|
41
|
-
self.class.new(secret, **options)
|
42
|
-
end
|
43
41
|
end
|
44
42
|
|
45
43
|
private
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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]
|
51
52
|
end
|
53
|
+
return [:invalid_message_serialization, error]
|
52
54
|
end
|
55
|
+
[:invalid_message_format, error]
|
53
56
|
end
|
54
57
|
end
|
55
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
|
@@ -5,8 +5,10 @@ require "active_support/core_ext/string/access"
|
|
5
5
|
require "active_support/core_ext/string/behavior"
|
6
6
|
require "active_support/core_ext/module/delegation"
|
7
7
|
|
8
|
-
module ActiveSupport
|
9
|
-
module Multibyte
|
8
|
+
module ActiveSupport # :nodoc:
|
9
|
+
module Multibyte # :nodoc:
|
10
|
+
# = Active Support \Multibyte \Chars
|
11
|
+
#
|
10
12
|
# Chars enables you to work transparently with UTF-8 encoding in the Ruby
|
11
13
|
# String class without having extensive knowledge about the encoding. A
|
12
14
|
# Chars object accepts a string upon initialization and proxies String
|
@@ -48,7 +50,7 @@ module ActiveSupport #:nodoc:
|
|
48
50
|
alias to_s wrapped_string
|
49
51
|
alias to_str wrapped_string
|
50
52
|
|
51
|
-
delegate :<=>, :=~, :acts_like_string?, to: :wrapped_string
|
53
|
+
delegate :<=>, :=~, :match?, :acts_like_string?, to: :wrapped_string
|
52
54
|
|
53
55
|
# Creates a new Chars instance by wrapping _string_.
|
54
56
|
def initialize(string)
|
@@ -59,7 +61,7 @@ module ActiveSupport #:nodoc:
|
|
59
61
|
# Forward all undefined methods to the wrapped string.
|
60
62
|
def method_missing(method, *args, &block)
|
61
63
|
result = @wrapped_string.__send__(method, *args, &block)
|
62
|
-
if
|
64
|
+
if method.end_with?("!")
|
63
65
|
self if result
|
64
66
|
else
|
65
67
|
result.kind_of?(String) ? chars(result) : result
|
@@ -73,17 +75,6 @@ module ActiveSupport #:nodoc:
|
|
73
75
|
@wrapped_string.respond_to?(method, include_private)
|
74
76
|
end
|
75
77
|
|
76
|
-
# Returns +true+ when the proxy class can handle the string. Returns
|
77
|
-
# +false+ otherwise.
|
78
|
-
def self.consumes?(string)
|
79
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
80
|
-
ActiveSupport::Multibyte::Chars.consumes? is deprecated and will be
|
81
|
-
removed from Rails 6.1. Use string.is_utf8? instead.
|
82
|
-
MSG
|
83
|
-
|
84
|
-
string.encoding == Encoding::UTF_8
|
85
|
-
end
|
86
|
-
|
87
78
|
# Works just like <tt>String#split</tt>, with the exception that the items
|
88
79
|
# in the resulting list are Chars instances instead of String. This makes
|
89
80
|
# chaining methods easier.
|
@@ -113,7 +104,7 @@ module ActiveSupport #:nodoc:
|
|
113
104
|
#
|
114
105
|
# 'Café'.mb_chars.reverse.to_s # => 'éfaC'
|
115
106
|
def reverse
|
116
|
-
chars(@wrapped_string.
|
107
|
+
chars(@wrapped_string.grapheme_clusters.reverse.join)
|
117
108
|
end
|
118
109
|
|
119
110
|
# Limits the byte size of the string to a number of bytes without breaking
|
@@ -134,46 +125,18 @@ module ActiveSupport #:nodoc:
|
|
134
125
|
end
|
135
126
|
alias_method :titlecase, :titleize
|
136
127
|
|
137
|
-
# Returns the KC normalization of the string by default. NFKC is
|
138
|
-
# considered the best normalization form for passing strings to databases
|
139
|
-
# and validations.
|
140
|
-
#
|
141
|
-
# * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
|
142
|
-
# <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
|
143
|
-
# ActiveSupport::Multibyte::Unicode.default_normalization_form
|
144
|
-
def normalize(form = nil)
|
145
|
-
form ||= Unicode.default_normalization_form
|
146
|
-
|
147
|
-
# See https://www.unicode.org/reports/tr15, Table 1
|
148
|
-
if alias_form = Unicode::NORMALIZATION_FORM_ALIASES[form]
|
149
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
150
|
-
ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
|
151
|
-
removed from Rails 6.1. Use #unicode_normalize(:#{alias_form}) instead.
|
152
|
-
MSG
|
153
|
-
|
154
|
-
send(:unicode_normalize, alias_form)
|
155
|
-
else
|
156
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
157
|
-
ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
|
158
|
-
removed from Rails 6.1. Use #unicode_normalize instead.
|
159
|
-
MSG
|
160
|
-
|
161
|
-
raise ArgumentError, "#{form} is not a valid normalization variant", caller
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
128
|
# Performs canonical decomposition on all the characters.
|
166
129
|
#
|
167
|
-
# 'é'.length # =>
|
168
|
-
# 'é'.mb_chars.decompose.to_s.length # =>
|
130
|
+
# 'é'.length # => 1
|
131
|
+
# 'é'.mb_chars.decompose.to_s.length # => 2
|
169
132
|
def decompose
|
170
133
|
chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack("U*"))
|
171
134
|
end
|
172
135
|
|
173
136
|
# Performs composition on all the characters.
|
174
137
|
#
|
175
|
-
# 'é'.length # =>
|
176
|
-
# 'é'.mb_chars.compose.to_s.length # =>
|
138
|
+
# 'é'.length # => 1
|
139
|
+
# 'é'.mb_chars.compose.to_s.length # => 1
|
177
140
|
def compose
|
178
141
|
chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack("U*"))
|
179
142
|
end
|
@@ -181,9 +144,9 @@ module ActiveSupport #:nodoc:
|
|
181
144
|
# Returns the number of grapheme clusters in the string.
|
182
145
|
#
|
183
146
|
# 'क्षि'.mb_chars.length # => 4
|
184
|
-
# 'क्षि'.mb_chars.grapheme_length # =>
|
147
|
+
# 'क्षि'.mb_chars.grapheme_length # => 2
|
185
148
|
def grapheme_length
|
186
|
-
@wrapped_string.
|
149
|
+
@wrapped_string.grapheme_clusters.length
|
187
150
|
end
|
188
151
|
|
189
152
|
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
|
@@ -195,13 +158,13 @@ module ActiveSupport #:nodoc:
|
|
195
158
|
chars(Unicode.tidy_bytes(@wrapped_string, force))
|
196
159
|
end
|
197
160
|
|
198
|
-
def as_json(options = nil)
|
161
|
+
def as_json(options = nil) # :nodoc:
|
199
162
|
to_s.as_json(options)
|
200
163
|
end
|
201
164
|
|
202
165
|
%w(reverse tidy_bytes).each do |method|
|
203
166
|
define_method("#{method}!") do |*args|
|
204
|
-
@wrapped_string =
|
167
|
+
@wrapped_string = public_send(method, *args).to_s
|
205
168
|
self
|
206
169
|
end
|
207
170
|
end
|