activesupport 4.0.13 → 5.2.5

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 (264) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +412 -444
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +8 -4
  5. data/lib/active_support/all.rb +5 -3
  6. data/lib/active_support/array_inquirer.rb +48 -0
  7. data/lib/active_support/backtrace_cleaner.rb +14 -12
  8. data/lib/active_support/benchmarkable.rb +6 -14
  9. data/lib/active_support/builder.rb +3 -1
  10. data/lib/active_support/cache/file_store.rb +67 -51
  11. data/lib/active_support/cache/mem_cache_store.rb +95 -97
  12. data/lib/active_support/cache/memory_store.rb +28 -30
  13. data/lib/active_support/cache/null_store.rb +7 -8
  14. data/lib/active_support/cache/redis_cache_store.rb +466 -0
  15. data/lib/active_support/cache/strategy/local_cache.rb +70 -56
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  17. data/lib/active_support/cache.rb +331 -206
  18. data/lib/active_support/callbacks.rb +697 -426
  19. data/lib/active_support/concern.rb +32 -10
  20. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  21. data/lib/active_support/concurrency/share_lock.rb +227 -0
  22. data/lib/active_support/configurable.rb +8 -5
  23. data/lib/active_support/core_ext/array/access.rb +39 -1
  24. data/lib/active_support/core_ext/array/conversions.rb +24 -35
  25. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +23 -13
  27. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  28. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -5
  29. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  30. data/lib/active_support/core_ext/array.rb +9 -7
  31. data/lib/active_support/core_ext/benchmark.rb +3 -1
  32. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  33. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  34. data/lib/active_support/core_ext/class/attribute.rb +41 -23
  35. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  36. data/lib/active_support/core_ext/class/subclasses.rb +20 -8
  37. data/lib/active_support/core_ext/class.rb +4 -4
  38. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  39. data/lib/active_support/core_ext/date/blank.rb +14 -0
  40. data/lib/active_support/core_ext/date/calculations.rb +21 -9
  41. data/lib/active_support/core_ext/date/conversions.rb +32 -22
  42. data/lib/active_support/core_ext/date/zones.rb +5 -34
  43. data/lib/active_support/core_ext/date.rb +6 -4
  44. data/lib/active_support/core_ext/date_and_time/calculations.rb +199 -57
  45. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  46. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  47. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  48. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  49. data/lib/active_support/core_ext/date_time/calculations.rb +78 -37
  50. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  51. data/lib/active_support/core_ext/date_time/conversions.rb +19 -13
  52. data/lib/active_support/core_ext/date_time.rb +7 -4
  53. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  54. data/lib/active_support/core_ext/digest.rb +3 -0
  55. data/lib/active_support/core_ext/enumerable.rb +113 -29
  56. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  57. data/lib/active_support/core_ext/file.rb +3 -1
  58. data/lib/active_support/core_ext/hash/compact.rb +29 -0
  59. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  60. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  61. data/lib/active_support/core_ext/hash/except.rb +12 -3
  62. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  63. data/lib/active_support/core_ext/hash/keys.rb +50 -38
  64. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  65. data/lib/active_support/core_ext/hash/slice.rb +12 -6
  66. data/lib/active_support/core_ext/hash/transform_values.rb +32 -0
  67. data/lib/active_support/core_ext/hash.rb +11 -8
  68. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  69. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  70. data/lib/active_support/core_ext/integer/time.rb +11 -33
  71. data/lib/active_support/core_ext/integer.rb +5 -3
  72. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  74. data/lib/active_support/core_ext/kernel/reporting.rb +5 -74
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +6 -4
  77. data/lib/active_support/core_ext/load_error.rb +5 -21
  78. data/lib/active_support/core_ext/marshal.rb +13 -10
  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 -8
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +170 -21
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  85. data/lib/active_support/core_ext/module/delegation.rb +135 -45
  86. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  87. data/lib/active_support/core_ext/module/introspection.rb +9 -25
  88. data/lib/active_support/core_ext/module/reachable.rb +5 -2
  89. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  90. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  91. data/lib/active_support/core_ext/module.rb +14 -10
  92. data/lib/active_support/core_ext/name_error.rb +22 -2
  93. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  94. data/lib/active_support/core_ext/numeric/conversions.rb +79 -74
  95. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  96. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  97. data/lib/active_support/core_ext/numeric.rb +6 -3
  98. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  99. data/lib/active_support/core_ext/object/blank.rb +70 -19
  100. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  101. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  102. data/lib/active_support/core_ext/object/duplicable.rb +100 -34
  103. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  104. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  105. data/lib/active_support/core_ext/object/json.rb +227 -0
  106. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  107. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  108. data/lib/active_support/core_ext/object/try.rb +94 -24
  109. data/lib/active_support/core_ext/object/with_options.rb +45 -5
  110. data/lib/active_support/core_ext/object.rb +14 -12
  111. data/lib/active_support/core_ext/range/compare_range.rb +61 -0
  112. data/lib/active_support/core_ext/range/conversions.rb +27 -7
  113. data/lib/active_support/core_ext/range/each.rb +19 -17
  114. data/lib/active_support/core_ext/range/include_range.rb +2 -22
  115. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  116. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  117. data/lib/active_support/core_ext/range.rb +7 -4
  118. data/lib/active_support/core_ext/regexp.rb +6 -0
  119. data/lib/active_support/core_ext/securerandom.rb +25 -0
  120. data/lib/active_support/core_ext/string/access.rb +41 -39
  121. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  122. data/lib/active_support/core_ext/string/conversions.rb +17 -13
  123. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  124. data/lib/active_support/core_ext/string/filters.rb +55 -6
  125. data/lib/active_support/core_ext/string/indent.rb +6 -4
  126. data/lib/active_support/core_ext/string/inflections.rb +66 -24
  127. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  128. data/lib/active_support/core_ext/string/multibyte.rb +15 -7
  129. data/lib/active_support/core_ext/string/output_safety.rb +114 -54
  130. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  131. data/lib/active_support/core_ext/string/strip.rb +4 -5
  132. data/lib/active_support/core_ext/string/zones.rb +4 -1
  133. data/lib/active_support/core_ext/string.rb +15 -13
  134. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  135. data/lib/active_support/core_ext/time/calculations.rb +123 -110
  136. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  137. data/lib/active_support/core_ext/time/conversions.rb +23 -14
  138. data/lib/active_support/core_ext/time/zones.rb +42 -26
  139. data/lib/active_support/core_ext/time.rb +7 -5
  140. data/lib/active_support/core_ext/uri.rb +6 -8
  141. data/lib/active_support/core_ext.rb +3 -2
  142. data/lib/active_support/current_attributes.rb +195 -0
  143. data/lib/active_support/dependencies/autoload.rb +3 -1
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies.rb +196 -166
  146. data/lib/active_support/deprecation/behaviors.rb +48 -15
  147. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  149. data/lib/active_support/deprecation/method_wrappers.rb +66 -20
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
  151. data/lib/active_support/deprecation/reporting.rb +32 -12
  152. data/lib/active_support/deprecation.rb +14 -11
  153. data/lib/active_support/descendants_tracker.rb +2 -0
  154. data/lib/active_support/digest.rb +20 -0
  155. data/lib/active_support/duration/iso8601_parser.rb +125 -0
  156. data/lib/active_support/duration/iso8601_serializer.rb +55 -0
  157. data/lib/active_support/duration.rb +354 -34
  158. data/lib/active_support/encrypted_configuration.rb +49 -0
  159. data/lib/active_support/encrypted_file.rb +99 -0
  160. data/lib/active_support/evented_file_update_checker.rb +205 -0
  161. data/lib/active_support/execution_wrapper.rb +128 -0
  162. data/lib/active_support/executor.rb +8 -0
  163. data/lib/active_support/file_update_checker.rb +63 -37
  164. data/lib/active_support/gem_version.rb +17 -0
  165. data/lib/active_support/gzip.rb +7 -5
  166. data/lib/active_support/hash_with_indifferent_access.rb +158 -35
  167. data/lib/active_support/i18n.rb +8 -6
  168. data/lib/active_support/i18n_railtie.rb +38 -20
  169. data/lib/active_support/inflections.rb +19 -12
  170. data/lib/active_support/inflector/inflections.rb +79 -30
  171. data/lib/active_support/inflector/methods.rb +197 -129
  172. data/lib/active_support/inflector/transliterate.rb +48 -27
  173. data/lib/active_support/inflector.rb +7 -5
  174. data/lib/active_support/json/decoding.rb +21 -25
  175. data/lib/active_support/json/encoding.rb +84 -292
  176. data/lib/active_support/json.rb +4 -2
  177. data/lib/active_support/key_generator.rb +26 -28
  178. data/lib/active_support/lazy_load_hooks.rb +51 -21
  179. data/lib/active_support/locale/en.yml +2 -0
  180. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  181. data/lib/active_support/log_subscriber.rb +13 -10
  182. data/lib/active_support/logger.rb +54 -3
  183. data/lib/active_support/logger_silence.rb +12 -7
  184. data/lib/active_support/logger_thread_safe_level.rb +34 -0
  185. data/lib/active_support/message_encryptor.rb +173 -50
  186. data/lib/active_support/message_verifier.rb +159 -22
  187. data/lib/active_support/messages/metadata.rb +71 -0
  188. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  189. data/lib/active_support/messages/rotator.rb +56 -0
  190. data/lib/active_support/multibyte/chars.rb +38 -26
  191. data/lib/active_support/multibyte/unicode.rb +138 -146
  192. data/lib/active_support/multibyte.rb +4 -2
  193. data/lib/active_support/notifications/fanout.rb +23 -16
  194. data/lib/active_support/notifications/instrumenter.rb +29 -8
  195. data/lib/active_support/notifications.rb +22 -13
  196. data/lib/active_support/number_helper/number_converter.rb +184 -0
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +29 -0
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +59 -0
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +14 -0
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +54 -0
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/number_helper.rb +125 -391
  206. data/lib/active_support/option_merger.rb +3 -1
  207. data/lib/active_support/ordered_hash.rb +6 -4
  208. data/lib/active_support/ordered_options.rb +31 -5
  209. data/lib/active_support/per_thread_registry.rb +19 -11
  210. data/lib/active_support/proxy_object.rb +2 -0
  211. data/lib/active_support/rails.rb +16 -8
  212. data/lib/active_support/railtie.rb +43 -9
  213. data/lib/active_support/reloader.rb +131 -0
  214. data/lib/active_support/rescuable.rb +108 -53
  215. data/lib/active_support/security_utils.rb +31 -0
  216. data/lib/active_support/string_inquirer.rb +11 -3
  217. data/lib/active_support/subscriber.rb +54 -17
  218. data/lib/active_support/tagged_logging.rb +14 -11
  219. data/lib/active_support/test_case.rb +42 -37
  220. data/lib/active_support/testing/assertions.rb +126 -39
  221. data/lib/active_support/testing/autorun.rb +5 -3
  222. data/lib/active_support/testing/constant_lookup.rb +3 -6
  223. data/lib/active_support/testing/declarative.rb +10 -22
  224. data/lib/active_support/testing/deprecation.rb +14 -10
  225. data/lib/active_support/testing/file_fixtures.rb +36 -0
  226. data/lib/active_support/testing/isolation.rb +55 -86
  227. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  228. data/lib/active_support/testing/setup_and_teardown.rb +30 -10
  229. data/lib/active_support/testing/stream.rb +44 -0
  230. data/lib/active_support/testing/tagged_logging.rb +5 -3
  231. data/lib/active_support/testing/time_helpers.rb +200 -0
  232. data/lib/active_support/time.rb +13 -13
  233. data/lib/active_support/time_with_zone.rb +223 -73
  234. data/lib/active_support/values/time_zone.rb +261 -126
  235. data/lib/active_support/values/unicode_tables.dat +0 -0
  236. data/lib/active_support/version.rb +6 -7
  237. data/lib/active_support/xml_mini/jdom.rb +116 -113
  238. data/lib/active_support/xml_mini/libxml.rb +17 -16
  239. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  240. data/lib/active_support/xml_mini/nokogiri.rb +15 -15
  241. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  242. data/lib/active_support/xml_mini/rexml.rb +17 -16
  243. data/lib/active_support/xml_mini.rb +69 -51
  244. data/lib/active_support.rb +29 -3
  245. metadata +84 -54
  246. data/lib/active_support/basic_object.rb +0 -11
  247. data/lib/active_support/buffered_logger.rb +0 -21
  248. data/lib/active_support/concurrency/latch.rb +0 -27
  249. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  250. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  251. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  252. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  253. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  254. data/lib/active_support/core_ext/logger.rb +0 -67
  255. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  256. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  257. data/lib/active_support/core_ext/proc.rb +0 -17
  258. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  259. data/lib/active_support/core_ext/struct.rb +0 -6
  260. data/lib/active_support/core_ext/thread.rb +0 -79
  261. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  262. data/lib/active_support/file_watcher.rb +0 -36
  263. data/lib/active_support/json/variable.rb +0 -18
  264. data/lib/active_support/testing/pending.rb +0 -14
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
- # lazy_load_hooks allows rails to lazily load a lot of components and thus
4
+ # lazy_load_hooks allows Rails to lazily load a lot of components and thus
3
5
  # making the app boot faster. Because of this feature now there is no need to
