activesupport 4.2.11.1 → 6.1.7.3

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 (272) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +464 -391
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -7
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +34 -6
  9. data/lib/active_support/benchmarkable.rb +6 -4
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +61 -55
  12. data/lib/active_support/cache/mem_cache_store.rb +115 -100
  13. data/lib/active_support/cache/memory_store.rb +81 -58
  14. data/lib/active_support/cache/null_store.rb +11 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +90 -42
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +386 -225
  19. data/lib/active_support/callbacks.rb +661 -594
  20. data/lib/active_support/concern.rb +80 -7
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +226 -0
  23. data/lib/active_support/configurable.rb +16 -17
  24. data/lib/active_support/configuration_file.rb +51 -0
  25. data/lib/active_support/core_ext/array/access.rb +41 -1
  26. data/lib/active_support/core_ext/array/conversions.rb +24 -20
  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 +11 -18
  30. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  32. data/lib/active_support/core_ext/array.rb +9 -6
  33. data/lib/active_support/core_ext/benchmark.rb +5 -3
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +52 -48
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -25
  39. data/lib/active_support/core_ext/class.rb +4 -3
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +14 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +17 -14
  43. data/lib/active_support/core_ext/date/conversions.rb +27 -24
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +6 -4
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +167 -65
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +19 -3
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -13
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  50. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +37 -19
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
  53. data/lib/active_support/core_ext/date_time/conversions.rb +16 -13
  54. data/lib/active_support/core_ext/date_time.rb +7 -5
  55. data/lib/active_support/core_ext/digest/uuid.rb +8 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +186 -22
  58. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +13 -10
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  65. data/lib/active_support/core_ext/hash/keys.rb +20 -43
  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 +10 -9
  69. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +11 -18
  72. data/lib/active_support/core_ext/integer.rb +5 -3
  73. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -5
  77. data/lib/active_support/core_ext/load_error.rb +3 -22
  78. data/lib/active_support/core_ext/marshal.rb +10 -8
  79. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  80. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  81. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +63 -69
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +148 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +19 -14
  85. data/lib/active_support/core_ext/module/delegation.rb +164 -51
  86. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -22
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  90. data/lib/active_support/core_ext/module.rb +13 -11
  91. data/lib/active_support/core_ext/name_error.rb +51 -4
  92. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +133 -136
  94. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  95. data/lib/active_support/core_ext/numeric.rb +5 -3
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +27 -3
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -93
  101. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  102. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  103. data/lib/active_support/core_ext/object/json.rb +63 -21
  104. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  105. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  106. data/lib/active_support/core_ext/object/try.rb +81 -23
  107. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  108. data/lib/active_support/core_ext/object.rb +14 -13
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  111. data/lib/active_support/core_ext/range/each.rb +18 -17
  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 +7 -4
  115. data/lib/active_support/core_ext/regexp.rb +10 -1
  116. data/lib/active_support/core_ext/securerandom.rb +45 -0
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  119. data/lib/active_support/core_ext/string/conversions.rb +8 -4
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +48 -6
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +102 -26
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  126. data/lib/active_support/core_ext/string/output_safety.rb +125 -40
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +6 -5
  129. data/lib/active_support/core_ext/string/zones.rb +4 -2
  130. data/lib/active_support/core_ext/string.rb +15 -13
  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 +3 -1
  134. data/lib/active_support/core_ext/time/calculations.rb +137 -53
  135. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +22 -13
  137. data/lib/active_support/core_ext/time/zones.rb +41 -7
  138. data/lib/active_support/core_ext/time.rb +7 -6
  139. data/lib/active_support/core_ext/uri.rb +11 -8
  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 +57 -0
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
  146. data/lib/active_support/dependencies.rb +241 -175
  147. data/lib/active_support/deprecation/behaviors.rb +58 -12
  148. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  151. data/lib/active_support/deprecation/method_wrappers.rb +62 -21
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
  153. data/lib/active_support/deprecation/reporting.rb +81 -18
  154. data/lib/active_support/deprecation.rb +17 -9
  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 +123 -0
  158. data/lib/active_support/duration/iso8601_serializer.rb +59 -0
  159. data/lib/active_support/duration.rb +364 -39
  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 +170 -0
  164. data/lib/active_support/execution_wrapper.rb +132 -0
  165. data/lib/active_support/executor.rb +8 -0
  166. data/lib/active_support/file_update_checker.rb +62 -37
  167. data/lib/active_support/fork_tracker.rb +64 -0
  168. data/lib/active_support/gem_version.rb +7 -5
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +171 -48
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +47 -16
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +58 -14
  175. data/lib/active_support/inflector/methods.rb +186 -169
  176. data/lib/active_support/inflector/transliterate.rb +83 -33
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +32 -30
  179. data/lib/active_support/json/encoding.rb +22 -61
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +11 -43
  182. data/lib/active_support/lazy_load_hooks.rb +53 -20
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +9 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  186. data/lib/active_support/log_subscriber.rb +52 -19
  187. data/lib/active_support/logger.rb +10 -24
  188. data/lib/active_support/logger_silence.rb +14 -20
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -10
  190. data/lib/active_support/message_encryptor.rb +167 -57
  191. data/lib/active_support/message_verifier.rb +151 -18
  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 +35 -80
  196. data/lib/active_support/multibyte/unicode.rb +22 -330
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +125 -23
  199. data/lib/active_support/notifications/instrumenter.rb +98 -16
  200. data/lib/active_support/notifications.rb +82 -14
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -14
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +14 -11
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +12 -10
  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 +15 -5
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +29 -57
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +123 -71
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +7 -5
  213. data/lib/active_support/ordered_options.rb +35 -7
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +10 -4
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +10 -11
  218. data/lib/active_support/railtie.rb +66 -10
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +112 -57
  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 +13 -4
  224. data/lib/active_support/subscriber.rb +80 -31
  225. data/lib/active_support/tagged_logging.rb +54 -17
  226. data/lib/active_support/test_case.rb +107 -44
  227. data/lib/active_support/testing/assertions.rb +158 -20
  228. data/lib/active_support/testing/autorun.rb +4 -2
  229. data/lib/active_support/testing/constant_lookup.rb +2 -1
  230. data/lib/active_support/testing/declarative.rb +3 -1
  231. data/lib/active_support/testing/deprecation.rb +13 -10
  232. data/lib/active_support/testing/file_fixtures.rb +38 -0
  233. data/lib/active_support/testing/isolation.rb +35 -26
  234. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  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 +13 -8
  239. data/lib/active_support/testing/stream.rb +43 -0
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +121 -20
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +215 -51
  244. data/lib/active_support/values/time_zone.rb +223 -71
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +116 -115
  247. data/lib/active_support/xml_mini/libxml.rb +16 -13
  248. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  249. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  250. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +38 -46
  253. data/lib/active_support.rb +25 -11
  254. metadata +100 -43
  255. data/lib/active_support/concurrency/latch.rb +0 -27
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  258. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  259. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  260. data/lib/active_support/core_ext/hash/compact.rb +0 -24
  261. data/lib/active_support/core_ext/hash/transform_values.rb +0 -23
  262. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  263. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  264. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  265. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  266. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  267. data/lib/active_support/core_ext/object/itself.rb +0 -15
  268. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  269. data/lib/active_support/core_ext/struct.rb +0 -6
  270. data/lib/active_support/core_ext/thread.rb +0 -86
  271. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  272. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,6 +1,8 @@
