activesupport 5.2.7 → 6.0.0.beta1

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +182 -566
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support/backtrace_cleaner.rb +23 -0
  6. data/lib/active_support/cache/file_store.rb +19 -12
  7. data/lib/active_support/cache/mem_cache_store.rb +16 -2
  8. data/lib/active_support/cache/memory_store.rb +5 -0
  9. data/lib/active_support/cache/null_store.rb +5 -0
  10. data/lib/active_support/cache/redis_cache_store.rb +39 -20
  11. data/lib/active_support/cache.rb +40 -18
  12. data/lib/active_support/callbacks.rb +16 -5
  13. data/lib/active_support/configurable.rb +4 -8
  14. data/lib/active_support/core_ext/array/extract.rb +21 -0
  15. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  16. data/lib/active_support/core_ext/array.rb +1 -1
  17. data/lib/active_support/core_ext/class/attribute.rb +1 -1
  18. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  19. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  20. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -17
  21. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  22. data/lib/active_support/core_ext/enumerable.rb +71 -67
  23. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  24. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  25. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  26. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  27. data/lib/active_support/core_ext/hash.rb +0 -2
  28. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  29. data/lib/active_support/core_ext/load_error.rb +1 -1
  30. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -5
  31. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -14
  32. data/lib/active_support/core_ext/module/delegation.rb +27 -7
  33. data/lib/active_support/core_ext/module/introspection.rb +37 -13
  34. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  35. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  36. data/lib/active_support/core_ext/module.rb +0 -1
  37. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  38. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  39. data/lib/active_support/core_ext/numeric.rb +0 -1
  40. data/lib/active_support/core_ext/object/blank.rb +1 -2
  41. data/lib/active_support/core_ext/object/duplicable.rb +5 -2
  42. data/lib/active_support/core_ext/object/json.rb +1 -0
  43. data/lib/active_support/core_ext/object/try.rb +15 -7
  44. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  45. data/lib/active_support/core_ext/range/compare_range.rb +1 -1
  46. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  47. data/lib/active_support/core_ext/range/include_range.rb +6 -0
  48. data/lib/active_support/core_ext/regexp.rb +0 -4
  49. data/lib/active_support/core_ext/securerandom.rb +23 -3
  50. data/lib/active_support/core_ext/string/access.rb +8 -0
  51. data/lib/active_support/core_ext/string/filters.rb +41 -0
  52. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  53. data/lib/active_support/core_ext/string/output_safety.rb +16 -5
  54. data/lib/active_support/core_ext/string/strip.rb +3 -1
  55. data/lib/active_support/core_ext/uri.rb +1 -0
  56. data/lib/active_support/current_attributes.rb +2 -0
  57. data/lib/active_support/dependencies.rb +28 -11
  58. data/lib/active_support/deprecation/behaviors.rb +1 -1
  59. data/lib/active_support/deprecation/method_wrappers.rb +4 -5
  60. data/lib/active_support/deprecation/proxy_wrappers.rb +0 -2
  61. data/lib/active_support/deprecation.rb +1 -1
  62. data/lib/active_support/descendants_tracker.rb +6 -5
  63. data/lib/active_support/duration/iso8601_parser.rb +2 -3
  64. data/lib/active_support/duration/iso8601_serializer.rb +3 -4
  65. data/lib/active_support/duration.rb +12 -14
  66. data/lib/active_support/encrypted_configuration.rb +0 -4
  67. data/lib/active_support/evented_file_update_checker.rb +25 -7
  68. data/lib/active_support/execution_wrapper.rb +14 -16
  69. data/lib/active_support/gem_version.rb +4 -4
  70. data/lib/active_support/hash_with_indifferent_access.rb +16 -28
  71. data/lib/active_support/i18n.rb +1 -0
  72. data/lib/active_support/i18n_railtie.rb +8 -1
  73. data/lib/active_support/inflector/inflections.rb +1 -4
  74. data/lib/active_support/inflector/methods.rb +15 -27
  75. data/lib/active_support/inflector/transliterate.rb +6 -6
  76. data/lib/active_support/json/decoding.rb +23 -23
  77. data/lib/active_support/json/encoding.rb +6 -2
  78. data/lib/active_support/key_generator.rb +0 -32
  79. data/lib/active_support/lazy_load_hooks.rb +5 -1
  80. data/lib/active_support/locale/en.rb +31 -0
  81. data/lib/active_support/log_subscriber.rb +31 -8
  82. data/lib/active_support/logger.rb +0 -15
  83. data/lib/active_support/logger_silence.rb +28 -12
  84. data/lib/active_support/logger_thread_safe_level.rb +27 -6
  85. data/lib/active_support/message_encryptor.rb +2 -4
  86. data/lib/active_support/message_verifier.rb +2 -2
  87. data/lib/active_support/multibyte/chars.rb +29 -48
  88. data/lib/active_support/multibyte/unicode.rb +44 -281
  89. data/lib/active_support/notifications/fanout.rb +42 -4
  90. data/lib/active_support/notifications/instrumenter.rb +73 -2
  91. data/lib/active_support/notifications.rb +32 -4
  92. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
  93. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
  94. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
  95. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
  96. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  97. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
  98. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
  99. data/lib/active_support/number_helper.rb +7 -0
  100. data/lib/active_support/ordered_options.rb +1 -1
  101. data/lib/active_support/parameter_filter.rb +124 -0
  102. data/lib/active_support/rails.rb +0 -6
  103. data/lib/active_support/reloader.rb +5 -6
  104. data/lib/active_support/subscriber.rb +16 -26
  105. data/lib/active_support/tagged_logging.rb +13 -4
  106. data/lib/active_support/test_case.rb +91 -0
  107. data/lib/active_support/testing/assertions.rb +15 -1
  108. data/lib/active_support/testing/deprecation.rb +0 -1
  109. data/lib/active_support/testing/file_fixtures.rb +2 -0
  110. data/lib/active_support/testing/isolation.rb +2 -2
  111. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  112. data/lib/active_support/testing/parallelization.rb +109 -0
  113. data/lib/active_support/testing/stream.rb +1 -1
  114. data/lib/active_support/testing/time_helpers.rb +7 -7
  115. data/lib/active_support/time_with_zone.rb +15 -5
  116. data/lib/active_support/values/time_zone.rb +12 -7
  117. data/lib/active_support/xml_mini/jdom.rb +2 -2
  118. data/lib/active_support/xml_mini/libxml.rb +2 -2
  119. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  120. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  121. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  122. data/lib/active_support/xml_mini/rexml.rb +2 -2
  123. data/lib/active_support/xml_mini.rb +2 -9
  124. data/lib/active_support.rb +1 -1
  125. metadata +12 -10
  126. data/lib/active_support/core_ext/digest.rb +0 -3
  127. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -18,8 +18,8 @@ module ActiveSupport