4
6
  # require <tt>ActiveRecord::Base</tt> at boot time purely to apply
5
7
  # configuration. Instead a hook is registered that applies configuration once
@@ -15,34 +17,62 @@ module ActiveSupport
15
17
  # end
16
18
  # end
17
19
  #
18
- # When the entirety of +activerecord/lib/active_record/base.rb+ has been
20
+ # When the entirety of +ActiveRecord::Base+ has been
19
21
  # evaluated then +run_load_hooks+ is invoked. The very last line of
20
- # +activerecord/lib/active_record/base.rb+ is:
22
+ # +ActiveRecord::Base+ is:
21
23
  #
22
24
  # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
23
- @load_hooks = Hash.new { |h,k| h[k] = [] }
24
- @loaded = Hash.new { |h,k| h[k] = [] }
25
-
26
- def self.on_load(name, options = {}, &block)
27
- @loaded[name].each do |base|
28
- execute_hook(base, options, block)
25
+ module LazyLoadHooks
26
+ def self.extended(base) # :nodoc:
27
+ base.class_eval do
28
+ @load_hooks = Hash.new { |h, k| h[k] = [] }
29
+ @loaded = Hash.new { |h, k| h[k] = [] }
30
+ @run_once = Hash.new { |h, k| h[k] = [] }
31
+ end
29
32
  end