1
- require 'active_support/log_subscriber'
2
- require 'active_support/logger'
3
- require 'active_support/notifications'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/log_subscriber"
4
+ require "active_support/logger"
5
+ require "active_support/notifications"
4
6
 
5
7
  module ActiveSupport
6
8
  class LogSubscriber
@@ -10,7 +12,7 @@ module ActiveSupport
10
12
  # class SyncLogSubscriberTest < ActiveSupport::TestCase
11
13
  # include ActiveSupport::LogSubscriber::TestHelper
12
14
  #
13
- # def setup
15
+ # setup do
14
16
  # ActiveRecord::LogSubscriber.attach_to(:active_record)
15
17
  # end
16
18
  #
@@ -33,7 +35,7 @@ module ActiveSupport
33
35
  # you can collect them doing @logger.logged(level), where level is the level
34
36
  # used in logging, like info, debug, warn and so on.
35
37
  module TestHelper
36
- def setup
38
+ def setup # :nodoc:
37
39
  @logger = MockLogger.new
38
40
  @notifier = ActiveSupport::Notifications::Fanout.new
39
41
 
@@ -44,7 +46,7 @@ module ActiveSupport
44
46
  ActiveSupport::Notifications.notifier = @notifier
45
47
  end
46
48
 
47
- def teardown
49
+ def teardown # :nodoc:
48
50
  set_logger(nil)
49
51
  ActiveSupport::Notifications.notifier = @old_notifier
50
52
  end
@@ -58,15 +60,15 @@ module ActiveSupport
58
60
  def initialize(level = DEBUG)
59
61
  @flush_count = 0
60
62
  @level = level