18
18
  super
19
19
  end
20
20
 
21
- def subscribe(pattern = nil, callable = nil, &block)
22
- subscriber = Subscribers.new(pattern, callable || block)
21
+ def subscribe(pattern = nil, block = Proc.new)
22
+ subscriber = Subscribers.new pattern, block
23
23
  synchronize do
24
24
  @subscribers << subscriber
25
25
  @listeners_for.clear
@@ -70,12 +70,29 @@ module ActiveSupport
70
70
 
71
71
  module Subscribers # :nodoc:
72
72
  def self.new(pattern, listener)
73
+ subscriber_class = Timed
74
+
73
75
  if listener.respond_to?(:start) && listener.respond_to?(:finish)
74
- subscriber = Evented.new pattern, listener
76
+ subscriber_class = Evented
75
77
  else
76
- subscriber = Timed.new pattern, listener
78
+ # Doing all this to detect a block like `proc { |x| }` vs
79
+ # `proc { |*x| }` or `proc { |**x| }`
80
+ if listener.respond_to?(:parameters)
81
+ params = listener.parameters
82
+ if params.length == 1 && params.first.first == :opt
83
+ subscriber_class = EventObject
84
+ end
85
+ end
77
86
  end
78
87
 
88
+ wrap_all pattern, subscriber_class.new(pattern, listener)
89
+ end
90
+
91
+ def self.event_object_subscriber(pattern, block)
92
+ wrap_all pattern, EventObject.new(pattern, block)
93
+ end
94
+
95
+ def self.wrap_all(pattern, subscriber)
79
96
  unless pattern