30
33
 
31
- @load_hooks[name] << [block, options]
32
- end
34
+ # Declares a block that will be executed when a Rails component is fully
35
+ # loaded.
36
+ #
37
+ # Options:
38
+ #
39
+ # * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+.
40
+ # * <tt>:run_once</tt> - Given +block+ will run only once.
41
+ def on_load(name, options = {}, &block)
42
+ @loaded[name].each do |base|
43
+ execute_hook(name, base, options, block)
44
+ end
33
45
 
34
- def self.execute_hook(base, options, block)
35
- if options[:yield]
36
- block.call(base)
37
- else
38
- base.instance_eval(&block)
46
+ @load_hooks[name] << [block, options]
39
47
  end
40
- end
41
48
 
42
- def self.run_load_hooks(name, base = Object)
43
- @loaded[name] << base
44
- @load_hooks[name].each do |hook, options|
45
- execute_hook(base, options, hook)
49
+ def run_load_hooks(name, base = Object)
50
+ @loaded[name] << base
51
+ @load_hooks[name].each do |hook, options|
52
+ execute_hook(name, base, options, hook)
53
+ end
46
54
  end
55
+
56
+ private
57
+
58
+ def with_execution_control(name, block, once)
59
+ unless @run_once[name].include?(block)
60
+ @run_once[name] << block if once
61
+
62
+ yield
63
+ end
64
+ end
65
+
66
+ def execute_hook(name, base, options, block)
67
+ with_execution_control(name, block, options[:run_once]) do
68
+ if options[:yield]
69
+ block.call(base)
70
+ else
71
+ base.instance_eval(&block)
72
+ end
73
+ end
74
+ end
47
75
  end