61
- @logged = Hash.new { |h,k| h[k] = [] }
63
+ @logged = Hash.new { |h, k| h[k] = [] }
62
64
  end
63
65
 
64
66
  def method_missing(level, message = nil)
65
- if block_given?
66
- @logged[level] << yield
67
- else
68
- @logged[level] << message
69
- end
67
+ if block_given?
68
+ @logged[level] << yield
69
+ else
70
+ @logged[level] << message
71
+ end
70
72
  end
71
73
 
72
74
  def logged(level)
@@ -1,10 +1,12 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
- require 'active_support/core_ext/class/attribute'
3
- require 'active_support/subscriber'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+ require "active_support/core_ext/class/attribute"
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
@@ -81,12 +113,13 @@ module ActiveSupport
81
113
 
82
114
  def finish(name, id, payload)
83
115
  super if logger
84
- rescue Exception => e
85
- logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
116
+ rescue => e
117
+ if logger
118
+ logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
119
+ end
86
120
  end
87
121
 
88
- protected
89
-
122
+ private
90
123
  %w(info debug warn error fatal unknown).each do |level|
91
124
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
92
125
  def #{level}(progname = nil, &block)
@@ -95,11 +128,11 @@ module ActiveSupport
95
128
  METHOD
96
129
  end
97
130
 
98
- # Set color by using a string or one of the defined constants. If a third
131
+ # Set color by using a symbol or one of the defined constants. If a third
99
132
  # option is set to +true+, it also adds bold to the string. This is based
100
133
  # on the Highline implementation and will automatically append CLEAR to the
101
134
  # end of the returned String.
102
- def color(text, color, bold=false)
135
+ def color(text, color, bold = false) # :doc:
103
136
  return text unless colorize_logging
104
137
  color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
105
138
  bold = bold ? BOLD : ""
@@ -1,11 +1,11 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
- require 'active_support/logger_silence'
3
- require 'active_support/logger_thread_safe_level'
4
- require 'logger'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/logger_silence"
4
+ require "active_support/logger_thread_safe_level"
5
+ require "logger"
5
6
 
6
7
  module ActiveSupport
7
8
  class Logger < ::Logger
8
- include ActiveSupport::LoggerThreadSafeLevel
9
9
  include LoggerSilence
10
10
 
11
11
  # Returns true if the logger destination matches one of the sources
@@ -14,7 +14,7 @@ module ActiveSupport
14
14
  # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
15
15
  # # => true
16
16
  def self.logger_outputs_to?(logger, *sources)
17
- logdev = logger.instance_variable_get("@logdev")
17
+ logdev = logger.instance_variable_get(:@logdev)
18
18
  logger_source = logdev.dev if logdev.respond_to?(:dev)
19
19
  sources.any? { |source| source == logger_source }
20
20
  end
@@ -58,16 +58,16 @@ module ActiveSupport
58
58
  end
59
59
 
60
60
  define_method(:silence) do |level = Logger::ERROR, &block|
61
- if logger.respond_to?(:silence) && logger.method(:silence).owner != ::Kernel
61
+ if logger.respond_to?(:silence)
62
62
  logger.silence(level) do
63
- if respond_to?(:silence) && method(:silence).owner != ::Kernel
63
+ if defined?(super)
64
64
  super(level, &block)
65
65
  else
66
66
  block.call(self)
67
67
  end
68
68
  end
69
69
  else
70
- if respond_to?(:silence) && method(:silence).owner != ::Kernel
70
+ if defined?(super)
71
71
  super(level, &block)
72
72
  else
73
73
  block.call(self)
@@ -77,23 +77,9 @@ module ActiveSupport
77
77
  end
78
78
  end
79
79
 
80
- def initialize(*args)
80
+ def initialize(*args, **kwargs)
81
81
  super
82
82
  @formatter = SimpleFormatter.new
83
- after_initialize if respond_to? :after_initialize
84
- end
85
-
86
- def add(severity, message = nil, progname = nil, &block)
87
- return true if @logdev.nil? || (severity || UNKNOWN) < level
88
- super
89
- end
90
-
91
- Logger::Severity.constants.each do |severity|
92
- class_eval(<<-EOT, __FILE__, __LINE__ + 1)
93
- def #{severity.downcase}? # def debug?
94
- Logger::#{severity} >= level # DEBUG >= level
95
- end # end
96
- EOT
97
83
  end
98
84
 
99
85
  # Simple formatter which only displays the message.
@@ -1,27 +1,21 @@
1
- require 'active_support/concern'
2
- require 'thread_safe'
1
+ # frozen_string_literal: true
3
2
 