80
97
  AllMessages.new(subscriber)
81
98
  else
@@ -130,6 +147,27 @@ module ActiveSupport
130
147
  end
131
148
  end
132
149
 
150
+ class EventObject < Evented
151
+ def start(name, id, payload)
152
+ stack = Thread.current[:_event_stack] ||= []
153
+ event = build_event name, id, payload
154
+ event.start!
155
+ stack.push event
156
+ end
157
+
158
+ def finish(name, id, payload)
159
+ stack = Thread.current[:_event_stack]
160
+ event = stack.pop
161
+ event.finish!
162
+ @delegate.call event
163
+ end
164
+
165
+ private
166
+ def build_event(name, id, payload)
167
+ ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
168
+ end
169
+ end
170
+
133
171
  class AllMessages # :nodoc:
134
172
  def initialize(delegate)
135
173
  @delegate = delegate
@@ -52,8 +52,13 @@ module ActiveSupport
52
52
  end
53
53
 
54
54
  class Event
55
- attr_reader :name, :time, :transaction_id, :payload, :children
56
- attr_accessor :end
55
+ attr_reader :name, :time, :end, :transaction_id, :payload, :children
56
+
57
+ def self.clock_gettime_supported? # :nodoc:
58
+ defined?(Process::CLOCK_PROCESS_CPUTIME_ID) &&
59
+ !Gem.win_platform?
60
+ end
61
+ private_class_method :clock_gettime_supported?
57
62
 
58
63
  def initialize(name, start, ending, transaction_id, payload)
59
64
  @name = name
@@ -63,6 +68,47 @@ module ActiveSupport
63
68
  @end = ending
64
69
  @children = []
65
70
  @duration = nil
71
+ @cpu_time_start = nil
72
+ @cpu_time_finish = nil
73
+ @allocation_count_start = 0
74
+ @allocation_count_finish = 0
75
+ end
76
+
77
+ # Record information at the time this event starts
78
+ def start!
79
+ @time = now
80
+ @cpu_time_start = now_cpu
81
+ @allocation_count_start = now_allocations
82
+ end
83
+
84
+ # Record information at the time this event finishes
85
+ def finish!
86
+ @cpu_time_finish = now_cpu
87
+ @end = now
88
+ @allocation_count_finish = now_allocations
89
+ end
90
+
91
+ def end=(ending)
92
+ ActiveSupport::Deprecation.deprecation_warning(:end=, :finish!)
93
+ @end = ending
94
+ end
95
+
96
+ # Returns the CPU time (in milliseconds) passed since the call to
97
+ # +start!+ and the call to +finish!+
98
+ def cpu_time
99
+ (@cpu_time_finish - @cpu_time_start) * 1000
100
+ end
101
+
102
+ # Returns the idle time time (in milliseconds) passed since the call to
103
+ # +start!+ and the call to +finish!+
104
+ def idle_time
105
+ duration - cpu_time
106
+ end
107
+
108
+ # Returns the number of allocations made since the call to +start!+ and
109
+ # the call to +finish!+
110
+ def allocations
111
+ @allocation_count_finish - @allocation_count_start
66
112
  end
67
113
 
68
114
  # Returns the difference in milliseconds between when the execution of the
@@ -88,6 +134,31 @@ module ActiveSupport
88
134
  def parent_of?(event)
89
135
  @children.include? event
90
136
  end
137
+
138
+ private
139
+ def now
140
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
141
+ end
142
+
143
+ if clock_gettime_supported?
144
+ def now_cpu
145
+ Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
146
+ end
147
+ else
148
+ def now_cpu
149
+ 0
150
+ end
151
+ end
152
+
153
+ if defined?(JRUBY_VERSION)
154
+ def now_allocations
155
+ 0
156
+ end
157
+ else
158
+ def now_allocations
159
+ GC.stat :total_allocated_objects
160
+ end
161
+ end
91
162
  end
