activesupport 5.1.7 → 6.1.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +434 -490
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -5
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +2 -0
  7. data/lib/active_support/array_inquirer.rb +6 -2
  8. data/lib/active_support/backtrace_cleaner.rb +31 -3
  9. data/lib/active_support/benchmarkable.rb +3 -1
  10. data/lib/active_support/builder.rb +2 -0
  11. data/lib/active_support/cache/file_store.rb +37 -36
  12. data/lib/active_support/cache/mem_cache_store.rb +72 -56
  13. data/lib/active_support/cache/memory_store.rb +61 -33
  14. data/lib/active_support/cache/null_store.rb +10 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +67 -21
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
  18. data/lib/active_support/cache.rb +310 -126
  19. data/lib/active_support/callbacks.rb +106 -100
  20. data/lib/active_support/concern.rb +79 -6
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  22. data/lib/active_support/concurrency/share_lock.rb +2 -1
  23. data/lib/active_support/configurable.rb +12 -14
  24. data/lib/active_support/configuration_file.rb +51 -0
  25. data/lib/active_support/core_ext/array/access.rb +21 -7
  26. data/lib/active_support/core_ext/array/conversions.rb +7 -5
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +2 -0
  30. data/lib/active_support/core_ext/array/inquiry.rb +2 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  32. data/lib/active_support/core_ext/array.rb +3 -1
  33. data/lib/active_support/core_ext/benchmark.rb +4 -2
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +2 -0
  35. data/lib/active_support/core_ext/big_decimal.rb +2 -0
  36. data/lib/active_support/core_ext/class/attribute.rb +50 -47
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -40
  39. data/lib/active_support/core_ext/class.rb +2 -0
  40. data/lib/active_support/core_ext/date/acts_like.rb +2 -0
  41. data/lib/active_support/core_ext/date/blank.rb +2 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +8 -5
  43. data/lib/active_support/core_ext/date/conversions.rb +12 -10
  44. data/lib/active_support/core_ext/date/zones.rb +2 -0
  45. data/lib/active_support/core_ext/date.rb +2 -0
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +61 -37
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -1
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +2 -1
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
  50. data/lib/active_support/core_ext/date_time/blank.rb +2 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
  53. data/lib/active_support/core_ext/date_time/conversions.rb +2 -1
  54. data/lib/active_support/core_ext/date_time.rb +2 -0
  55. data/lib/active_support/core_ext/digest/uuid.rb +4 -1
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +174 -71
  58. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  59. data/lib/active_support/core_ext/file.rb +2 -0
  60. data/lib/active_support/core_ext/hash/conversions.rb +7 -5
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +4 -2
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +2 -0
  65. data/lib/active_support/core_ext/hash/keys.rb +3 -30
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  68. data/lib/active_support/core_ext/hash.rb +3 -2
  69. data/lib/active_support/core_ext/integer/inflections.rb +2 -0
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +7 -14
  72. data/lib/active_support/core_ext/integer.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  74. data/lib/active_support/core_ext/kernel/reporting.rb +2 -0
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +2 -1
  77. data/lib/active_support/core_ext/load_error.rb +3 -8
  78. data/lib/active_support/core_ext/marshal.rb +4 -0
  79. data/lib/active_support/core_ext/module/aliasing.rb +2 -0
  80. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  81. data/lib/active_support/core_ext/module/attr_internal.rb +4 -2
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +44 -56
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +18 -18
  84. data/lib/active_support/core_ext/module/concerning.rb +15 -10
  85. data/lib/active_support/core_ext/module/delegation.rb +103 -58
  86. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  87. data/lib/active_support/core_ext/module/introspection.rb +18 -15
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  90. data/lib/active_support/core_ext/module.rb +3 -1
  91. data/lib/active_support/core_ext/name_error.rb +36 -2
  92. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +131 -129
  94. data/lib/active_support/core_ext/numeric/time.rb +7 -15
  95. data/lib/active_support/core_ext/numeric.rb +2 -1
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +13 -3
  98. data/lib/active_support/core_ext/object/conversions.rb +2 -0
  99. data/lib/active_support/core_ext/object/deep_dup.rb +3 -1
  100. data/lib/active_support/core_ext/object/duplicable.rb +9 -114
  101. data/lib/active_support/core_ext/object/inclusion.rb +2 -0
  102. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  103. data/lib/active_support/core_ext/object/json.rb +22 -2
  104. data/lib/active_support/core_ext/object/to_param.rb +2 -0
  105. data/lib/active_support/core_ext/object/to_query.rb +2 -0
  106. data/lib/active_support/core_ext/object/try.rb +19 -7
  107. data/lib/active_support/core_ext/object/with_options.rb +4 -2
  108. data/lib/active_support/core_ext/object.rb +2 -0
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  111. data/lib/active_support/core_ext/range/each.rb +5 -2
  112. data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
  113. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  114. data/lib/active_support/core_ext/range.rb +4 -1
  115. data/lib/active_support/core_ext/regexp.rb +10 -5
  116. data/lib/active_support/core_ext/securerandom.rb +25 -3
  117. data/lib/active_support/core_ext/string/access.rb +7 -16
  118. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  119. data/lib/active_support/core_ext/string/conversions.rb +3 -0
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +44 -1
  122. data/lib/active_support/core_ext/string/indent.rb +2 -0
  123. data/lib/active_support/core_ext/string/inflections.rb +69 -16
  124. data/lib/active_support/core_ext/string/inquiry.rb +3 -0
  125. data/lib/active_support/core_ext/string/multibyte.rb +9 -4
  126. data/lib/active_support/core_ext/string/output_safety.rb +104 -20
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +5 -1
  129. data/lib/active_support/core_ext/string/zones.rb +2 -0
  130. data/lib/active_support/core_ext/string.rb +2 -0
  131. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  132. data/lib/active_support/core_ext/symbol.rb +3 -0
  133. data/lib/active_support/core_ext/time/acts_like.rb +2 -0
  134. data/lib/active_support/core_ext/time/calculations.rb +76 -18
  135. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +4 -0
  137. data/lib/active_support/core_ext/time/zones.rb +6 -4
  138. data/lib/active_support/core_ext/time.rb +2 -0
  139. data/lib/active_support/core_ext/uri.rb +11 -6
  140. data/lib/active_support/core_ext.rb +3 -1
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +210 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +2 -0
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
  146. data/lib/active_support/dependencies.rb +134 -60
  147. data/lib/active_support/deprecation/behaviors.rb +43 -11
  148. data/lib/active_support/deprecation/constant_accessor.rb +4 -2
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +2 -1
  151. data/lib/active_support/deprecation/method_wrappers.rb +29 -21
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +32 -6
  153. data/lib/active_support/deprecation/reporting.rb +54 -9
  154. data/lib/active_support/deprecation.rb +9 -2
  155. data/lib/active_support/descendants_tracker.rb +61 -9
  156. data/lib/active_support/digest.rb +22 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +6 -6
  158. data/lib/active_support/duration/iso8601_serializer.rb +20 -14
  159. data/lib/active_support/duration.rb +102 -45
  160. data/lib/active_support/encrypted_configuration.rb +45 -0
  161. data/lib/active_support/encrypted_file.rb +117 -0
  162. data/lib/active_support/environment_inquirer.rb +20 -0
  163. data/lib/active_support/evented_file_update_checker.rb +84 -117
  164. data/lib/active_support/execution_wrapper.rb +19 -13
  165. data/lib/active_support/executor.rb +2 -0
  166. data/lib/active_support/file_update_checker.rb +2 -1
  167. data/lib/active_support/fork_tracker.rb +64 -0
  168. data/lib/active_support/gem_version.rb +3 -1
  169. data/lib/active_support/gzip.rb +2 -0
  170. data/lib/active_support/hash_with_indifferent_access.rb +123 -41
  171. data/lib/active_support/i18n.rb +4 -1
  172. data/lib/active_support/i18n_railtie.rb +19 -14
  173. data/lib/active_support/inflections.rb +2 -0
  174. data/lib/active_support/inflector/inflections.rb +19 -8
  175. data/lib/active_support/inflector/methods.rb +87 -77
  176. data/lib/active_support/inflector/transliterate.rb +56 -18
  177. data/lib/active_support/inflector.rb +2 -0
  178. data/lib/active_support/json/decoding.rb +27 -26
  179. data/lib/active_support/json/encoding.rb +13 -3
  180. data/lib/active_support/json.rb +2 -0
  181. data/lib/active_support/key_generator.rb +3 -33
  182. data/lib/active_support/lazy_load_hooks.rb +7 -2
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +7 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +2 -0
  186. data/lib/active_support/log_subscriber.rb +42 -11
  187. data/lib/active_support/logger.rb +4 -17
  188. data/lib/active_support/logger_silence.rb +13 -20
  189. data/lib/active_support/logger_thread_safe_level.rb +54 -7
  190. data/lib/active_support/message_encryptor.rb +100 -32
  191. data/lib/active_support/message_verifier.rb +85 -14
  192. data/lib/active_support/messages/metadata.rb +80 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  194. data/lib/active_support/messages/rotator.rb +57 -0
  195. data/lib/active_support/multibyte/chars.rb +12 -68
  196. data/lib/active_support/multibyte/unicode.rb +17 -327
  197. data/lib/active_support/multibyte.rb +2 -0
  198. data/lib/active_support/notifications/fanout.rb +118 -16
  199. data/lib/active_support/notifications/instrumenter.rb +73 -9
  200. data/lib/active_support/notifications.rb +74 -8
  201. data/lib/active_support/number_helper/number_converter.rb +7 -6
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -2
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -3
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -3
  206. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  207. data/lib/active_support/number_helper/number_to_phone_converter.rb +5 -2
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +14 -27
  209. data/lib/active_support/number_helper/rounding_helper.rb +16 -30
  210. data/lib/active_support/number_helper.rb +40 -12
  211. data/lib/active_support/option_merger.rb +24 -3
  212. data/lib/active_support/ordered_hash.rb +3 -1
  213. data/lib/active_support/ordered_options.rb +17 -5
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +4 -1
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +3 -10
  218. data/lib/active_support/railtie.rb +60 -9
  219. data/lib/active_support/reloader.rb +12 -11
  220. data/lib/active_support/rescuable.rb +7 -6
  221. data/lib/active_support/secure_compare_rotator.rb +51 -0
  222. data/lib/active_support/security_utils.rb +26 -15
  223. data/lib/active_support/string_inquirer.rb +6 -3
  224. data/lib/active_support/subscriber.rb +74 -24
  225. data/lib/active_support/tagged_logging.rb +44 -8
  226. data/lib/active_support/test_case.rb +94 -2
  227. data/lib/active_support/testing/assertions.rb +58 -20
  228. data/lib/active_support/testing/autorun.rb +2 -0
  229. data/lib/active_support/testing/constant_lookup.rb +2 -0
  230. data/lib/active_support/testing/declarative.rb +2 -0
  231. data/lib/active_support/testing/deprecation.rb +2 -1
  232. data/lib/active_support/testing/file_fixtures.rb +4 -0
  233. data/lib/active_support/testing/isolation.rb +4 -2
  234. data/lib/active_support/testing/method_call_assertions.rb +30 -1
  235. data/lib/active_support/testing/parallelization/server.rb +78 -0
  236. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  237. data/lib/active_support/testing/parallelization.rb +51 -0
  238. data/lib/active_support/testing/setup_and_teardown.rb +12 -7
  239. data/lib/active_support/testing/stream.rb +3 -2
  240. data/lib/active_support/testing/tagged_logging.rb +2 -0
  241. data/lib/active_support/testing/time_helpers.rb +78 -13
  242. data/lib/active_support/time.rb +2 -0
  243. data/lib/active_support/time_with_zone.rb +113 -41
  244. data/lib/active_support/values/time_zone.rb +54 -25
  245. data/lib/active_support/version.rb +2 -0
  246. data/lib/active_support/xml_mini/jdom.rb +5 -4
  247. data/lib/active_support/xml_mini/libxml.rb +4 -2
  248. data/lib/active_support/xml_mini/libxmlsax.rb +6 -4
  249. data/lib/active_support/xml_mini/nokogiri.rb +4 -2
  250. data/lib/active_support/xml_mini/nokogirisax.rb +5 -3
  251. data/lib/active_support/xml_mini/rexml.rb +12 -3
  252. data/lib/active_support/xml_mini.rb +5 -11
  253. data/lib/active_support.rb +18 -13
  254. metadata +71 -32
  255. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  256. data/lib/active_support/core_ext/hash/compact.rb +0 -27
  257. data/lib/active_support/core_ext/hash/transform_values.rb +0 -30
  258. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  259. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  260. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  261. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  262. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -44,10 +44,12 @@ en:
44
44
  delimiter: ","
45
45
  # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
46
46
  precision: 3
47
+ # Determine how rounding is performed (see BigDecimal::mode)
48
+ round_mode: default
47
49
  # If set to true, precision will mean the number of significant digits instead
48
50
  # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
49
51
  significant: false
50
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
52
+ # If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2)
51
53
  strip_insignificant_zeros: false
52
54
 
53
55
  # Used in NumberHelper.number_to_currency()
@@ -56,10 +58,11 @@ en:
56
58
  # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
57
59
  format: "%u%n"
58
60
  unit: "$"
59
- # These five are to override number.format and are optional
61
+ # These six are to override number.format and are optional
60
62
  separator: "."
61
63
  delimiter: ","
62
64
  precision: 2
65
+ # round_mode:
63
66
  significant: false
64
67
  strip_insignificant_zeros: false
65
68
 
@@ -87,10 +90,11 @@ en:
87
90
  # Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
88
91
  human:
89
92
  format:
90
- # These five are to override number.format and are optional
93
+ # These six are to override number.format and are optional
91
94
  # separator:
92
95
  delimiter: ""
93
96
  precision: 3
97
+ # round_mode:
94
98
  significant: true
95
99
  strip_insignificant_zeros: true
96
100
  # Used in number_to_human_size()
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/log_subscriber"
2
4
  require "active_support/logger"