76
+
77
+ extend LazyLoadHooks
48
78
  end
@@ -106,6 +106,8 @@ en:
106
106
  mb: "MB"
107
107
  gb: "GB"
108
108
  tb: "TB"
109
+ pb: "PB"
110
+ eb: "EB"
109
111
  # Used in NumberHelper.number_to_human()
110
112
  decimal_units:
111
113
  format: "%n %u"
@@ -1,6 +1,8 @@
1
- require 'active_support/log_subscriber'
2
- require 'active_support/buffered_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,6 +1,8 @@
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
8
  # ActiveSupport::LogSubscriber is an object set to consume
@@ -49,8 +51,7 @@ module ActiveSupport
49
51
  CYAN = "\e[36m"
50
52
  WHITE = "\e[37m"
51
53
 
52
- mattr_accessor :colorize_logging
53
- self.colorize_logging = true
54
+ mattr_accessor :colorize_logging, default: true
54
55
 
55
56
  class << self
56
57
  def logger
@@ -81,11 +82,13 @@ module ActiveSupport
81
82
 
82
83
  def finish(name, id, payload)
83
84
  super if logger
84
- rescue Exception => e
85
- logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
85
+ rescue => e
86
+ if logger
87
+ logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
88
+ end
86
89
  end
87
90
 