92
163
  end
93
164
  end
@@ -34,7 +34,7 @@ module ActiveSupport
34
34
  # name # => String, name of the event (such as 'render' from above)
35
35
  # start # => Time, when the instrumented block started execution
36
36
  # finish # => Time, when the instrumented block ended execution
37
- # id # => String, unique ID for this notification
37
+ # id # => String, unique ID for the instrumenter that fired the event
38
38
  # payload # => Hash, the payload
39
39
  # end
40
40
  #
@@ -59,7 +59,7 @@ module ActiveSupport
59
59
  # event.payload # => { extra: :information }
60
60
  #
61
61
  # The block in the <tt>subscribe</tt> call gets the name of the event, start
62
- # timestamp, end timestamp, a string with a unique identifier for that event
62
+ # timestamp, end timestamp, a string with a unique identifier for that event's instrumenter
63
63
  # (something like "535801666f04d0298cd6"), and a hash with the payload, in
64
64
  # that order.
65
65
  #
@@ -67,9 +67,12 @@ module ActiveSupport
67
67
  # have a key <tt>:exception</tt> with an array of two elements as value: a string with
68
68
  # the name of the exception class, and the exception message.
69
69
  # The <tt>:exception_object</tt> key of the payload will have the exception
70
- # itself as the value.
70
+ # itself as the value:
71
71
  #
72
- # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
72
+ # event.payload[:exception] # => ["ArgumentError", "Invalid value"]
73
+ # event.payload[:exception_object] # => #<ArgumentError: Invalid value>
74
+ #
75
+ # As the earlier example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
73
76
  # is able to take the arguments as they come and provide an object-oriented
74
77
  # interface to that data.
75
78
  #
@@ -171,6 +174,31 @@ module ActiveSupport
171
174
  end
172
175
  end
173
176
 
177
+ # Subscribe to a given event name with the passed +block+.
178
+ #
179
+ # You can subscribe to events by passing a String to match exact event
180
+ # names, or by passing a Regexp to match all events that match a pattern.
181
+ #
182
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
183
+ # @event = ActiveSupport::Notifications::Event.new(*args)
184
+ # end
185
+ #
186
+ # The +block+ will receive five parameters with information about the event:
187
+ #
188
+ # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
189
+ # name # => String, name of the event (such as 'render' from above)
190
+ # start # => Time, when the instrumented block started execution
191
+ # finish # => Time, when the instrumented block ended execution
192
+ # id # => String, unique ID for the instrumenter that fired the event
193
+ # payload # => Hash, the payload
194
+ # end
195
+ #
196
+ # If the block passed to the method only takes one parameter,
197
+ # it will yield an event object to the block:
198
+ #
199
+ # ActiveSupport::Notifications.subscribe(/render/) do |event|
200
+ # @event = event
201
+ # end
174
202
  def subscribe(*args, &block)
175
203
  notifier.subscribe(*args, &block)
176
204
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/numeric/inquiry"
3
+ require "active_support/number_helper/number_converter"
4
4
 
5
5
  module ActiveSupport
6
6
  module NumberHelper
@@ -17,7 +17,7 @@ module ActiveSupport
17
17
  end
18
18
 
19
19
  rounded_number = NumberToRoundedConverter.convert(number, options)
20
- format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, options[:unit])
20
+ format.gsub("%n", rounded_number).gsub("%u", options[:unit])
21
21
  end
22
22
 
23
23
  private
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToDelimitedConverter < NumberConverter #:nodoc:
@@ -14,7 +16,7 @@ module ActiveSupport
14
16
  private
15
17
 
16
18
  def parts
17
- left, right = number.to_s.split(".".freeze)
19
+ left, right = number.to_s.split(".")
18
20
  left.gsub!(delimiter_pattern) do |digit_to_delimit|
19
21
  "#{digit_to_delimit}#{options[:delimiter]}"
20
22
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToHumanConverter < NumberConverter # :nodoc:
@@ -25,7 +27,7 @@ module ActiveSupport
25
27
 