3
5
  require "active_support/notifications"
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/module/attribute_accessors"
2
4
  require "active_support/core_ext/class/attribute"
3
5
  require "active_support/subscriber"
4
6
 
5
7
  module ActiveSupport
6
- # ActiveSupport::LogSubscriber is an object set to consume
7
- # ActiveSupport::Notifications with the sole purpose of logging them.
8
+ # <tt>ActiveSupport::LogSubscriber</tt> is an object set to consume
9
+ # <tt>ActiveSupport::Notifications</tt> with the sole purpose of logging them.
8
10
  # The log subscriber dispatches notifications to a registered object based
9
11
  # on its given namespace.
10
12
  #
@@ -14,7 +16,7 @@ module ActiveSupport
14
16
  # module ActiveRecord
15
17
  # class LogSubscriber < ActiveSupport::LogSubscriber
16
18
  # def sql(event)
17
- # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
19
+ # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
18
20
  # end
19
21
  # end
20
22
  # end
@@ -27,13 +29,39 @@ module ActiveSupport
27
29
  # subscriber, the line above should be called after your
28
30
  # <tt>ActiveRecord::LogSubscriber</tt> definition.
29
31
  #
30
- # After configured, whenever a "sql.active_record" notification is published,
31
- # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
32
- # the sql method.
32
+ # A logger also needs to be set with <tt>ActiveRecord::LogSubscriber.logger=</tt>.
33
+ # This is assigned automatically in a Rails environment.
34
+ #
35
+ # After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
36
+ # it will properly dispatch the event
37
+ # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
38
+ #
39
+ # Being an <tt>ActiveSupport::Notifications</tt> consumer,
40
+ # <tt>ActiveSupport::LogSubscriber</tt> exposes a simple interface to check if
41
+ # instrumented code raises an exception. It is common to log a different
42
+ # message in case of an error, and this can be achieved by extending
43
+ # the previous example:
44
+ #
45
+ # module ActiveRecord
46
+ # class LogSubscriber < ActiveSupport::LogSubscriber
47
+ # def sql(event)
48
+ # exception = event.payload[:exception]
49
+ #
50
+ # if exception
51
+ # exception_object = event.payload[:exception_object]
52
+ #
53
+ # error "[ERROR] #{event.payload[:name]}: #{exception.join(', ')} " \
54
+ # "(#{exception_object.backtrace.first})"
55
+ # else
56
+ # # standard logger code
57
+ # end
58
+ # end
59
+ # end
60
+ # end
33
61
  #