88
- protected
91
+ private
89
92
 
90
93
  %w(info debug warn error fatal unknown).each do |level|
91
94
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -95,11 +98,11 @@ module ActiveSupport
95
98
  METHOD
96
99
  end
97
100
 
98
- # Set color by using a string or one of the defined constants. If a third
101
+ # Set color by using a symbol or one of the defined constants. If a third
99
102
  # option is set to +true+, it also adds bold to the string. This is based
100
103
  # on the Highline implementation and will automatically append CLEAR to the
101
104
  # end of the returned String.
102
- def color(text, color, bold=false)
105
+ def color(text, color, bold = false) # :doc:
103
106
  return text unless colorize_logging
104
107
  color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
105
108
  bold = bold ? BOLD : ""
@@ -1,11 +1,25 @@
1
- require 'active_support/core_ext/class/attribute_accessors'
2
- require 'active_support/logger_silence'
3
- 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"
4
6
 
5
7
  module ActiveSupport
6
8
  class Logger < ::Logger
9
+ include ActiveSupport::LoggerThreadSafeLevel
7
10
  include LoggerSilence
8
11
 
12
+ # Returns true if the logger destination matches one of the sources
13
+ #
14
+ # logger = Logger.new(STDOUT)
15
+ # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
16
+ # # => true
17
+ def self.logger_outputs_to?(logger, *sources)
18
+ logdev = logger.instance_variable_get("@logdev")
19
+ logger_source = logdev.dev if logdev.respond_to?(:dev)
20
+ sources.any? { |source| source == logger_source }
21
+ end
22
+
9
23
  # Broadcasts logs to multiple loggers.
10
24
  def self.broadcast(logger) # :nodoc:
11
25
  Module.new do
@@ -38,12 +52,49 @@ module ActiveSupport
38
52
  logger.level = level
39
53
  super(level)
40
54
  end
55
+
56
+ define_method(:local_level=) do |level|
57
+ logger.local_level = level if logger.respond_to?(:local_level=)
58
+ super(level) if respond_to?(:local_level=)
59
+ end
60
+
61
+ define_method(:silence) do |level = Logger::ERROR, &block|
62
+ if logger.respond_to?(:silence)
63
+ logger.silence(level) do
64
+ if defined?(super)
65
+ super(level, &block)
66
+ else
67
+ block.call(self)
68
+ end
69
+ end
70
+ else
71
+ if defined?(super)
72
+ super(level, &block)
73
+ else
74
+ block.call(self)
75
+ end
76
+ end
77
+ end
41
78
  end
42
79
  end
43
80
 
44
81
  def initialize(*args)
45
82
  super
46
83
  @formatter = SimpleFormatter.new