26
28
  rounded_number = NumberToRoundedConverter.convert(number, options)
27
29
  unit = determine_unit(units, exponent)
28
- format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, unit).strip
30
+ format.gsub("%n", rounded_number).gsub("%u", unit).strip
29
31
  end
30
32
 
31
33
  private
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToHumanSizeConverter < NumberConverter #:nodoc:
@@ -22,7 +24,7 @@ module ActiveSupport
22
24
  human_size = number / (base**exponent)
23
25
  number_to_format = NumberToRoundedConverter.convert(human_size, options)
24
26
  end
25
- conversion_format.gsub("%n".freeze, number_to_format).gsub("%u".freeze, unit)
27
+ conversion_format.gsub("%n", number_to_format).gsub("%u", unit)
26
28
  end
27
29
 
28
30
  private
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToPercentageConverter < NumberConverter # :nodoc:
@@ -7,7 +9,7 @@ module ActiveSupport
7
9
 
8
10
  def convert
9
11
  rounded_number = NumberToRoundedConverter.convert(number, options)
10
- options[:format].gsub("%n".freeze, rounded_number)
12
+ options[:format].gsub("%n", rounded_number)
11
13
  end
12
14
  end
13
15
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToPhoneConverter < NumberConverter #:nodoc:
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/number_helper/number_converter"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  class NumberToRoundedConverter < NumberConverter # :nodoc:
@@ -20,9 +22,9 @@ module ActiveSupport
20
22
  formatted_string =
21
23
  if BigDecimal === rounded_number && rounded_number.finite?
22
24
  s = rounded_number.to_s("F")
23
- s << "0".freeze * precision
24
- a, b = s.split(".".freeze, 2)
25
- a << ".".freeze
25
+ s << "0" * precision
26
+ a, b = s.split(".", 2)
27
+ a << "."
26
28
  a << b[0, precision]
27
29
  else
28
30
  "%00.#{precision}f" % rounded_number
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/dependencies/autoload"
4
+
3
5
  module ActiveSupport
4
6
  module NumberHelper
5
7
  extend ActiveSupport::Autoload
@@ -85,6 +87,9 @@ module ActiveSupport
85
87
  # number given by <tt>:format</tt>). Accepts the same fields
86
88
  # than <tt>:format</tt>, except <tt>%n</tt> is here the
87
89
  # absolute value of the number.
90
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
91
+ # insignificant zeros after the decimal separator (defaults to
92
+ # +false+).
88
93
  #
89
94
  # ==== Examples
90
95
  #
@@ -100,6 +105,8 @@ module ActiveSupport
100
105
  # # => "&pound;1234567890,50"
101
106
  # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
102
107
  # # => "1234567890,50 &pound;"
108
+ # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
109
+ # # => "$1,234,567,890.5"
103
110
  def number_to_currency(number, options = {})
104
111
  NumberToCurrencyConverter.convert(number, options)
105
112
  end
@@ -39,7 +39,7 @@ module ActiveSupport
39
39
  end
40
40
 
41
41
  def method_missing(name, *args)
42
- name_string = name.to_s.dup
42
+ name_string = name.to_s
43
43
  if name_string.chomp!("=")
44
44
  self[name_string] = args.first