34
62
  # Log subscriber also has some helpers to deal with logging and automatically
35
- # flushes all logs when the request finishes (via action_dispatch.callback
36
- # notification) in a Rails environment.
63
+ # flushes all logs when the request finishes
64
+ # (via <tt>action_dispatch.callback</tt> notification) in a Rails environment.
37
65
  class LogSubscriber < Subscriber
38
66
  # Embed in a String to clear all previous ANSI sequences.
39
67
  CLEAR = "\e[0m"
@@ -49,8 +77,7 @@ module ActiveSupport
49
77
  CYAN = "\e[36m"
50
78
  WHITE = "\e[37m"
51
79
 
52
- mattr_accessor :colorize_logging
53
- self.colorize_logging = true
80
+ mattr_accessor :colorize_logging, default: true
54
81
 
55
82
  class << self
56
83
  def logger
@@ -69,6 +96,11 @@ module ActiveSupport
69
96
  def flush_all!
70
97
  logger.flush if logger.respond_to?(:flush)
71
98
  end
99
+
100
+ private
101
+ def fetch_public_methods(subscriber, inherit_all)
102
+ subscriber.public_methods(inherit_all) - LogSubscriber.public_instance_methods(true)
103
+ end
72
104
  end
73
105
 
74
106
  def logger
@@ -88,7 +120,6 @@ module ActiveSupport
88
120
  end
89
121
 
90
122
  private
91
-
92
123
  %w(info debug warn error fatal unknown).each do |level|
93
124
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
94
125
  def #{level}(progname = nil, &block)
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/logger_silence"
2
4
  require "active_support/logger_thread_safe_level"
3
5
  require "logger"
4
6
 
5
7
  module ActiveSupport
6
8
  class Logger < ::Logger
7
- include ActiveSupport::LoggerThreadSafeLevel
8
9
  include LoggerSilence
9
10
 
10
11
  # Returns true if the logger destination matches one of the sources
@@ -13,7 +14,7 @@ module ActiveSupport
13
14
  # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
14
15
  # # => true
15
16
  def self.logger_outputs_to?(logger, *sources)
16
- logdev = logger.instance_variable_get("@logdev")
17
+ logdev = logger.instance_variable_get(:@logdev)
17
18
  logger_source = logdev.dev if logdev.respond_to?(:dev)
18
19
  sources.any? { |source| source == logger_source }
19
20
  end
@@ -76,23 +77,9 @@ module ActiveSupport
76
77
  end
77
78
  end
78
79
 
79
- def initialize(*args)
80
+ def initialize(*args, **kwargs)
80
81
  super
81
82
  @formatter = SimpleFormatter.new
82
- after_initialize if respond_to? :after_initialize
83
- end
84
-
85
- def add(severity, message = nil, progname = nil, &block)
86
- return true if @logdev.nil? || (severity || UNKNOWN) < level
87
- super
88
- end
89
-
90
- Logger::Severity.constants.each do |severity|
91
- class_eval(<<-EOT, __FILE__, __LINE__ + 1)
92
- def #{severity.downcase}? # def debug?
93
- Logger::#{severity} >= level # DEBUG >= level
94
- end # end
95
- EOT
96
83
  end