84
+ after_initialize if respond_to? :after_initialize
85
+ end
86
+
87
+ def add(severity, message = nil, progname = nil, &block)
88
+ return true if @logdev.nil? || (severity || UNKNOWN) < level
89
+ super
90
+ end
91
+
92
+ Logger::Severity.constants.each do |severity|
93
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
94
+ def #{severity.downcase}? # def debug?
95
+ Logger::#{severity} >= level # DEBUG >= level
96
+ end # end
97
+ EOT
47
98
  end
48
99
 
49
100
  # Simple formatter which only displays the message.
@@ -1,24 +1,29 @@
1
- require 'active_support/concern'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "concurrent"
2
6
 
3
7
  module LoggerSilence
4
8
  extend ActiveSupport::Concern
5
-
9
+
6
10
  included do
7
- cattr_accessor :silencer
8
- self.silencer = true
11
+ cattr_accessor :silencer, default: true
9
12
  end
10
13
 
11
14
  # Silences the logger for the duration of the block.
12
15
  def silence(temporary_level = Logger::ERROR)
13
16
  if silencer
14
17
  begin
15
- old_logger_level, self.level = level, temporary_level
18
+ old_local_level = local_level
19
+ self.local_level = temporary_level
20
+
16
21
  yield self
17
22
  ensure
18
- self.level = old_logger_level
23
+ self.local_level = old_local_level
19
24
  end
20
25
  else
21
26
  yield self
22
27
  end
23
28
  end
24
- end
29
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "fiber"
5
+
6
+ module ActiveSupport
7
+ module LoggerThreadSafeLevel # :nodoc:
8
+ extend ActiveSupport::Concern
9
+
10
+ def after_initialize
11
+ @local_levels = Concurrent::Map.new(initial_capacity: 2)
12
+ end
13
+
14
+ def local_log_id
15
+ Fiber.current.__id__
16
+ end
17
+
18
+ def local_level
19
+ @local_levels[local_log_id]
20
+ end
21
+
22
+ def local_level=(level)
23
+ if level
24
+ @local_levels[local_log_id] = level
25
+ else
26
+ @local_levels.delete(local_log_id)
27
+ end
28
+ end
29
+
30
+ def level
31
+ local_level || super
32
+ end
33
+ end
34
+ end
@@ -1,6 +1,11 @@
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/array/extract_options"
6
+ require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/message_verifier"
8
+ require "active_support/messages/metadata"
4
9
 
5
10
  module ActiveSupport
6
11
  # MessageEncryptor is a simple way to encrypt values which get stored
@@ -12,12 +17,83 @@ module ActiveSupport
12
17
  # This can be used in situations similar to the <tt>MessageVerifier</tt>, but
13
18
  # where you don't want users to be able to determine the value of the payload.
14
19
  #
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"
20
+ # len = ActiveSupport::MessageEncryptor.key_len
21
+ # salt = SecureRandom.random_bytes(len)
22
+ # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, len) # => "\x89\xE0\x156\xAC..."
23
+ # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
24
+ # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
25
+ # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
26
+ #
27
+ # === Confining messages to a specific purpose
28
+ #
29
+ # By default any message can be used throughout your app. But they can also be
30
+ # confined to a specific +:purpose+.
31
+ #
32
+ # token = crypt.encrypt_and_sign("this is the chair", purpose: :login)
33
+ #
34
+ # Then that same purpose must be passed when verifying to get the data back out:
35
+ #
36
+ # crypt.decrypt_and_verify(token, purpose: :login) # => "this is the chair"
37
+ # crypt.decrypt_and_verify(token, purpose: :shipping) # => nil
38
+ # crypt.decrypt_and_verify(token) # => nil
39
+ #
40
+ # Likewise, if a message has no purpose it won't be returned when verifying with
41
+ # a specific purpose.
42
+ #
43
+ # token = crypt.encrypt_and_sign("the conversation is lively")
44
+ # crypt.decrypt_and_verify(token, purpose: :scare_tactics) # => nil
45
+ # crypt.decrypt_and_verify(token) # => "the conversation is lively"
46
+ #
47
+ # === Making messages expire
48
+ #
49
+ # By default messages last forever and verifying one year from now will still
50
+ # return the original value. But messages can be set to expire at a given
51
+ # time with +:expires_in+ or +:expires_at+.
52
+ #
53
+ # crypt.encrypt_and_sign(parcel, expires_in: 1.month)
54
+ # crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
55
+ #
56
+ # Then the messages can be verified and returned upto the expire time.
57
+ # Thereafter, verifying returns +nil+.
58
+ #
59
+ # === Rotating keys
60
+ #
61
+ # MessageEncryptor also supports rotating out old configurations by falling
62
+ # back to a stack of encryptors. Call +rotate+ to build and add an encryptor
63
+ # so +decrypt_and_verify+ will also try the fallback.
64
+ #
65
+ # By default any rotated encryptors use the values of the primary
66
+ # encryptor unless specified otherwise.
67
+ #
68
+ # You'd give your encryptor the new defaults:
69
+ #
70
+ # crypt = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
71
+ #
72
+ # Then gradually rotate the old values out by adding them as fallbacks. Any message
73
+ # generated with the old values will then work until the rotation is removed.
74
+ #
75
+ # crypt.rotate old_secret # Fallback to an old secret instead of @secret.
76
+ # crypt.rotate cipher: "aes-256-cbc" # Fallback to an old cipher instead of aes-256-gcm.
77
+ #
78
+ # Though if both the secret and the cipher was changed at the same time,
79
+ # the above should be combined into:
80
+ #
81
+ # crypt.rotate old_secret, cipher: "aes-256-cbc"
20
82
  class MessageEncryptor