45
45
  else
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/duplicable"
4
+ require "active_support/core_ext/array/extract"
5
+
6
+ module ActiveSupport
7
+ # +ParameterFilter+ allows you to specify keys for sensitive data from
8
+ # hash-like object and replace corresponding value. Filtering only certain
9
+ # sub-keys from a hash is possible by using the dot notation:
10
+ # 'credit_card.number'. If a proc is given, each key and value of a hash and
11
+ # all sub-hashes are passed to it, where the value or the key can be replaced
12
+ # using String#replace or similar methods.
13
+ #
14
+ # ActiveSupport::ParameterFilter.new([:password])
15
+ # => replaces the value to all keys matching /password/i with "[FILTERED]"
16
+ #
17
+ # ActiveSupport::ParameterFilter.new([:foo, "bar"])
18
+ # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
19
+ #
20
+ # ActiveSupport::ParameterFilter.new(["credit_card.code"])
21
+ # => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
22
+ # change { file: { code: "xxxx"} }
23
+ #
24
+ # ActiveSupport::ParameterFilter.new([-> (k, v) do
25
+ # v.reverse! if k =~ /secret/i
26
+ # end])
27
+ # => reverses the value to all keys matching /secret/i
28
+ class ParameterFilter
29
+ FILTERED = "[FILTERED]" # :nodoc:
30
+
31
+ # Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
32
+ # Other types of filters are treated as +String+ using +to_s+.
33
+ # For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
34
+ #
35
+ # ==== Options
36
+ #
37
+ # * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
38
+ def initialize(filters = [], mask: FILTERED)
39
+ @filters = filters
40
+ @mask = mask
41
+ end
42
+
43
+ # Mask value of +params+ if key matches one of filters.
44
+ def filter(params)
45
+ compiled_filter.call(params)
46
+ end
47
+
48
+ # Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
49
+ def filter_param(key, value)
50
+ @filters.empty? ? value : compiled_filter.value_for_key(key, value)
51
+ end
52
+
53
+ private
54
+
55
+ def compiled_filter
56
+ @compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
57
+ end
58
+
59
+ class CompiledFilter # :nodoc:
60
+ def self.compile(filters, mask:)
61
+ return lambda { |params| params.dup } if filters.empty?
62
+
63
+ strings, regexps, blocks = [], [], []
64
+
65
+ filters.each do |item|
66
+ case item
67
+ when Proc
68
+ blocks << item
69
+ when Regexp
70
+ regexps << item
71
+ else
72
+ strings << Regexp.escape(item.to_s)
73
+ end
74
+ end
75
+
76
+ deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.") }
77
+ deep_strings = strings.extract! { |s| s.include?("\\.") }
78
+
79
+ regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
80
+ deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
81
+
82
+ new regexps, deep_regexps, blocks, mask: mask
83
+ end
84
+
85
+ attr_reader :regexps, :deep_regexps, :blocks
86
+
87
+ def initialize(regexps, deep_regexps, blocks, mask:)
88
+ @regexps = regexps
89
+ @deep_regexps = deep_regexps.any? ? deep_regexps : nil
90
+ @blocks = blocks
91
+ @mask = mask
92
+ end
93
+
94
+ def call(params, parents = [], original_params = params)
95
+ filtered_params = params.class.new
96
+
97
+ params.each do |key, value|
98
+ filtered_params[key] = value_for_key(key, value, parents, original_params)
99
+ end
100
+
101
+ filtered_params
102
+ end
103
+
104
+ def value_for_key(key, value, parents = [], original_params = nil)
105
+ parents.push(key) if deep_regexps
106
+ if regexps.any? { |r| r.match?(key) }
107
+ value = @mask
108
+ elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
109
+ value = @mask
110
+ elsif value.is_a?(Hash)
111
+ value = call(value, parents, original_params)
112
+ elsif value.is_a?(Array)
113
+ value = value.map { |v| v.is_a?(Hash) ? call(v, parents, original_params) : v }
114
+ elsif blocks.any?
115
+ key = key.dup if key.duplicable?
116
+ value = value.dup if value.duplicable?
117
+ blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
118
+ end
119
+ parents.pop if deep_regexps
120
+ value
121
+ end
122
+ end
123
+ end
124
+ end
@@ -27,9 +27,3 @@ require "active_support/core_ext/module/delegation"
27
27
 
28
28
  # Defines ActiveSupport::Deprecation.
29
29
  require "active_support/deprecation"
30
-
31
- # Defines Regexp#match?.
32
- #
33
- # This should be removed when Rails needs Ruby 2.4 or later, and the require
34
- # added where other Regexp extensions are being used (easy to grep).
35
- require "active_support/core_ext/regexp"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/execution_wrapper"
4
+ require "active_support/executor"
4
5
 
5
6
  module ActiveSupport
6
7
  #--
@@ -49,17 +50,15 @@ module ActiveSupport
49
50
  def self.reload!
50
51
  executor.wrap do
51
52
  new.tap do |instance|