97
84
 
98
85
  # Simple formatter which only displays the message.
@@ -1,28 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/concern"
2
4
  require "active_support/core_ext/module/attribute_accessors"
3
- require "concurrent"
4
-
5
- module LoggerSilence
6
- extend ActiveSupport::Concern
5
+ require "active_support/logger_thread_safe_level"
7
6
 
8
- included do
9
- cattr_accessor :silencer
10
- self.silencer = true
11
- end
7
+ module ActiveSupport
8
+ module LoggerSilence
9
+ extend ActiveSupport::Concern
12
10
 
13
- # Silences the logger for the duration of the block.
14
- def silence(temporary_level = Logger::ERROR)
15
- if silencer
16
- begin
17
- old_local_level = local_level
18
- self.local_level = temporary_level
11
+ included do
12
+ cattr_accessor :silencer, default: true
13
+ include ActiveSupport::LoggerThreadSafeLevel
14
+ end
19
15
 
20
- yield self
21
- ensure
22
- self.local_level = old_local_level
23
- end
24
- else
25
- yield self
16
+ # Silences the logger for the duration of the block.
17
+ def silence(severity = Logger::ERROR)
18
+ silencer ? log_at(severity) { yield self } : yield(self)
26
19
  end
27
20
  end
28
21
  end
@@ -1,31 +1,78 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/concern"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "concurrent"
6
+ require "fiber"
2
7
 
3
8
  module ActiveSupport
4
9
  module LoggerThreadSafeLevel # :nodoc:
5
10
  extend ActiveSupport::Concern
6
11
 
7
- def after_initialize
8
- @local_levels = Concurrent::Map.new(initial_capacity: 2)
12
+ included do
13
+ cattr_accessor :local_levels, default: Concurrent::Map.new(initial_capacity: 2), instance_accessor: false
14
+ end
15
+
16
+ Logger::Severity.constants.each do |severity|
17
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
18
+ def #{severity.downcase}? # def debug?
19
+ Logger::#{severity} >= level # DEBUG >= level
20
+ end # end
21
+ EOT
9
22
  end
10
23
 
11
24
  def local_log_id
12
- Thread.current.__id__
25
+ Fiber.current.__id__
13
26
  end
14
27
 
15
28
  def local_level
16
- @local_levels[local_log_id]
29
+ self.class.local_levels[local_log_id]
17
30
  end
18
31
 
19
32
  def local_level=(level)
20
- if level
21
- @local_levels[local_log_id] = level
33
+ case level
34
+ when Integer
35
+ self.class.local_levels[local_log_id] = level
36
+ when Symbol
37
+ self.class.local_levels[local_log_id] = Logger::Severity.const_get(level.to_s.upcase)
38
+ when nil
39
+ self.class.local_levels.delete(local_log_id)
22
40
  else
23
- @local_levels.delete(local_log_id)
41
+ raise ArgumentError, "Invalid log level: #{level.inspect}"
24
42
  end
25
43
  end
26
44
 
27
45
  def level
28
46
  local_level || super
29
47
  end
48
+
49
+ # Change the thread-local level for the duration of the given block.
50
+ def log_at(level)
51
+ old_local_level, self.local_level = local_level, level
52
+ yield
53
+ ensure
54
+ self.local_level = old_local_level
55
+ end
56
+
57
+ # Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
58
+ # FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
59
+ def add(severity, message = nil, progname = nil, &block) #:nodoc:
60
+ severity ||= UNKNOWN
61
+ progname ||= @progname
62
+
63
+ return true if @logdev.nil? || severity < level
64
+
65
+ if message.nil?
66
+ if block_given?
67
+ message = yield
68
+ else
69
+ message = progname
70
+ progname = @progname
71
+ end
72
+ end
73
+
74
+ @logdev.write \
75
+ format_message(format_severity(severity), Time.now, progname, message)
76
+ end
30
77
  end
31
78
  end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "openssl"
2
4
  require "base64"
3
- require "active_support/core_ext/array/extract_options"
5
+ require "active_support/core_ext/module/attribute_accessors"
4
6
  require "active_support/message_verifier"
7
+ require "active_support/messages/metadata"
5
8
 
6
9
  module ActiveSupport
7
10
  # MessageEncryptor is a simple way to encrypt values which get stored
@@ -13,13 +16,82 @@ module ActiveSupport
13
16
  # This can be used in situations similar to the <tt>MessageVerifier</tt>, but
14
17
  # where you don't want users to be able to determine the value of the payload.
15
18
  #