83
+ prepend Messages::Rotator::Encryptor
84
+
85
+ cattr_accessor :use_authenticated_message_encryption, instance_accessor: false, default: false
86
+
87
+ class << self
88
+ def default_cipher #:nodoc:
89
+ if use_authenticated_message_encryption
90
+ "aes-256-gcm"
91
+ else
92
+ "aes-256-cbc"
93
+ end
94
+ end
95
+ end
96
+
21
97
  module NullSerializer #:nodoc:
22
98
  def self.load(value)
23
99
  value
@@ -28,79 +104,126 @@ module ActiveSupport
28
104
  end
29
105
  end
30
106
 
107
+ module NullVerifier #:nodoc:
108
+ def self.verify(value)
109
+ value
110
+ end
111
+
112
+ def self.generate(value)
113
+ value
114
+ end
115
+ end
116
+
31
117
  class InvalidMessage < StandardError; end
32
118
  OpenSSLCipherError = OpenSSL::Cipher::CipherError
33
119
 
34
120
  # Initialize a new MessageEncryptor. +secret+ must be at least as long as
35
- # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256
121
+ # the cipher key size. For the default 'aes-256-gcm' cipher, this is 256
36
122
  # bits. If you are using a user-entered secret, you can generate a suitable
37
- # key with <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or
38
- # similar.
123
+ # key by using <tt>ActiveSupport::KeyGenerator</tt> or a similar key
124
+ # derivation function.
125
+ #
126
+ # First additional parameter is used as the signature key for +MessageVerifier+.
127
+ # This allows you to specify keys to encrypt and sign data.
128
+ #
129
+ # ActiveSupport::MessageEncryptor.new('secret', 'signature_secret')
39
130
  #
40
131
  # Options:
41
132
  # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
42
- # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
133
+ # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-gcm'.
134
+ # * <tt>:digest</tt> - String of digest to use for signing. Default is
135
+ # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'.
43
136
  # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
44
137
  def initialize(secret, *signature_key_or_options)
45
138
  options = signature_key_or_options.extract_options!
46
139
  sign_secret = signature_key_or_options.first
47
140
  @secret = secret
48
141
  @sign_secret = sign_secret
49
- @cipher = options[:cipher] || 'aes-256-cbc'
50
- @verifier = MessageVerifier.new(@sign_secret || @secret, :serializer => NullSerializer)
142
+ @cipher = options[:cipher] || self.class.default_cipher
143
+ @digest = options[:digest] || "SHA1" unless aead_mode?
144
+ @verifier = resolve_verifier
51
145
  @serializer = options[:serializer] || Marshal
52
146
  end
53
147
 
54
148
  # Encrypt and sign a message. We need to sign the message in order to avoid