52
- begin
53
- instance.run!
54
- ensure
55
- instance.complete!
56
- end
53
+ instance.run!
54
+ ensure
55
+ instance.complete!
57
56
  end
58
57
  end
59
58
  prepare!
60
59
  end
61
60
 
62
- def self.run!(reset: false) # :nodoc:
61
+ def self.run! # :nodoc:
63
62
  if check!
64
63
  super
65
64
  else
@@ -54,25 +54,20 @@ module ActiveSupport
54
54
  @@subscribers ||= []
55
55
  end
56
56
 
57
- # TODO Change this to private once we've dropped Ruby 2.2 support.
58
- # Workaround for Ruby 2.2 "private attribute?" warning.
59
- protected
60
-
61
- attr_reader :subscriber, :notifier, :namespace
62
-
63
57
  private
58
+ attr_reader :subscriber, :notifier, :namespace
64
59
 
65
- def add_event_subscriber(event) # :doc:
66
- return if %w{ start finish }.include?(event.to_s)
60
+ def add_event_subscriber(event) # :doc:
61
+ return if %w{ start finish }.include?(event.to_s)
67
62
 
68
- pattern = "#{event}.#{namespace}"
63
+ pattern = "#{event}.#{namespace}"
69
64
 
70
- # Don't add multiple subscribers (eg. if methods are redefined).
71
- return if subscriber.patterns.include?(pattern)
65
+ # Don't add multiple subscribers (eg. if methods are redefined).
66
+ return if subscriber.patterns.include?(pattern)
72
67
 
73
- subscriber.patterns << pattern
74
- notifier.subscribe(pattern, subscriber)
75
- end
68
+ subscriber.patterns << pattern
69
+ notifier.subscribe(pattern, subscriber)
70
+ end
76
71
  end
77
72
 
78
73
  attr_reader :patterns # :nodoc:
@@ -84,32 +79,27 @@ module ActiveSupport
84
79
  end
85
80
 
86
81
  def start(name, id, payload)
87
- e = ActiveSupport::Notifications::Event.new(name, now, nil, id, payload)
82
+ event = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload)
83
+ event.start!
88
84
  parent = event_stack.last
89
- parent << e if parent
85
+ parent << event if parent
90
86
 
91
- event_stack.push e
87
+ event_stack.push event
92
88
  end
93
89
 
94
90
  def finish(name, id, payload)
95
- finished = now
96
- event = event_stack.pop
97
- event.end = finished
91
+ event = event_stack.pop
92
+ event.finish!
98
93
  event.payload.merge!(payload)
99
94
 
100
- method = name.split(".".freeze).first
95
+ method = name.split(".").first
101
96
  send(method, event)
102
97
  end
103
98
 
104
99
  private
105
-
106
100
  def event_stack
107
101
  SubscriberQueueRegistry.instance.get_queue(@queue_key)
108
102
  end
109
-
110
- def now
111
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
112
- end
113
103
  end
114
104
 
115
105
  # This is a registry for all the event stacks kept for subscribers.
@@ -46,21 +46,30 @@ module ActiveSupport
46
46
 
47
47
  def current_tags
48
48
  # We use our object ID here to avoid conflicting with other instances
49
- thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}".freeze
49
+ thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
50
50
  Thread.current[thread_key] ||= []
51
51
  end
52
52
 
53
53
  def tags_text
54
54
  tags = current_tags
55
- if tags.any?
55
+ if tags.one?
56
+ "[#{tags[0]}] "
57
+ elsif tags.any?
56
58
  tags.collect { |tag| "[#{tag}] " }.join
57
59
  end
58
60
  end
59
61
  end
60
62
 
61
63
  def self.new(logger)
62
- # Ensure we set a default formatter so we aren't extending nil!
63
- logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
64
+ logger = logger.dup
65
+
66
+ if logger.formatter
67
+ logger.formatter = logger.formatter.dup
68
+ else
69
+ # Ensure we set a default formatter so we aren't extending nil!
70
+ logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
71
+ end
72
+
64
73
  logger.formatter.extend Formatter
65
74
  logger.extend(self)
66
75
  end