16
- # salt = SecureRandom.random_bytes(64)
17
- # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, 32) # => "\x89\xE0\x156\xAC..."
18
- # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
19
- # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
20
- # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
19
+ # len = ActiveSupport::MessageEncryptor.key_len
20
+ # salt = SecureRandom.random_bytes(len)
21
+ # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, len) # => "\x89\xE0\x156\xAC..."
22
+ # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
23
+ # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
24
+ # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
25
+ #
26
+ # === Confining messages to a specific purpose
27
+ #
28
+ # By default any message can be used throughout your app. But they can also be
29
+ # confined to a specific +:purpose+.
30
+ #
31
+ # token = crypt.encrypt_and_sign("this is the chair", purpose: :login)
32
+ #
33
+ # Then that same purpose must be passed when verifying to get the data back out:
34
+ #
35
+ # crypt.decrypt_and_verify(token, purpose: :login) # => "this is the chair"
36
+ # crypt.decrypt_and_verify(token, purpose: :shipping) # => nil
37
+ # crypt.decrypt_and_verify(token) # => nil
38
+ #
39
+ # Likewise, if a message has no purpose it won't be returned when verifying with
40
+ # a specific purpose.
41
+ #
42
+ # token = crypt.encrypt_and_sign("the conversation is lively")
43
+ # crypt.decrypt_and_verify(token, purpose: :scare_tactics) # => nil
44
+ # crypt.decrypt_and_verify(token) # => "the conversation is lively"
45
+ #
46
+ # === Making messages expire
47
+ #
48
+ # By default messages last forever and verifying one year from now will still
49
+ # return the original value. But messages can be set to expire at a given
50
+ # time with +:expires_in+ or +:expires_at+.
51
+ #
52
+ # crypt.encrypt_and_sign(parcel, expires_in: 1.month)
53
+ # crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
54
+ #
55
+ # Then the messages can be verified and returned up to the expire time.
56
+ # Thereafter, verifying returns +nil+.
57
+ #
58
+ # === Rotating keys
59
+ #
60
+ # MessageEncryptor also supports rotating out old configurations by falling
61
+ # back to a stack of encryptors. Call +rotate+ to build and add an encryptor
62
+ # so +decrypt_and_verify+ will also try the fallback.
63
+ #
64
+ # By default any rotated encryptors use the values of the primary
65
+ # encryptor unless specified otherwise.
66
+ #
67
+ # You'd give your encryptor the new defaults:
68
+ #
69
+ # crypt = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
70
+ #
71
+ # Then gradually rotate the old values out by adding them as fallbacks. Any message
72
+ # generated with the old values will then work until the rotation is removed.
73
+ #
74
+ # crypt.rotate old_secret # Fallback to an old secret instead of @secret.
75
+ # crypt.rotate cipher: "aes-256-cbc" # Fallback to an old cipher instead of aes-256-gcm.
76
+ #
77
+ # Though if both the secret and the cipher was changed at the same time,
78
+ # the above should be combined into:
79
+ #
80
+ # crypt.rotate old_secret, cipher: "aes-256-cbc"
21
81
  class MessageEncryptor
22
- DEFAULT_CIPHER = "aes-256-cbc"
82
+ prepend Messages::Rotator::Encryptor
83
+
84
+ cattr_accessor :use_authenticated_message_encryption, instance_accessor: false, default: false
85
+
86
+ class << self
87
+ def default_cipher #:nodoc:
88
+ if use_authenticated_message_encryption
89
+ "aes-256-gcm"
90
+ else
91
+ "aes-256-cbc"
92
+ end
93
+ end
94
+ end
23
95
 
24
96
  module NullSerializer #:nodoc:
25
97
  def self.load(value)
@@ -45,7 +117,7 @@ module ActiveSupport
45
117
  OpenSSLCipherError = OpenSSL::Cipher::CipherError
46
118
 
47
119
  # Initialize a new MessageEncryptor. +secret+ must be at least as long as
48
- # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256
120
+ # the cipher key size. For the default 'aes-256-gcm' cipher, this is 256
49
121
  # bits. If you are using a user-entered secret, you can generate a suitable
50
122
  # key by using <tt>ActiveSupport::KeyGenerator</tt> or a similar key
51
123
  # derivation function.
@@ -57,41 +129,38 @@ module ActiveSupport
57
129
  #
58
130
  # Options:
59
131
  # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
60
- # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
132
+ # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-gcm'.
61
133
  # * <tt>:digest</tt> - String of digest to use for signing. Default is
62
134
  # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'.
63
135
  # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
64
- def initialize(secret, *signature_key_or_options)
65
- options = signature_key_or_options.extract_options!
66
- sign_secret = signature_key_or_options.first
136
+ def initialize(secret, sign_secret = nil, cipher: nil, digest: nil, serializer: nil)
67
137
  @secret = secret
68
138
  @sign_secret = sign_secret
69
- @cipher = options[:cipher] || DEFAULT_CIPHER
70
- @digest = options[:digest] || "SHA1" unless aead_mode?
139
+ @cipher = cipher || self.class.default_cipher
140
+ @digest = digest || "SHA1" unless aead_mode?
71
141
  @verifier = resolve_verifier