4
- module LoggerSilence
5
- extend ActiveSupport::Concern
3
+ require "active_support/concern"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "active_support/logger_thread_safe_level"
6
6
 
7
- included do
8
- cattr_accessor :silencer
9
- self.silencer = true
10
- end
7
+ module ActiveSupport
8
+ module LoggerSilence
9
+ extend ActiveSupport::Concern
11
10
 
12
- # Silences the logger for the duration of the block.
13
- def silence(temporary_level = Logger::ERROR)
14
- if silencer
15
- begin
16
- old_local_level = local_level
17
- self.local_level = temporary_level
11
+ included do
12
+ cattr_accessor :silencer, default: true
13
+ include ActiveSupport::LoggerThreadSafeLevel
14
+ end
18
15
 
19
- yield self
20
- ensure
21
- self.local_level = old_local_level
22
- end
23
- else
24
- 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)
25
19
  end
26
20
  end
27
21
  end
@@ -1,32 +1,78 @@
1
- require 'active_support/concern'
2
- require 'thread_safe'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "concurrent"
6
+ require "fiber"
3
7
 
4
8
  module ActiveSupport
5
- module LoggerThreadSafeLevel
9
+ module LoggerThreadSafeLevel # :nodoc:
6
10
  extend ActiveSupport::Concern
7
11
 
8
- def after_initialize
9
- @local_levels = ThreadSafe::Cache.new
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
10
22
  end
11
23
 
12
24
  def local_log_id
13
- Thread.current.__id__
25
+ Fiber.current.__id__
14
26
  end
15
27
 
16
28
  def local_level
17
- @local_levels[local_log_id]
29
+ self.class.local_levels[local_log_id]
18
30
  end
19
31
 
20
32
  def local_level=(level)
21
- if level
22
- @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)
23
40
  else
24
- @local_levels.delete(local_log_id)
41
+ raise ArgumentError, "Invalid log level: #{level.inspect}"
25
42
  end
26
43
  end
27
44
 
28
45
  def level
29
46
  local_level || super
30
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
31
77
  end
32
78
  end
@@ -1,6 +1,10 @@
1
- require 'openssl'
2
- require 'base64'
3
- require 'active_support/core_ext/array/extract_options'
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+ require "base64"
5
+ require "active_support/core_ext/module/attribute_accessors"
6
+ require "active_support/message_verifier"
7
+ require "active_support/messages/metadata"
4
8
 
5
9
  module ActiveSupport
6
10
  # MessageEncryptor is a simple way to encrypt values which get stored
@@ -12,13 +16,82 @@ module ActiveSupport
12
16
  # This can be used in situations similar to the <tt>MessageVerifier</tt>, but
13
17
  # where you don't want users to be able to determine the value of the payload.
14
18
  #
15
- # salt = SecureRandom.random_bytes(64)
16
- # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt) # => "\x89\xE0\x156\xAC..."
17
- # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
18
- # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
19
- # 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"
20
81
  class MessageEncryptor
21
- 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
22
95
 
23
96
  module NullSerializer #:nodoc:
24
97
  def self.load(value)
@@ -30,85 +103,122 @@ module ActiveSupport
30
103
  end
31
104
  end
32
105
 
106
+ module NullVerifier #:nodoc:
107
+ def self.verify(value)
108
+ value
109
+ end
110
+
111
+ def self.generate(value)
112
+ value
113
+ end
114
+ end
115
+
33
116
  class InvalidMessage < StandardError; end
34
117
  OpenSSLCipherError = OpenSSL::Cipher::CipherError
35
118
 
36
119
  # Initialize a new MessageEncryptor. +secret+ must be at least as long as
37
- # 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
38
121
  # bits. If you are using a user-entered secret, you can generate a suitable
39
- # key with <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or
40
- # similar.
122
+ # key by using <tt>ActiveSupport::KeyGenerator</tt> or a similar key
123
+ # derivation function.
124
+ #
125
+ # First additional parameter is used as the signature key for +MessageVerifier+.
126
+ # This allows you to specify keys to encrypt and sign data.
127
+ #
128
+ # ActiveSupport::MessageEncryptor.new('secret', 'signature_secret')
41
129
  #
42
130
  # Options:
43
131
  # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
44
- # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
45
- # * <tt>:digest</tt> - String of digest to use for signing. Default is +SHA1+.
132
+ # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-gcm'.
133
+ # * <tt>:digest</tt> - String of digest to use for signing. Default is
134
+ # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'.
46
135
  # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
