activesupport 6.0.0.beta3 → 6.0.0.rc1

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +149 -1
  3. data/README.rdoc +2 -1
  4. data/lib/active_support.rb +1 -0
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/backtrace_cleaner.rb +5 -1
  7. data/lib/active_support/cache.rb +5 -5
  8. data/lib/active_support/cache/redis_cache_store.rb +8 -5
  9. data/lib/active_support/concern.rb +24 -1
  10. data/lib/active_support/configurable.rb +3 -3
  11. data/lib/active_support/core_ext/array/access.rb +18 -6
  12. data/lib/active_support/core_ext/class/attribute.rb +10 -15
  13. data/lib/active_support/core_ext/enumerable.rb +24 -4
  14. data/lib/active_support/core_ext/hash/except.rb +1 -1
  15. data/lib/active_support/core_ext/module/attribute_accessors.rb +5 -5
  16. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +5 -5
  17. data/lib/active_support/core_ext/range/compare_range.rb +21 -12
  18. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  19. data/lib/active_support/core_ext/string/output_safety.rb +49 -4
  20. data/lib/active_support/core_ext/time/calculations.rb +1 -2
  21. data/lib/active_support/dependencies.rb +5 -0
  22. data/lib/active_support/dependencies/zeitwerk_integration.rb +22 -18
  23. data/lib/active_support/deprecation/method_wrappers.rb +7 -18
  24. data/lib/active_support/descendants_tracker.rb +52 -6
  25. data/lib/active_support/duration.rb +0 -1
  26. data/lib/active_support/evented_file_update_checker.rb +3 -1
  27. data/lib/active_support/gem_version.rb +1 -1
  28. data/lib/active_support/hash_with_indifferent_access.rb +6 -3
  29. data/lib/active_support/i18n_railtie.rb +2 -1
  30. data/lib/active_support/inflector/transliterate.rb +16 -13
  31. data/lib/active_support/notifications/fanout.rb +4 -4
  32. data/lib/active_support/notifications/instrumenter.rb +8 -8
  33. data/lib/active_support/security_utils.rb +1 -1
  34. data/lib/active_support/subscriber.rb +55 -6
  35. data/lib/active_support/testing/parallelization.rb +13 -2
  36. metadata +10 -9
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "weakref"
4
+
3
5
  module ActiveSupport
4
6
  # This module provides an internal implementation to track descendants
5
7
  # which is faster than iterating through ObjectSpace.
@@ -8,7 +10,8 @@ module ActiveSupport
8
10
 
9
11
  class << self
10
12
  def direct_descendants(klass)
11
- @@direct_descendants[klass] || []
13
+ descendants = @@direct_descendants[klass]
14
+ descendants ? descendants.to_a : []
12
15
  end
13
16
 
14
17
  def descendants(klass)
@@ -20,10 +23,10 @@ module ActiveSupport
20
23
  def clear
21
24
  if defined? ActiveSupport::Dependencies
22
25
  @@direct_descendants.each do |klass, descendants|
23
- if ActiveSupport::Dependencies.autoloaded?(klass)
26
+ if Dependencies.autoloaded?(klass)
24
27
  @@direct_descendants.delete(klass)
25
28
  else
26
- descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
29
+ descendants.reject! { |v| Dependencies.autoloaded?(v) }
27
30
  end
28
31
  end
29
32
  else
@@ -34,15 +37,17 @@ module ActiveSupport
34
37
  # This is the only method that is not thread safe, but is only ever called
35
38
  # during the eager loading phase.
36
39
  def store_inherited(klass, descendant)