72
- @serializer = options[:serializer] || Marshal
142
+ @serializer = serializer || Marshal
73
143
  end
74
144
 
75
145
  # Encrypt and sign a message. We need to sign the message in order to avoid
76
- # padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
77
- def encrypt_and_sign(value)
78
- verifier.generate(_encrypt(value))
146
+ # padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
147
+ def encrypt_and_sign(value, expires_at: nil, expires_in: nil, purpose: nil)
148
+ verifier.generate(_encrypt(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose))
79
149
  end
80
150
 
81
151
  # Decrypt and verify a message. We need to verify the message in order to
82
- # avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
83
- def decrypt_and_verify(value)
84
- _decrypt(verifier.verify(value))
152
+ # avoid padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
153
+ def decrypt_and_verify(data, purpose: nil, **)
154
+ _decrypt(verifier.verify(data), purpose)
85
155
  end
86
156
 
87
157
  # Given a cipher, returns the key length of the cipher to help generate the key of desired size
88
- def self.key_len(cipher = DEFAULT_CIPHER)
158
+ def self.key_len(cipher = default_cipher)
89
159
  OpenSSL::Cipher.new(cipher).key_len
90
160
  end
91
161
 
92
162
  private
93
-
94
- def _encrypt(value)
163
+ def _encrypt(value, **metadata_options)
95
164
  cipher = new_cipher
96
165
  cipher.encrypt
97
166
  cipher.key = @secret
@@ -100,17 +169,17 @@ module ActiveSupport
100
169
  iv = cipher.random_iv
101
170
  cipher.auth_data = "" if aead_mode?
102
171
 
103
- encrypted_data = cipher.update(@serializer.dump(value))
172
+ encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), **metadata_options))
104
173
  encrypted_data << cipher.final
105
174
 
106
175
  blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
107
- blob << "--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
176
+ blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
108
177
  blob
109
178
  end
110
179
 
111
- def _decrypt(encrypted_message)
180
+ def _decrypt(encrypted_message, purpose)
112
181
  cipher = new_cipher
113
- encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }
182
+ encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) }
114
183
 
115
184
  # Currently the OpenSSL bindings do not raise an error if auth_tag is
116
185
  # truncated, which would allow an attacker to easily forge it. See
@@ -128,7 +197,8 @@ module ActiveSupport
128
197
  decrypted_data = cipher.update(encrypted_data)
129
198
  decrypted_data << cipher.final
130
199
 
131
- @serializer.load(decrypted_data)
200
+ message = Messages::Metadata.verify(decrypted_data, purpose)
201
+ @serializer.load(message) if message
132
202
  rescue OpenSSLCipherError, TypeError, ArgumentError
133
203
  raise InvalidMessage
134
204
  end
@@ -137,9 +207,7 @@ module ActiveSupport
137
207
  OpenSSL::Cipher.new(@cipher)
138
208
  end
139
209
 
140
- def verifier
141
- @verifier
142
- end
210
+ attr_reader :verifier
143
211
 
144
212
  def aead_mode?
145
213
  @aead_mode ||= new_cipher.authenticated?
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "base64"
2
4
  require "active_support/core_ext/object/blank"
3
5
  require "active_support/security_utils"
6
+ require "active_support/messages/metadata"
7
+ require "active_support/messages/rotator"
4
8
 
5
9
  module ActiveSupport
6
10
  # +MessageVerifier+ makes it easy to generate and verify messages which are
@@ -27,17 +31,83 @@ module ActiveSupport
27
31
  #
28
32
  # +MessageVerifier+ creates HMAC signatures using SHA1 hash algorithm by default.
29
33
  # If you want to use a different hash algorithm, you can change it by providing
30
- # `:digest` key as an option while initializing the verifier:
34
+ # +:digest+ key as an option while initializing the verifier:
31
35
  #
32
36
  # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', digest: 'SHA256')
