activesupport 1.2.4 → 8.1.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.
Files changed (309) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +505 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support/actionable_error.rb +50 -0
  6. data/lib/active_support/all.rb +5 -0
  7. data/lib/active_support/array_inquirer.rb +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +234 -0
  9. data/lib/active_support/benchmark.rb +21 -0
  10. data/lib/active_support/benchmarkable.rb +53 -0
  11. data/lib/active_support/broadcast_logger.rb +238 -0
  12. data/lib/active_support/builder.rb +8 -0
  13. data/lib/active_support/cache/coder.rb +153 -0
  14. data/lib/active_support/cache/entry.rb +134 -0
  15. data/lib/active_support/cache/file_store.rb +244 -0
  16. data/lib/active_support/cache/mem_cache_store.rb +288 -0
  17. data/lib/active_support/cache/memory_store.rb +264 -0
  18. data/lib/active_support/cache/null_store.rb +62 -0
  19. data/lib/active_support/cache/redis_cache_store.rb +498 -0
  20. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  21. data/lib/active_support/cache/strategy/local_cache.rb +246 -0
  22. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  23. data/lib/active_support/cache.rb +1170 -0
  24. data/lib/active_support/callbacks.rb +960 -0
  25. data/lib/active_support/class_attribute.rb +33 -0
  26. data/lib/active_support/code_generator.rb +79 -0
  27. data/lib/active_support/concern.rb +217 -0
  28. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  29. data/lib/active_support/concurrency/null_lock.rb +13 -0
  30. data/lib/active_support/concurrency/share_lock.rb +225 -0
  31. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  32. data/lib/active_support/configurable.rb +193 -0
  33. data/lib/active_support/configuration_file.rb +60 -0
  34. data/lib/active_support/continuous_integration.rb +145 -0
  35. data/lib/active_support/core_ext/array/access.rb +100 -0
  36. data/lib/active_support/core_ext/array/conversions.rb +209 -26
  37. data/lib/active_support/core_ext/array/extract.rb +21 -0
  38. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  39. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  40. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  41. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  42. data/lib/active_support/core_ext/array.rb +8 -4
  43. data/lib/active_support/core_ext/benchmark.rb +6 -0
  44. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  45. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  46. data/lib/active_support/core_ext/class/attribute.rb +137 -0
  47. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  48. data/lib/active_support/core_ext/class/subclasses.rb +24 -0
  49. data/lib/active_support/core_ext/class.rb +4 -0
  50. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  51. data/lib/active_support/core_ext/date/blank.rb +18 -0
  52. data/lib/active_support/core_ext/date/calculations.rb +161 -0
  53. data/lib/active_support/core_ext/date/conversions.rb +95 -28
  54. data/lib/active_support/core_ext/date/zones.rb +8 -0
  55. data/lib/active_support/core_ext/date.rb +6 -5
  56. data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
  57. data/lib/active_support/core_ext/date_and_time/compatibility.rb +23 -0
  58. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  59. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  60. data/lib/active_support/core_ext/date_time/blank.rb +18 -0
  61. data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
  62. data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
  63. data/lib/active_support/core_ext/date_time/conversions.rb +108 -0
  64. data/lib/active_support/core_ext/date_time.rb +7 -0
  65. data/lib/active_support/core_ext/digest/uuid.rb +76 -0
  66. data/lib/active_support/core_ext/digest.rb +3 -0
  67. data/lib/active_support/core_ext/enumerable.rb +277 -7
  68. data/lib/active_support/core_ext/erb/util.rb +201 -0
  69. data/lib/active_support/core_ext/file/atomic.rb +72 -0
  70. data/lib/active_support/core_ext/file.rb +3 -0
  71. data/lib/active_support/core_ext/hash/conversions.rb +262 -0
  72. data/lib/active_support/core_ext/hash/deep_merge.rb +43 -0
  73. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  74. data/lib/active_support/core_ext/hash/except.rb +12 -0
  75. data/lib/active_support/core_ext/hash/indifferent_access.rb +19 -55
  76. data/lib/active_support/core_ext/hash/keys.rb +134 -44
  77. data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -22
  78. data/lib/active_support/core_ext/hash/slice.rb +27 -0
  79. data/lib/active_support/core_ext/hash.rb +9 -8
  80. data/lib/active_support/core_ext/integer/inflections.rb +29 -13
  81. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  82. data/lib/active_support/core_ext/integer/time.rb +22 -0
  83. data/lib/active_support/core_ext/integer.rb +4 -6
  84. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  85. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  86. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  87. data/lib/active_support/core_ext/kernel.rb +4 -78
  88. data/lib/active_support/core_ext/load_error.rb +6 -35
  89. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  90. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  91. data/lib/active_support/core_ext/module/attr_internal.rb +48 -0
  92. data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
  93. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
  94. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  95. data/lib/active_support/core_ext/module/delegation.rb +225 -0
  96. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  97. data/lib/active_support/core_ext/module/introspection.rb +65 -0
  98. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  99. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  100. data/lib/active_support/core_ext/module.rb +13 -0
  101. data/lib/active_support/core_ext/name_error.rb +59 -0
  102. data/lib/active_support/core_ext/numeric/bytes.rb +73 -42
  103. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  104. data/lib/active_support/core_ext/numeric/time.rb +64 -57
  105. data/lib/active_support/core_ext/numeric.rb +4 -6
  106. data/lib/active_support/core_ext/object/acts_like.rb +45 -0
  107. data/lib/active_support/core_ext/object/blank.rb +199 -0
  108. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  109. data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
  110. data/lib/active_support/core_ext/object/duplicable.rb +69 -0
  111. data/lib/active_support/core_ext/object/inclusion.rb +37 -0
  112. data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
  113. data/lib/active_support/core_ext/object/json.rb +267 -0
  114. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  115. data/lib/active_support/core_ext/object/to_query.rb +93 -0
  116. data/lib/active_support/core_ext/object/try.rb +158 -0
  117. data/lib/active_support/core_ext/object/with.rb +46 -0
  118. data/lib/active_support/core_ext/object/with_options.rb +101 -0
  119. data/lib/active_support/core_ext/object.rb +17 -0
  120. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  121. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  122. data/lib/active_support/core_ext/pathname.rb +4 -0
  123. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  124. data/lib/active_support/core_ext/range/conversions.rb +58 -17
  125. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  126. data/lib/active_support/core_ext/range/sole.rb +17 -0
  127. data/lib/active_support/core_ext/range.rb +5 -4
  128. data/lib/active_support/core_ext/regexp.rb +14 -0
  129. data/lib/active_support/core_ext/securerandom.rb +57 -0
  130. data/lib/active_support/core_ext/string/access.rb +93 -56
  131. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  132. data/lib/active_support/core_ext/string/conversions.rb +57 -16
  133. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  134. data/lib/active_support/core_ext/string/filters.rb +151 -0
  135. data/lib/active_support/core_ext/string/indent.rb +45 -0
  136. data/lib/active_support/core_ext/string/inflections.rb +297 -54
  137. data/lib/active_support/core_ext/string/inquiry.rb +16 -0
  138. data/lib/active_support/core_ext/string/multibyte.rb +67 -0
  139. data/lib/active_support/core_ext/string/output_safety.rb +235 -0
  140. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -18
  141. data/lib/active_support/core_ext/string/strip.rb +27 -0
  142. data/lib/active_support/core_ext/string/zones.rb +16 -0
  143. data/lib/active_support/core_ext/string.rb +14 -10
  144. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  145. data/lib/active_support/core_ext/symbol.rb +3 -0
  146. data/lib/active_support/core_ext/thread/backtrace/location.rb +7 -0
  147. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  148. data/lib/active_support/core_ext/time/calculations.rb +358 -153
  149. data/lib/active_support/core_ext/time/compatibility.rb +15 -0
  150. data/lib/active_support/core_ext/time/conversions.rb +69 -30
  151. data/lib/active_support/core_ext/time/zones.rb +97 -0
  152. data/lib/active_support/core_ext/time.rb +6 -6
  153. data/lib/active_support/core_ext.rb +5 -1
  154. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  155. data/lib/active_support/current_attributes.rb +243 -0
  156. data/lib/active_support/deep_mergeable.rb +53 -0
  157. data/lib/active_support/delegation.rb +183 -0
  158. data/lib/active_support/dependencies/autoload.rb +72 -0
  159. data/lib/active_support/dependencies/interlock.rb +55 -0
  160. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  161. data/lib/active_support/dependencies.rb +84 -222
  162. data/lib/active_support/deprecation/behaviors.rb +148 -0
  163. data/lib/active_support/deprecation/constant_accessor.rb +74 -0
  164. data/lib/active_support/deprecation/deprecators.rb +104 -0
  165. data/lib/active_support/deprecation/disallowed.rb +54 -0
  166. data/lib/active_support/deprecation/method_wrappers.rb +68 -0
  167. data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
  168. data/lib/active_support/deprecation/reporting.rb +162 -0
  169. data/lib/active_support/deprecation.rb +81 -0
  170. data/lib/active_support/deprecator.rb +7 -0
  171. data/lib/active_support/descendants_tracker.rb +112 -0
  172. data/lib/active_support/digest.rb +22 -0
  173. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  174. data/lib/active_support/duration/iso8601_serializer.rb +64 -0
  175. data/lib/active_support/duration.rb +524 -0
  176. data/lib/active_support/editor.rb +70 -0
  177. data/lib/active_support/encrypted_configuration.rb +126 -0
  178. data/lib/active_support/encrypted_file.rb +133 -0
  179. data/lib/active_support/environment_inquirer.rb +40 -0
  180. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  181. data/lib/active_support/error_reporter.rb +318 -0
  182. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  183. data/lib/active_support/event_reporter.rb +592 -0
  184. data/lib/active_support/evented_file_update_checker.rb +185 -0
  185. data/lib/active_support/execution_context/test_helper.rb +13 -0
  186. data/lib/active_support/execution_context.rb +110 -0
  187. data/lib/active_support/execution_wrapper.rb +150 -0
  188. data/lib/active_support/executor/test_helper.rb +7 -0
  189. data/lib/active_support/executor.rb +8 -0
  190. data/lib/active_support/file_update_checker.rb +166 -0
  191. data/lib/active_support/fork_tracker.rb +43 -0
  192. data/lib/active_support/gem_version.rb +17 -0
  193. data/lib/active_support/gzip.rb +41 -0
  194. data/lib/active_support/hash_with_indifferent_access.rb +464 -0
  195. data/lib/active_support/html_safe_translation.rb +56 -0
  196. data/lib/active_support/i18n.rb +17 -0
  197. data/lib/active_support/i18n_railtie.rb +140 -0
  198. data/lib/active_support/inflections.rb +68 -49
  199. data/lib/active_support/inflector/inflections.rb +290 -0
  200. data/lib/active_support/inflector/methods.rb +387 -0
  201. data/lib/active_support/inflector/transliterate.rb +147 -0
  202. data/lib/active_support/inflector.rb +7 -164
  203. data/lib/active_support/isolated_execution_state.rb +76 -0
  204. data/lib/active_support/json/decoding.rb +78 -0
  205. data/lib/active_support/json/encoding.rb +256 -0
  206. data/lib/active_support/json.rb +4 -0
  207. data/lib/active_support/key_generator.rb +66 -0
  208. data/lib/active_support/lazy_load_hooks.rb +107 -0
  209. data/lib/active_support/locale/en.rb +33 -0
  210. data/lib/active_support/locale/en.yml +141 -0
  211. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  212. data/lib/active_support/log_subscriber.rb +188 -0
  213. data/lib/active_support/logger.rb +55 -0
  214. data/lib/active_support/logger_silence.rb +21 -0
  215. data/lib/active_support/logger_thread_safe_level.rb +50 -0
  216. data/lib/active_support/message_encryptor.rb +374 -0
  217. data/lib/active_support/message_encryptors.rb +193 -0
  218. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  219. data/lib/active_support/message_pack/extensions.rb +310 -0
  220. data/lib/active_support/message_pack/serializer.rb +63 -0
  221. data/lib/active_support/message_pack.rb +50 -0
  222. data/lib/active_support/message_verifier.rb +377 -0
  223. data/lib/active_support/message_verifiers.rb +189 -0
  224. data/lib/active_support/messages/codec.rb +65 -0
  225. data/lib/active_support/messages/metadata.rb +146 -0
  226. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  227. data/lib/active_support/messages/rotation_coordinator.rb +102 -0
  228. data/lib/active_support/messages/rotator.rb +69 -0
  229. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  230. data/lib/active_support/multibyte/chars.rb +188 -0
  231. data/lib/active_support/multibyte/unicode.rb +42 -0
  232. data/lib/active_support/multibyte.rb +27 -0
  233. data/lib/active_support/notifications/fanout.rb +467 -0
  234. data/lib/active_support/notifications/instrumenter.rb +240 -0
  235. data/lib/active_support/notifications.rb +281 -0
  236. data/lib/active_support/number_helper/number_converter.rb +190 -0
  237. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  238. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  239. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  240. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  241. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  242. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  243. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  244. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  245. data/lib/active_support/number_helper.rb +479 -0
  246. data/lib/active_support/option_merger.rb +38 -0
  247. data/lib/active_support/ordered_hash.rb +50 -0
  248. data/lib/active_support/ordered_options.rb +141 -25
  249. data/lib/active_support/parameter_filter.rb +157 -0
  250. data/lib/active_support/rails.rb +26 -0
  251. data/lib/active_support/railtie.rb +180 -0
  252. data/lib/active_support/reloader.rb +138 -0
  253. data/lib/active_support/rescuable.rb +176 -0
  254. data/lib/active_support/secure_compare_rotator.rb +58 -0
  255. data/lib/active_support/security_utils.rb +38 -0
  256. data/lib/active_support/string_inquirer.rb +35 -0
  257. data/lib/active_support/structured_event_subscriber.rb +99 -0
  258. data/lib/active_support/subscriber.rb +141 -0
  259. data/lib/active_support/syntax_error_proxy.rb +67 -0
  260. data/lib/active_support/tagged_logging.rb +157 -0
  261. data/lib/active_support/test_case.rb +365 -0
  262. data/lib/active_support/testing/assertions.rb +369 -0
  263. data/lib/active_support/testing/autorun.rb +10 -0
  264. data/lib/active_support/testing/constant_lookup.rb +51 -0
  265. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  266. data/lib/active_support/testing/declarative.rb +28 -0
  267. data/lib/active_support/testing/deprecation.rb +82 -0
  268. data/lib/active_support/testing/error_reporter_assertions.rb +124 -0
  269. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  270. data/lib/active_support/testing/file_fixtures.rb +38 -0
  271. data/lib/active_support/testing/isolation.rb +121 -0
  272. data/lib/active_support/testing/method_call_assertions.rb +69 -0
  273. data/lib/active_support/testing/notification_assertions.rb +92 -0
  274. data/lib/active_support/testing/parallelization/server.rb +98 -0
  275. data/lib/active_support/testing/parallelization/worker.rb +107 -0
  276. data/lib/active_support/testing/parallelization.rb +79 -0
  277. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  278. data/lib/active_support/testing/setup_and_teardown.rb +57 -0
  279. data/lib/active_support/testing/stream.rb +41 -0
  280. data/lib/active_support/testing/tagged_logging.rb +27 -0
  281. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  282. data/lib/active_support/testing/time_helpers.rb +273 -0
  283. data/lib/active_support/time.rb +20 -0
  284. data/lib/active_support/time_with_zone.rb +613 -0
  285. data/lib/active_support/values/time_zone.rb +599 -158
  286. data/lib/active_support/version.rb +7 -6
  287. data/lib/active_support/xml_mini/jdom.rb +175 -0
  288. data/lib/active_support/xml_mini/libxml.rb +80 -0
  289. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  290. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  291. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  292. data/lib/active_support/xml_mini/rexml.rb +137 -0
  293. data/lib/active_support/xml_mini.rb +212 -0
  294. data/lib/active_support.rb +122 -10
  295. metadata +524 -93
  296. data/CHANGELOG +0 -283
  297. data/lib/active_support/binding_of_caller.rb +0 -84
  298. data/lib/active_support/breakpoint.rb +0 -523
  299. data/lib/active_support/class_attribute_accessors.rb +0 -57
  300. data/lib/active_support/class_inheritable_attributes.rb +0 -117
  301. data/lib/active_support/clean_logger.rb +0 -36
  302. data/lib/active_support/core_ext/blank.rb +0 -38
  303. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -14
  304. data/lib/active_support/core_ext/cgi.rb +0 -5
  305. data/lib/active_support/core_ext/exception.rb +0 -29
  306. data/lib/active_support/core_ext/integer/even_odd.rb +0 -24
  307. data/lib/active_support/core_ext/object_and_class.rb +0 -44
  308. data/lib/active_support/module_attribute_accessors.rb +0 -57
  309. data/lib/active_support/whiny_nil.rb +0 -38
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require "active_support/json"
5
+ require_relative "serializer_with_fallback"
6
+
7
+ module ActiveSupport
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
+ ]
23
+
24
+ ActiveSupport.on_load(:message_pack) do
25
+ ENVELOPE_SERIALIZERS << ActiveSupport::MessagePack
26
+ TIMESTAMP_SERIALIZERS << ActiveSupport::MessagePack
27
+ end
28
+
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)
37
+ else
38
+ data = wrap_in_metadata_envelope({ "data" => data }, **metadata) if has_metadata
39
+ serialize(data)
40
+ end
41
+ end
42
+
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"
56
+ end
57
+ end
58
+ end
59
+
60
+ def use_message_serializer_for_metadata?
61
+ Metadata.use_message_serializer_for_metadata && Metadata::ENVELOPE_SERIALIZERS.include?(serializer)
62
+ end
63
+
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"]
80
+
81
+ if hash["exp"] && Time.now.utc >= parse_expiry(hash["exp"])
82
+ throw :invalid_message_content, "expired"
83
+ end
84
+
85
+ if hash["pur"].to_s != purpose.to_s
86
+ throw :invalid_message_content, "mismatched purpose"
87
+ end
88
+
89
+ hash
90
+ end
91
+
92
+ def metadata_envelope?(object)
93
+ object.is_a?(Hash) && object.key?("_rails")
94
+ end
95
+
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
112
+ end
113
+
114
+ def parse_expiry(expires_at)
115
+ if !expires_at.is_a?(String)
116
+ expires_at
117
+ elsif ActiveSupport.use_standard_json_time_format
118
+ Time.iso8601(expires_at)
119
+ else
120
+ Time.parse(expires_at)
121
+ end
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
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Messages
5
+ class RotationConfiguration # :nodoc:
6
+ attr_reader :signed, :encrypted
7
+
8
+ def initialize
9
+ @signed, @encrypted = [], []
10
+ end
11
+
12
+ def rotate(kind, *args, **options)
13
+ args << options unless options.empty?
14
+ case kind
15
+ when :signed
16
+ @signed << args
17
+ when :encrypted
18
+ @encrypted << args
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,102 @@
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 prepend(**options, &block)
36
+ raise ArgumentError, "Options cannot be specified when using a block" if block && !options.empty?
37
+ changing_configuration!
38
+
39
+ @rotate_options.unshift(block || options)
40
+
41
+ self
42
+ end
43
+
44
+ def rotate_defaults
45
+ rotate()
46
+ end
47
+
48
+ def clear_rotations
49
+ changing_configuration!
50
+ @rotate_options.clear
51
+ self
52
+ end
53
+
54
+ def on_rotation(&callback)
55
+ changing_configuration!
56
+ @on_rotation = callback
57
+ end
58
+
59
+ private
60
+ def changing_configuration!
61
+ if @codecs.any?
62
+ raise <<~MESSAGE
63
+ Cannot change #{self.class} configuration after it has already been applied.
64
+
65
+ The configuration has been applied with the following salts:
66
+ #{@codecs.keys.map { |salt| "- #{salt.inspect}" }.join("\n")}
67
+ MESSAGE
68
+ end
69
+ end
70
+
71
+ def normalize_options(options)
72
+ options = options.dup
73
+
74
+ options[:secret_generator] ||= @secret_generator
75
+
76
+ secret_generator_kwargs = options[:secret_generator].parameters.
77
+ filter_map { |type, name| name if type == :key || type == :keyreq }
78
+ options[:secret_generator_options] = options.extract!(*secret_generator_kwargs)
79
+
80
+ options[:on_rotation] = @on_rotation
81
+
82
+ options
83
+ end
84
+
85
+ def build_with_rotations(salt)
86
+ rotate_options = @rotate_options.map { |options| options.is_a?(Proc) ? options.(salt) : options }
87
+ transitional = self.transitional && rotate_options.first
88
+ rotate_options.compact!
89
+ rotate_options[0..1] = rotate_options[0..1].reverse if transitional
90
+ rotate_options = rotate_options.map { |options| normalize_options(options) }.uniq
91
+
92
+ raise "No options have been configured for #{salt}" if rotate_options.empty?
93
+
94
+ rotate_options.map { |options| build(salt.to_s, **options) }.reduce(&:fall_back_to)
95
+ end
96
+
97
+ def build(salt, secret_generator:, secret_generator_options:, **options)
98
+ raise NotImplementedError
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Messages
5
+ module Rotator # :nodoc:
6
+ def initialize(*args, on_rotation: nil, **options)
7
+ super(*args, **options)
8
+ @args = args
9
+ @options = options
10
+ @rotations = []
11
+ @on_rotation = on_rotation
12
+ end
13
+
14
+ def rotate(*args, **options)
15
+ fall_back_to build_rotation(*args, **options)
16
+ end
17
+
18
+ def on_rotation(&on_rotation)
19
+ @on_rotation = on_rotation
20
+ self
21
+ end
22
+
23
+ def fall_back_to(fallback)
24
+ @rotations << fallback
25
+ self
26
+ end
27
+
28
+ def read_message(message, on_rotation: @on_rotation, **options)
29
+ if @rotations.empty?
30
+ super(message, **options)
31
+ else
32
+ thrown, error = catch_rotation_error do
33
+ return super(message, **options)
34
+ end
35
+
36
+ @rotations.each do |rotation|
37
+ catch_rotation_error do
38
+ value = rotation.read_message(message, **options)
39
+ on_rotation&.call
40
+ return value
41
+ end
42
+ end
43
+
44
+ throw thrown, error
45
+ end
46
+ end
47
+
48
+ def initialize_dup(*)
49
+ super
50
+ @rotations = @rotations.dup
51
+ end
52
+
53
+ private
54
+ def build_rotation(*args, **options)
55
+ self.class.new(*args, *@args.drop(args.length), **@options, **options)
56
+ end
57
+
58
+ def catch_rotation_error(&block)
59
+ error = catch :invalid_message_format do
60
+ error = catch :invalid_message_serialization do
61
+ return [nil, block.call]
62
+ end
63
+ return [:invalid_message_serialization, error]
64
+ end
65
+ [:invalid_message_format, error]
66
+ end
67
+ end
68
+ end
69
+ 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
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/json"
4
+ require "active_support/core_ext/string/access"
5
+ require "active_support/core_ext/string/behavior"
6
+ require "active_support/core_ext/module/delegation"
7
+
8
+ module ActiveSupport # :nodoc:
9
+ module Multibyte # :nodoc:
10
+ # = Active Support \Multibyte \Chars
11
+ #
12
+ # Chars enables you to work transparently with UTF-8 encoding in the Ruby
13
+ # String class without having extensive knowledge about the encoding. A
14
+ # Chars object accepts a string upon initialization and proxies String
15
+ # methods in an encoding safe manner. All the normal String methods are also
16
+ # implemented on the proxy.
17
+ #
18
+ # String methods are proxied through the Chars object, and can be accessed
19
+ # through the +mb_chars+ method. Methods which would normally return a
20
+ # String object now return a Chars object so methods can be chained.
21
+ #
22
+ # 'The Perfect String '.mb_chars.downcase.strip
23
+ # # => #<ActiveSupport::Multibyte::Chars:0x007fdc434ccc10 @wrapped_string="the perfect string">
24
+ #
25
+ # Chars objects are perfectly interchangeable with String objects as long as
26
+ # no explicit class checks are made. If certain methods do explicitly check
27
+ # the class, call +to_s+ before you pass chars objects to them.
28
+ #
29
+ # bad.explicit_checking_method 'T'.mb_chars.downcase.to_s
30
+ #
31
+ # The default Chars implementation assumes that the encoding of the string
32
+ # is UTF-8, if you want to handle different encodings you can write your own
33
+ # multibyte string handler and configure it through
34
+ # ActiveSupport::Multibyte.proxy_class.
35
+ #
36
+ # class CharsForUTF32
37
+ # def size
38
+ # @wrapped_string.size / 4
39
+ # end
40
+ #
41
+ # def self.accepts?(string)
42
+ # string.length % 4 == 0
43
+ # end
44
+ # end
45
+ #
46
+ # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
47
+ class Chars
48
+ include Comparable
49
+ attr_reader :wrapped_string
50
+ alias to_s wrapped_string
51
+ alias to_str wrapped_string
52
+
53
+ delegate :<=>, :=~, :match?, :acts_like_string?, to: :wrapped_string
54
+
55
+ # Creates a new Chars instance by wrapping _string_.
56
+ def initialize(string, deprecation: true)
57
+ if deprecation
58
+ ActiveSupport.deprecator.warn(
59
+ "ActiveSupport::Multibyte::Chars is deprecated and will be removed in Rails 8.2. " \
60
+ "Use normal string methods instead."
61
+ )
62
+ end
63
+
64
+ @wrapped_string = string
65
+ if string.encoding != Encoding::UTF_8
66
+ @wrapped_string = @wrapped_string.dup
67
+ @wrapped_string.force_encoding(Encoding::UTF_8)
68
+ end
69
+ end
70
+
71
+ # Forward all undefined methods to the wrapped string.
72
+ def method_missing(method, ...)
73
+ result = @wrapped_string.__send__(method, ...)
74
+ if method.end_with?("!")
75
+ self if result
76
+ else
77
+ result.kind_of?(String) ? chars(result) : result
78
+ end
79
+ end
80
+
81
+ # Returns +true+ if _obj_ responds to the given method. Private methods
82
+ # are included in the search only if the optional second parameter
83
+ # evaluates to +true+.
84
+ def respond_to_missing?(method, include_private)
85
+ @wrapped_string.respond_to?(method, include_private)
86
+ end
87
+
88
+ # Works just like <tt>String#split</tt>, with the exception that the items
89
+ # in the resulting list are Chars instances instead of String. This makes
90
+ # chaining methods easier.
91
+ #
92
+ # 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
93
+ def split(*args)
94
+ @wrapped_string.split(*args).map { |i| self.class.new(i) }
95
+ end
96
+
97
+ # Works like <tt>String#slice!</tt>, but returns an instance of
98
+ # Chars, or +nil+ if the string was not modified. The string will not be
99
+ # modified if the range given is out of bounds
100
+ #
101
+ # string = 'Welcome'
102
+ # string.mb_chars.slice!(3) # => #<ActiveSupport::Multibyte::Chars:0x000000038109b8 @wrapped_string="c">
103
+ # string # => 'Welome'
104
+ # string.mb_chars.slice!(0..3) # => #<ActiveSupport::Multibyte::Chars:0x00000002eb80a0 @wrapped_string="Welo">
105
+ # string # => 'me'
106
+ def slice!(*args)
107
+ string_sliced = @wrapped_string.slice!(*args)
108
+ if string_sliced
109
+ chars(string_sliced)
110
+ end
111
+ end
112
+
113
+ # Reverses all characters in the string.
114
+ #
115
+ # 'Café'.mb_chars.reverse.to_s # => 'éfaC'
116
+ def reverse
117
+ chars(@wrapped_string.grapheme_clusters.reverse.join)
118
+ end
119
+
120
+ # Limits the byte size of the string to a number of bytes without breaking
121
+ # characters. Usable when the storage for a string is limited for some
122
+ # reason.
123
+ #
124
+ # 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
125
+ def limit(limit)
126
+ chars(@wrapped_string.truncate_bytes(limit, omission: nil))
127
+ end
128
+
129
+ # Capitalizes the first letter of every word, when possible.
130
+ #
131
+ # "ÉL QUE SE ENTERÓ".mb_chars.titleize.to_s # => "Él Que Se Enteró"
132
+ # "日本語".mb_chars.titleize.to_s # => "日本語"
133
+ def titleize
134
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { $1.upcase })
135
+ end
136
+ alias_method :titlecase, :titleize
137
+
138
+ # Performs canonical decomposition on all the characters.
139
+ #
140
+ # 'é'.length # => 1
141
+ # 'é'.mb_chars.decompose.to_s.length # => 2
142
+ def decompose
143
+ chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack("U*"))
144
+ end
145
+
146
+ # Performs composition on all the characters.
147
+ #
148
+ # 'é'.length # => 1
149
+ # 'é'.mb_chars.compose.to_s.length # => 1
150
+ def compose
151
+ chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack("U*"))
152
+ end
153
+
154
+ # Returns the number of grapheme clusters in the string.
155
+ #
156
+ # 'क्षि'.mb_chars.length # => 4
157
+ # 'क्षि'.mb_chars.grapheme_length # => 2
158
+ def grapheme_length
159
+ @wrapped_string.grapheme_clusters.length
160
+ end
161
+
162
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
163
+ # resulting in a valid UTF-8 string.
164
+ #
165
+ # Passing +true+ will forcibly tidy all bytes, assuming that the string's
166
+ # encoding is entirely CP1252 or ISO-8859-1.
167
+ def tidy_bytes(force = false)
168
+ chars(Unicode.tidy_bytes(@wrapped_string, force))
169
+ end
170
+
171
+ def as_json(options = nil) # :nodoc:
172
+ to_s.as_json(options)
173
+ end
174
+
175
+ %w(reverse tidy_bytes).each do |method|
176
+ define_method("#{method}!") do |*args|
177
+ @wrapped_string = public_send(method, *args).to_s
178
+ self
179
+ end
180
+ end
181
+
182
+ private
183
+ def chars(string)
184
+ self.class.new(string)
185
+ end
186
+ end
187
+ end
188
+ end