37
- (@@direct_descendants[klass] ||= []) << descendant
40
+ (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
38
41
  end
39
42
 
40
43
  private
41
44
 
42
45
  def accumulate_descendants(klass, acc)
43
46
  if direct_descendants = @@direct_descendants[klass]
44
- acc.concat(direct_descendants)
45
- direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
47
+ direct_descendants.each do |direct_descendant|
48
+ acc << direct_descendant
49
+ accumulate_descendants(direct_descendant, acc)
50
+ end
46
51
  end
47
52
  end
48
53
  end
@@ -59,5 +64,46 @@ module ActiveSupport
59
64
  def descendants
60
65
  DescendantsTracker.descendants(self)
61
66
  end
67
+
68
+ # DescendantsArray is an array that contains weak references to classes.
69
+ class DescendantsArray # :nodoc:
70
+ include Enumerable
71
+
72
+ def initialize
73
+ @refs = []
74
+ end
75
+
76
+ def initialize_copy(orig)
77
+ @refs = @refs.dup
78
+ end
79
+
80
+ def <<(klass)
81
+ cleanup!
82
+ @refs << WeakRef.new(klass)
83
+ end
84
+
85
+ def each
86
+ @refs.each do |ref|
87
+ yield ref.__getobj__
88
+ rescue WeakRef::RefError
89
+ end
90
+ end
91
+
92
+ def refs_size
93
+ @refs.size
94
+ end
95
+
96
+ def cleanup!
97
+ @refs.delete_if { |ref| !ref.weakref_alive? }
98
+ end
99
+
100
+ def reject!
101
+ @refs.reject! do |ref|
102
+ yield ref.__getobj__
103
+ rescue WeakRef::RefError
104
+ true
105
+ end
106
+ end
107
+ end
62
108
  end
63
109
  end
@@ -4,7 +4,6 @@ require "active_support/core_ext/array/conversions"
4
4
  require "active_support/core_ext/module/delegation"
5
5
  require "active_support/core_ext/object/acts_like"
6
6
  require "active_support/core_ext/string/filters"
7
- require "active_support/deprecation"
8
7
 
9
8
  module ActiveSupport
10
9
  # Provides accurate date and time measurements using Date#advance and
@@ -131,7 +131,9 @@ module ActiveSupport
131
131
  ext = @ph.normalize_extension(file.extname)
132
132
 
133
133
  file.dirname.ascend do |dir|
134
- if @dirs.fetch(dir, []).include?(ext)
134
+ matching = @dirs[dir]
135
+
136
+ if matching && (matching.empty? || matching.include?(ext))
135
137
  break true
136
138
  elsif dir == @lcsp || dir.root?
137
139
  break false
@@ -10,7 +10,7 @@ module ActiveSupport
10
10
  MAJOR = 6
11
11
  MINOR = 0
12
12
  TINY = 0
13
- PRE = "beta3"
13
+ PRE = "rc1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -225,8 +225,8 @@ module ActiveSupport
225
225
  # hash[:a] = 'x'
226
226
  # hash[:b] = 'y'
227
227
  # hash.values_at('a', 'b') # => ["x", "y"]
228
- def values_at(*indices)
229
- indices.collect { |key| self[convert_key(key)] }
228
+ def values_at(*keys)
229
+ super(*keys.map { |key| convert_key(key) })
230
230
  end
231
231
 
232
232
  # Returns an array of the values at the specified indices, but also
@@ -239,7 +239,7 @@ module ActiveSupport
239
239
  # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
240
240
  # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
241
241
  def fetch_values(*indices, &block)
242
- indices.collect { |key| fetch(key, &block) }
242
+ super(*indices.map { |key| convert_key(key) }, &block)
243
243
  end
244
244
 
245
245
  # Returns a shallow copy of the hash.
@@ -293,6 +293,9 @@ module ActiveSupport
293
293
  super(convert_key(key))
294
294
  end
295
295
 
296
+ def except(*keys)
297
+ slice(*self.keys - keys.map { |key| convert_key(key) })
298
+ end
296
299
  alias_method :without, :except
297
300
 
298
301
  def stringify_keys!; self end
@@ -97,7 +97,8 @@ module I18n
97
97
  If you desire the default locale to be included in the defaults, please
98
98
  explicitly configure it with `config.i18n.fallbacks.defaults =
99
99
  [I18n.default_locale]` or `config.i18n.fallbacks = [I18n.default_locale,
100
- {...}]`
100
+ {...}]`. If you want to opt-in to the new behavior, use
101
+ `config.i18n.fallbacks.defaults = [nil, {...}]`.
101
102
  MSG
102
103
  args.unshift I18n.default_locale
103
104
  end
@@ -51,19 +51,18 @@ module ActiveSupport
51
51
  #
52
52
  # Now you can have different transliterations for each locale:
53
53
  #
54
- # I18n.locale = :en
55
- # transliterate('Jürgen')
54
+ # transliterate('Jürgen', locale: :en)
56
55
  # # => "Jurgen"
57
56
  #
58
- # I18n.locale = :de
59
- # transliterate('Jürgen')
57
+ # transliterate('Jürgen', locale: :de)
60
58
  # # => "Juergen"
61
- def transliterate(string, replacement = "?")
59
+ def transliterate(string, replacement = "?", locale: nil)
62
60
  raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
63
61
 
64
62
  I18n.transliterate(
65
63
  ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
66
- replacement: replacement
64
+ replacement: replacement,
65
+ locale: locale
67
66
  )
68
67
  end
69
68
 
@@ -75,8 +74,8 @@ module ActiveSupport
75
74
  #
76
75
  # To use a custom separator, override the +separator+ argument.
77
76
  #
78
- # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
79
- # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
77
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
78
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
80
79
  #
81
80
  # To preserve the case of the characters in a string, use the +preserve_case+ argument.
82
81
  #
@@ -85,13 +84,17 @@ module ActiveSupport
85
84
  #
86
85
  # It preserves dashes and underscores unless they are used as separators:
87
86
  #
88
- # parameterize("^très|Jolie__ ") # => "tres-jolie__"
89
- # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
90
- # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
87
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
88
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
89
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
91
90
  #
92
- def parameterize(string, separator: "-", preserve_case: false)
91
+ # If the optional parameter +locale+ is specified,
92
+ # the word will be parameterized as a word of that language.
93
+ # By default, this parameter is set to <tt>nil</tt> and it will use
94
+ # the configured <tt>I18n.locale<tt>.
95
+ def parameterize(string, separator: "-", preserve_case: false, locale: nil)
93
96
  # Replace accented chars with their ASCII equivalents.
94
- parameterized_string = transliterate(string)
97
+ parameterized_string = transliterate(string, locale: locale)
95
98
 
96
99
  # Turn unwanted chars into the separator.
97
100
  parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
@@ -20,8 +20,8 @@ module ActiveSupport
20
20
  super
21
21
  end
22
22
 
23
- def subscribe(pattern = nil, block = Proc.new)
24
- subscriber = Subscribers.new pattern, block
23
+ def subscribe(pattern = nil, callable = nil, &block)
24
+ subscriber = Subscribers.new(pattern, callable || block)
25
25
  synchronize do
26
26
  if String === pattern
27
27
  @string_subscribers[pattern] << subscriber
@@ -180,13 +180,13 @@ module ActiveSupport
180
180
 
181
181
  def start(name, id, payload)
182
182
  timestack = Thread.current[:_timestack] ||= []
183
- timestack.push Time.now
183
+ timestack.push Concurrent.monotonic_time
184
184
  end
185
185
 
186
186
  def finish(name, id, payload)
187
187
  timestack = Thread.current[:_timestack]
188
188
  started = timestack.pop
189
- @delegate.call(name, started, Time.now, id, payload)
189
+ @delegate.call(name, started, Concurrent.monotonic_time, id, payload)
190
190
  end
191
191
  end
192
192
 
@@ -13,14 +13,15 @@ module ActiveSupport
13
13
  @notifier = notifier
14
14
  end
15
15
 
16
- # Instrument the given block by measuring the time taken to execute it
17
- # and publish it. Notice that events get sent even if an error occurs
18
- # in the passed-in block.
16
+ # Given a block, instrument it by measuring the time taken to execute
17
+ # and publish it. Without a block, simply send a message via the
18
+ # notifier. Notice that events get sent even if an error occurs in the
19
+ # passed-in block.
19
20
  def instrument(name, payload = {})
20
21
  # some of the listeners might have state
21
22
  listeners_state = start name, payload
22
23
  begin
23
- yield payload
24
+ yield payload if block_given?
24
25
  rescue Exception => e
25
26
  payload[:exception] = [e.class.name, e.message]
26
27
  payload[:exception_object] = e
@@ -67,9 +68,8 @@ module ActiveSupport
67
68
  @transaction_id = transaction_id
68
69
  @end = ending
69
70
  @children = []
70
- @duration = nil
71
- @cpu_time_start = nil
72
- @cpu_time_finish = nil
71
+ @cpu_time_start = 0
72
+ @cpu_time_finish = 0
73
73
  @allocation_count_start = 0
74
74
  @allocation_count_finish = 0
75
75
  end
@@ -124,7 +124,7 @@ module ActiveSupport
124
124
  #
125
125
  # @event.duration # => 1000.138
126
126
  def duration
127
- @duration ||= 1000.0 * (self.end - time)
127
+ 1000.0 * (self.end - time)
128
128
  end
129
129
 
130
130
  def <<(event)
@@ -24,7 +24,7 @@ module ActiveSupport
24
24
  # The values are first processed by SHA256, so that we don't leak length info
25
25
  # via timing attacks.
26
26
  def secure_compare(a, b)
27
- fixed_length_secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b)) && a == b
27
+ fixed_length_secure_compare(::Digest::SHA256.digest(a), ::Digest::SHA256.digest(b)) && a == b
28
28
  end
29
29
  module_function :secure_compare
30
30
  end
@@ -24,6 +24,10 @@ module ActiveSupport
24
24
  # After configured, whenever a "sql.active_record" notification is published,
25
25
  # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
26
26
  # the +sql+ method.
27
+ #
28
+ # We can detach a subscriber as well:
29
+ #
30
+ # ActiveRecord::StatsSubscriber.detach_from(:active_record)
27
31
  class Subscriber
28
32
  class << self
29
33
  # Attach the subscriber to a namespace.
@@ -40,6 +44,25 @@ module ActiveSupport
40
44
  end
41
45
  end
42
46
 
47
+ # Detach the subscriber from a namespace.
48
+ def detach_from(namespace, notifier = ActiveSupport::Notifications)
49
+ @namespace = namespace
50
+ @subscriber = find_attached_subscriber
51
+ @notifier = notifier
52
+
53
+ return unless subscriber
54
+
55
+ subscribers.delete(subscriber)
56
+
57
+ # Remove event subscribers of all existing methods on the class.
58
+ subscriber.public_methods(false).each do |event|
59
+ remove_event_subscriber(event)
60
+ end
61
+
62
+ # Reset notifier so that event subscribers will not add for new methods added to the class.
63
+ @notifier = nil
64
+ end
65
+
43
66
  # Adds event subscribers for all new methods added to the class.
44
67
  def method_added(event)
45
68
  # Only public methods are added as subscribers, and only if a notifier
@@ -58,15 +81,41 @@ module ActiveSupport
58
81
  attr_reader :subscriber, :notifier, :namespace
59
82
 
60
83
  def add_event_subscriber(event) # :doc:
61
- return if %w{ start finish }.include?(event.to_s)
84
+ return if invalid_event?(event.to_s)
62
85
 
63
- pattern = "#{event}.#{namespace}"
86
+ pattern = prepare_pattern(event)
64
87
 
65
88
  # Don't add multiple subscribers (eg. if methods are redefined).
66
- return if subscriber.patterns.include?(pattern)
89
+ return if pattern_subscribed?(pattern)
90
+
91
+ subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
92
+ end
93
+
94
+ def remove_event_subscriber(event) # :doc:
95
+ return if invalid_event?(event.to_s)
96
+
97
+ pattern = prepare_pattern(event)
98
+
99
+ return unless pattern_subscribed?(pattern)
100
+
101
+ notifier.unsubscribe(subscriber.patterns[pattern])
102
+ subscriber.patterns.delete(pattern)
103
+ end
104
+
105
+ def find_attached_subscriber
106
+ subscribers.find { |attached_subscriber| attached_subscriber.instance_of?(self) }
107
+ end
108
+
109
+ def invalid_event?(event)
110
+ %w{ start finish }.include?(event.to_s)
111
+ end
112
+
113
+ def prepare_pattern(event)
114
+ "#{event}.#{namespace}"
115
+ end
67
116
 
68
- subscriber.patterns << pattern
69
- notifier.subscribe(pattern, subscriber)
117
+ def pattern_subscribed?(pattern)
118
+ subscriber.patterns.key?(pattern)
70
119
  end
71
120
  end
72
121
 
@@ -74,7 +123,7 @@ module ActiveSupport
74
123
 
75
124
  def initialize
76
125
  @queue_key = [self.class.name, object_id].join "-"
77
- @patterns = []
126
+ @patterns = {}
78
127
  super
79
128
  end
80
129
 
@@ -71,7 +71,9 @@ module ActiveSupport
71
71
  fork do
72
72
  DRb.stop_service
73
73
 
74
- after_fork(worker)
74
+ begin
75
+ after_fork(worker)
76
+ rescue => setup_exception; end
75
77
 
76
78
  queue = DRbObject.new_with_uri(@url)
77
79
 
@@ -79,7 +81,11 @@ module ActiveSupport
79
81
  klass = job[0]
80
82
  method = job[1]
81
83
  reporter = job[2]
82
- result = Minitest.run_one_method(klass, method)
84
+ result = klass.with_info_handler reporter do
85
+ Minitest.run_one_method(klass, method)
86
+ end
87
+
88
+ add_setup_exception(result, setup_exception) if setup_exception
83
89
 
84
90
  begin
85
91
  queue.record(reporter, result)
@@ -104,6 +110,11 @@ module ActiveSupport
104
110
  @queue_size.times { @queue << nil }
105
111
  @pool.each { |pid| Process.waitpid pid }
106
112
  end
113
+
114
+ private
115
+ def add_setup_exception(result, setup_exception)
116
+ result.failures.prepend Minitest::UnexpectedError.new(setup_exception)
117
+ end
107
118
  end
108
119
  end
109
120
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activesupport
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.beta3
4
+ version: 6.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-13 00:00:00.000000000 Z
11
+ date: 2019-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -84,20 +84,20 @@ dependencies:
84
84
  requirements:
85
85
  - - "~>"
86
86
  - !ruby/object:Gem::Version
87
- version: '1.3'
87
+ version: '2.1'
88
88
  - - ">="
89
89
  - !ruby/object:Gem::Version
90
- version: 1.3.1
90
+ version: 2.1.4
91
91
  type: :runtime
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
95
  - - "~>"
96
96
  - !ruby/object:Gem::Version
97
- version: '1.3'
97
+ version: '2.1'
98
98
  - - ">="
99
99
  - !ruby/object:Gem::Version
100
- version: 1.3.1
100
+ version: 2.1.4
101
101
  description: A toolkit of support libraries and Ruby core extensions extracted from
102
102
  the Rails framework. Rich support for multibyte strings, internationalization, time
103
103
  zones, and testing.
@@ -110,6 +110,7 @@ files:
110
110
  - MIT-LICENSE
111
111
  - README.rdoc
112
112
  - lib/active_support.rb
113
+ - lib/active_support/actionable_error.rb
113
114
  - lib/active_support/all.rb
114
115
  - lib/active_support/array_inquirer.rb
115
116
  - lib/active_support/backtrace_cleaner.rb
@@ -352,12 +353,12 @@ files:
352
353
  - lib/active_support/xml_mini/nokogiri.rb
353
354
  - lib/active_support/xml_mini/nokogirisax.rb
354
355
  - lib/active_support/xml_mini/rexml.rb
355
- homepage: http://rubyonrails.org
356
+ homepage: https://rubyonrails.org
356
357
  licenses:
357
358
  - MIT
358
359
  metadata:
359
- source_code_uri: https://github.com/rails/rails/tree/v6.0.0.beta3/activesupport
360
- changelog_uri: https://github.com/rails/rails/blob/v6.0.0.beta3/activesupport/CHANGELOG.md
360
+ source_code_uri: https://github.com/rails/rails/tree/v6.0.0.rc1/activesupport
361
+ changelog_uri: https://github.com/rails/rails/blob/v6.0.0.rc1/activesupport/CHANGELOG.md
361
362
  post_install_message:
362
363
  rdoc_options:
363
364
  - "--encoding"