37
+ #
38
+ # === Confining messages to a specific purpose
39
+ #
40
+ # By default any message can be used throughout your app. But they can also be
41
+ # confined to a specific +:purpose+.
42
+ #
43
+ # token = @verifier.generate("this is the chair", purpose: :login)
44
+ #
45
+ # Then that same purpose must be passed when verifying to get the data back out:
46
+ #
47
+ # @verifier.verified(token, purpose: :login) # => "this is the chair"
48
+ # @verifier.verified(token, purpose: :shipping) # => nil
49
+ # @verifier.verified(token) # => nil
50
+ #
51
+ # @verifier.verify(token, purpose: :login) # => "this is the chair"
52
+ # @verifier.verify(token, purpose: :shipping) # => ActiveSupport::MessageVerifier::InvalidSignature
53
+ # @verifier.verify(token) # => ActiveSupport::MessageVerifier::InvalidSignature
54
+ #
55
+ # Likewise, if a message has no purpose it won't be returned when verifying with
56
+ # a specific purpose.
57
+ #
58
+ # token = @verifier.generate("the conversation is lively")
59
+ # @verifier.verified(token, purpose: :scare_tactics) # => nil
60
+ # @verifier.verified(token) # => "the conversation is lively"
61
+ #
62
+ # @verifier.verify(token, purpose: :scare_tactics) # => ActiveSupport::MessageVerifier::InvalidSignature
63
+ # @verifier.verify(token) # => "the conversation is lively"
64
+ #
65
+ # === Making messages expire
66
+ #
67
+ # By default messages last forever and verifying one year from now will still
68
+ # return the original value. But messages can be set to expire at a given
69
+ # time with +:expires_in+ or +:expires_at+.
70
+ #
71
+ # @verifier.generate(parcel, expires_in: 1.month)
72
+ # @verifier.generate(doowad, expires_at: Time.now.end_of_year)
73
+ #
74
+ # Then the messages can be verified and returned up to the expire time.
75
+ # Thereafter, the +verified+ method returns +nil+ while +verify+ raises
76
+ # <tt>ActiveSupport::MessageVerifier::InvalidSignature</tt>.
77
+ #
78
+ # === Rotating keys
79
+ #
80
+ # MessageVerifier also supports rotating out old configurations by falling
81
+ # back to a stack of verifiers. Call +rotate+ to build and add a verifier to
82
+ # so either +verified+ or +verify+ will also try verifying with the fallback.
83
+ #
84
+ # By default any rotated verifiers use the values of the primary
85
+ # verifier unless specified otherwise.
86
+ #
87
+ # You'd give your verifier the new defaults:
88
+ #
89
+ # verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512", serializer: JSON)
90
+ #
91
+ # Then gradually rotate the old values out by adding them as fallbacks. Any message
92
+ # generated with the old values will then work until the rotation is removed.
93
+ #
94
+ # verifier.rotate old_secret # Fallback to an old secret instead of @secret.
95
+ # verifier.rotate digest: "SHA256" # Fallback to an old digest instead of SHA512.
96
+ # verifier.rotate serializer: Marshal # Fallback to an old serializer instead of JSON.
97
+ #
98
+ # Though the above would most likely be combined into one rotation:
99
+ #
100
+ # verifier.rotate old_secret, digest: "SHA256", serializer: Marshal
33
101
  class MessageVerifier
102
+ prepend Messages::Rotator::Verifier
103
+
34
104
  class InvalidSignature < StandardError; end
35
105
 
36
- def initialize(secret, options = {})
106
+ def initialize(secret, digest: nil, serializer: nil)
37
107
  raise ArgumentError, "Secret should not be nil." unless secret
38
108
  @secret = secret
39
- @digest = options[:digest] || "SHA1"
40
- @serializer = options[:serializer] || Marshal
109
+ @digest = digest || "SHA1"
110
+ @serializer = serializer || Marshal
41
111
  end
42
112
 
43
113
  # Checks if a signed message could have been generated by signing an object
@@ -52,7 +122,7 @@ module ActiveSupport
52
122
  def valid_message?(signed_message)
53
123
  return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
54
124
 
55
- data, digest = signed_message.split("--".freeze)
125
+ data, digest = signed_message.split("--")
56
126
  data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
57
127
  end
58
128
 
@@ -77,11 +147,12 @@ module ActiveSupport
77
147
  #
78
148
  # incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
79
149
  # verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format
80
- def verified(signed_message)
150
+ def verified(signed_message, purpose: nil, **)
81
151
  if valid_message?(signed_message)
82
152
  begin
83
- data = signed_message.split("--".freeze)[0]
84
- @serializer.load(decode(data))
153
+ data = signed_message.split("--")[0]
154
+ message = Messages::Metadata.verify(decode(data), purpose)
155
+ @serializer.load(message) if message
85
156
  rescue ArgumentError => argument_error
86
157
  return if argument_error.message.include?("invalid base64")
87
158
  raise
@@ -101,19 +172,19 @@ module ActiveSupport
101
172
  #
102
173
  # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
103
174
  # other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature
104
- def verify(signed_message)
105
- verified(signed_message) || raise(InvalidSignature)
175
+ def verify(*args, **options)
176
+ verified(*args, **options) || raise(InvalidSignature)
106
177
  end
107
178
 
108
179
  # Generates a signed message for the provided value.
109
180
  #
110
- # The message is signed with the +MessageVerifier+'s secret. Without knowing
111
- # the secret, the original value cannot be extracted from the message.
181
+ # The message is signed with the +MessageVerifier+'s secret.
182
+ # Returns Base64-encoded message joined with the generated signature.
112
183
  #
113
184
  # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
114
185
  # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
115
- def generate(value)
116
- data = encode(@serializer.dump(value))
186
+ def generate(value, expires_at: nil, expires_in: nil, purpose: nil)
187
+ data = encode(Messages::Metadata.wrap(@serializer.dump(value), expires_at: expires_at, expires_in: expires_in, purpose: purpose))
117
188
  "#{data}--#{generate_digest(data)}"
118
189
  end
119
190