55
- # padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
56
- def encrypt_and_sign(value)
57
- verifier.generate(_encrypt(value))
149
+ # padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
150
+ def encrypt_and_sign(value, expires_at: nil, expires_in: nil, purpose: nil)
151
+ verifier.generate(_encrypt(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose))
58
152
  end
59
153
 
60
154
  # Decrypt and verify a message. We need to verify the message in order to
61
- # avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
62
- def decrypt_and_verify(value)
63
- _decrypt(verifier.verify(value))
155
+ # avoid padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/.
156
+ def decrypt_and_verify(data, purpose: nil, **)
157
+ _decrypt(verifier.verify(data), purpose)
64
158
  end
65
159
 
66
- private
67
-
68
- def _encrypt(value)
69
- cipher = new_cipher
70
- cipher.encrypt
71
- cipher.key = @secret
160
+ # Given a cipher, returns the key length of the cipher to help generate the key of desired size
161
+ def self.key_len(cipher = default_cipher)
162
+ OpenSSL::Cipher.new(cipher).key_len
163
+ end
72
164
 
73
- # Rely on OpenSSL for the initialization vector
74
- iv = cipher.random_iv
165
+ private
166
+ def _encrypt(value, **metadata_options)
167
+ cipher = new_cipher
168
+ cipher.encrypt
169
+ cipher.key = @secret
75
170
 
76
- encrypted_data = cipher.update(@serializer.dump(value))
77
- encrypted_data << cipher.final
171
+ # Rely on OpenSSL for the initialization vector
172
+ iv = cipher.random_iv
173
+ cipher.auth_data = "" if aead_mode?
78
174
 
79
- [encrypted_data, iv].map {|v| ::Base64.strict_encode64(v)}.join("--")
80
- end
175
+ encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), metadata_options))
176
+ encrypted_data << cipher.final
81
177
 
82
- def _decrypt(encrypted_message)
83
- cipher = new_cipher
84
- encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.decode64(v)}
178
+ blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
179
+ blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
180
+ blob
181
+ end
85
182
 
86
- cipher.decrypt
87
- cipher.key = @secret
88
- cipher.iv = iv
183
+ def _decrypt(encrypted_message, purpose)
184
+ cipher = new_cipher
185
+ encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }
186
+
187
+ # Currently the OpenSSL bindings do not raise an error if auth_tag is
188
+ # truncated, which would allow an attacker to easily forge it. See
189
+ # https://github.com/ruby/openssl/issues/63
190
+ raise InvalidMessage if aead_mode? && (auth_tag.nil? || auth_tag.bytes.length != 16)
191
+
192
+ cipher.decrypt
193
+ cipher.key = @secret
194
+ cipher.iv = iv
195
+ if aead_mode?
196
+ cipher.auth_tag = auth_tag
197
+ cipher.auth_data = ""
198
+ end
199
+
200
+ decrypted_data = cipher.update(encrypted_data)
201
+ decrypted_data << cipher.final
202
+
203
+ message = Messages::Metadata.verify(decrypted_data, purpose)
204
+ @serializer.load(message) if message
205
+ rescue OpenSSLCipherError, TypeError, ArgumentError
206
+ raise InvalidMessage
207
+ end
89
208
 
90
- decrypted_data = cipher.update(encrypted_data)
91
- decrypted_data << cipher.final
209
+ def new_cipher
210
+ OpenSSL::Cipher.new(@cipher)
211
+ end
92
212
 
93
- @serializer.load(decrypted_data)
94
- rescue OpenSSLCipherError, TypeError
95
- raise InvalidMessage
96
- end
213
+ def verifier
214
+ @verifier
215
+ end
97
216
 
98
- def new_cipher
99
- OpenSSL::Cipher::Cipher.new(@cipher)
100
- end
217
+ def aead_mode?
218
+ @aead_mode ||= new_cipher.authenticated?
219
+ end
101
220
 
102
- def verifier
103
- @verifier
104
- end
221
+ def resolve_verifier
222
+ if aead_mode?
223
+ NullVerifier
224
+ else
225
+ MessageVerifier.new(@sign_secret || @secret, digest: @digest, serializer: NullSerializer)
226
+ end
227
+ end
105
228
  end
106
229
  end