47
- def initialize(secret, *signature_key_or_options)
48
- options = signature_key_or_options.extract_options!
49
- sign_secret = signature_key_or_options.first
136
+ def initialize(secret, sign_secret = nil, cipher: nil, digest: nil, serializer: nil)
50
137
  @secret = secret
51
138
  @sign_secret = sign_secret
52
- @cipher = options[:cipher] || 'aes-256-cbc'
53
- @verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer)
54
- @serializer = options[:serializer] || Marshal
139
+ @cipher = cipher || self.class.default_cipher
140
+ @digest = digest || "SHA1" unless aead_mode?
141
+ @verifier = resolve_verifier
142
+ @serializer = serializer || Marshal
55
143
  end
56
144
 
57
145
  # Encrypt and sign a message. We need to sign the message in order to avoid
58
- # padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
59
- def encrypt_and_sign(value)
60
- 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))
61
149
  end
62
150
 
63
151
  # Decrypt and verify a message. We need to verify the message in order to
64
- # avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
65
- def decrypt_and_verify(value)
66
- _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)
67
155
  end
68
156
 
69
157
  # Given a cipher, returns the key length of the cipher to help generate the key of desired size
70
- def self.key_len(cipher = DEFAULT_CIPHER)
158
+ def self.key_len(cipher = default_cipher)
71
159
  OpenSSL::Cipher.new(cipher).key_len
72
160
  end
73
161
 
74
162
  private
163
+ def _encrypt(value, **metadata_options)
164
+ cipher = new_cipher
165
+ cipher.encrypt
166
+ cipher.key = @secret
75
167
 
76
- def _encrypt(value)
77
- cipher = new_cipher
78
- cipher.encrypt
79
- cipher.key = @secret
168
+ # Rely on OpenSSL for the initialization vector
169
+ iv = cipher.random_iv
170
+ cipher.auth_data = "" if aead_mode?
80
171
 
81
- # Rely on OpenSSL for the initialization vector
82
- iv = cipher.random_iv
172
+ encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), **metadata_options))
173
+ encrypted_data << cipher.final
83
174
 
84
- encrypted_data = cipher.update(@serializer.dump(value))
85
- encrypted_data << cipher.final
86
-
87
- "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
88
- end
175
+ blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
176
+ blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
177
+ blob
178
+ end
89
179
 
90
- def _decrypt(encrypted_message)
91
- cipher = new_cipher
92
- encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}
180
+ def _decrypt(encrypted_message, purpose)
181
+ cipher = new_cipher
182
+ encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) }
183
+
184
+ # Currently the OpenSSL bindings do not raise an error if auth_tag is
185
+ # truncated, which would allow an attacker to easily forge it. See
186
+ # https://github.com/ruby/openssl/issues/63
187
+ raise InvalidMessage if aead_mode? && (auth_tag.nil? || auth_tag.bytes.length != 16)
188
+
189
+ cipher.decrypt
190
+ cipher.key = @secret
191
+ cipher.iv = iv
192
+ if aead_mode?
193
+ cipher.auth_tag = auth_tag
194
+ cipher.auth_data = ""
195
+ end
196
+
197
+ decrypted_data = cipher.update(encrypted_data)
198
+ decrypted_data << cipher.final
199
+
200
+ message = Messages::Metadata.verify(decrypted_data, purpose)
201
+ @serializer.load(message) if message
202
+ rescue OpenSSLCipherError, TypeError, ArgumentError
203
+ raise InvalidMessage
204
+ end
93
205
 
94
- cipher.decrypt
95
- cipher.key = @secret
96
- cipher.iv = iv
206
+ def new_cipher
207
+ OpenSSL::Cipher.new(@cipher)
208
+ end
97
209
 
98
- decrypted_data = cipher.update(encrypted_data)
99
- decrypted_data << cipher.final
210
+ attr_reader :verifier
100
211
 
101
- @serializer.load(decrypted_data)
102
- rescue OpenSSLCipherError, TypeError, ArgumentError
103
- raise InvalidMessage
104
- end
105
-
106
- def new_cipher
107
- OpenSSL::Cipher.new(@cipher)
108
- end
212
+ def aead_mode?
213
+ @aead_mode ||= new_cipher.authenticated?
214
+ end
109
215
 
110
- def verifier
111
- @verifier
112
- end
216
+ def resolve_verifier
217
+ if aead_mode?
218
+ NullVerifier
219
+ else
220
+ MessageVerifier.new(@sign_secret || @secret, digest: @digest, serializer: NullSerializer)
221
+ end
222
+ end
113
223
  end
114
224
  end