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
@@ -97,23 +97,43 @@ module Enumerable
97
97
  end
98
98
  end
99
99
 
100
+ # Returns a new array that includes the passed elements.
101
+ #
102
+ # [ 1, 2, 3 ].including(4, 5)
103
+ # # => [ 1, 2, 3, 4, 5 ]
104
+ #
105
+ # ["David", "Rafael"].including %w[ Aaron Todd ]
106
+ # # => ["David", "Rafael", "Aaron", "Todd"]
107
+ def including(*elements)
108
+ to_a.including(*elements)
109
+ end
110
+
100
111
  # The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
101
112
  # collection does not include the object.
102
113
  def exclude?(object)
103
114
  !include?(object)
104
115
  end
105
116
 
106
- # Returns a copy of the enumerable without the specified elements.
117
+ # Returns a copy of the enumerable excluding the specified elements.
118
+ #
119
+ # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
120
+ # # => ["David", "Rafael"]
107
121
  #
108
- # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
122
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
109
123
  # # => ["David", "Rafael"]
110
124
  #
111
- # {foo: 1, bar: 2, baz: 3}.without :bar
125
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
112
126
  # # => {foo: 1, baz: 3}
113
- def without(*elements)
127
+ def excluding(*elements)
128
+ elements.flatten!(1)
114
129
  reject { |element| elements.include?(element) }
115
130
  end
116
131
 
132
+ # Alias for #excluding.
133
+ def without(*elements)
134
+ excluding(*elements)
135
+ end
136
+
117
137
  # Convert an enumerable to an array based on the given key.
118
138
  #
119
139
  # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
@@ -10,7 +10,7 @@ class Hash
10
10
  # This is useful for limiting a set of parameters to everything but a few known toggles:
11
11
  # @person.update(params[:person].except(:admin))
12
12
  def except(*keys)
13
- dup.except!(*keys)
13
+ slice(*self.keys - keys)
14
14
  end
15
15
 
16
16
  # Removes the given keys from hash and returns it.
@@ -24,7 +24,7 @@ class Module
24
24
  # end
25
25
  # # => NameError: invalid attribute name: 1_Badname
26
26
  #
27
- # If you want to opt out the creation on the instance reader method, pass
27
+ # To omit the instance reader method, pass
28
28
  # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
29
29
  #
30
30
  # module HairColors
@@ -91,7 +91,7 @@ class Module
91
91
  # Person.new.hair_colors = [:blonde, :red]
92
92
  # HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
93
93
  #
94
- # If you want to opt out the instance writer method, pass
94
+ # To omit the instance writer method, pass
95
95
  # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
96
96
  #
97
97
  # module HairColors
@@ -166,8 +166,8 @@ class Module
166
166
  # Citizen.new.hair_colors << :blue
167
167
  # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
168
168
  #
169
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
170
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
169
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
170
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
171
171
  #
172
172
  # module HairColors
173
173
  # mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
@@ -180,7 +180,7 @@ class Module
180
180
  # Person.new.hair_colors = [:brown] # => NoMethodError
181
181
  # Person.new.hair_colors # => NoMethodError
182
182
  #
183
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
183
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
184
184
  #
185
185
  # module HairColors
186
186
  # mattr_accessor :hair_colors, instance_accessor: false
@@ -25,7 +25,7 @@ class Module
25
25
  # end
26
26
  # # => NameError: invalid attribute name: 1_Badname
27
27
  #
28
- # If you want to opt out of the creation of the instance reader method, pass
28
+ # To omit the instance reader method, pass
29
29
  # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
30
30
  #
31
31
  # class Current
@@ -66,7 +66,7 @@ class Module
66
66
  # Current.user = "DHH"
67
67
  # Thread.current[:attr_Current_user] # => "DHH"
68
68
  #
69
- # If you want to opt out of the creation of the instance writer method, pass
69
+ # To omit the instance writer method, pass
70
70
  # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
71
71
  #
72
72
  # class Current
@@ -118,8 +118,8 @@ class Module
118
118
  # Customer.user # => "Rafael"
119
119
  # Account.user # => "DHH"
120
120
  #
121
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
122
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
121
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
122
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
123
123
  #
124
124
  # class Current
125
125
  # thread_mattr_accessor :user, instance_writer: false, instance_reader: false
@@ -128,7 +128,7 @@ class Module
128
128
  # Current.new.user = "DHH" # => NoMethodError
129
129
  # Current.new.user # => NoMethodError
130
130
  #
131
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
131
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
132
132
  #
133
133
  # class Current
134
134
  # thread_mattr_accessor :user, instance_accessor: false
@@ -3,9 +3,10 @@
3
3
  module ActiveSupport
4
4
  module CompareWithRange
5
5
  # Extends the default Range#=== to support range comparisons.
6
- # (1..5) === (1..5) # => true
7
- # (1..5) === (2..3) # => true
8
- # (1..5) === (2..6) # => false
6
+ # (1..5) === (1..5) # => true
7
+ # (1..5) === (2..3) # => true
8
+ # (1..5) === (1...6) # => true
9
+ # (1..5) === (2..6) # => false
9
10
  #
10
11
  # The native Range#=== behavior is untouched.
11
12
  # ('a'..'f') === ('c') # => true
@@ -13,17 +14,20 @@ module ActiveSupport
13
14
  def ===(value)
14
15
  if value.is_a?(::Range)
15
16
  # 1...10 includes 1..9 but it does not include 1..10.
17
+ # 1..10 includes 1...11 but it does not include 1...12.
16
18
  operator = exclude_end? && !value.exclude_end? ? :< : :<=
17
- super(value.first) && value.last.send(operator, last)
19
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
20
+ super(value.first) && value_max.send(operator, last)
18
21
  else
19
22
  super
20
23
  end
21
24
  end
22
25
 
23
26
  # Extends the default Range#include? to support range comparisons.
24
- # (1..5).include?(1..5) # => true
25
- # (1..5).include?(2..3) # => true
26
- # (1..5).include?(2..6) # => false
27
+ # (1..5).include?(1..5) # => true
28
+ # (1..5).include?(2..3) # => true
29
+ # (1..5).include?(1...6) # => true
30
+ # (1..5).include?(2..6) # => false
27
31
  #
28
32
  # The native Range#include? behavior is untouched.
29
33
  # ('a'..'f').include?('c') # => true
@@ -31,17 +35,20 @@ module ActiveSupport
31
35
  def include?(value)
32
36
  if value.is_a?(::Range)
33
37
  # 1...10 includes 1..9 but it does not include 1..10.
38
+ # 1..10 includes 1...11 but it does not include 1...12.
34
39
  operator = exclude_end? && !value.exclude_end? ? :< : :<=
35
- super(value.first) && value.last.send(operator, last)
40
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
41
+ super(value.first) && value_max.send(operator, last)
36
42
  else
37
43
  super
38
44
  end
39
45
  end
40
46
 
41
47
  # Extends the default Range#cover? to support range comparisons.
42
- # (1..5).cover?(1..5) # => true
43
- # (1..5).cover?(2..3) # => true
44
- # (1..5).cover?(2..6) # => false
48
+ # (1..5).cover?(1..5) # => true
49
+ # (1..5).cover?(2..3) # => true
50
+ # (1..5).cover?(1...6) # => true
51
+ # (1..5).cover?(2..6) # => false
45
52
  #
46
53
  # The native Range#cover? behavior is untouched.
47
54
  # ('a'..'f').cover?('c') # => true
@@ -49,8 +56,10 @@ module ActiveSupport
49
56
  def cover?(value)
50
57
  if value.is_a?(::Range)
51
58
  # 1...10 covers 1..9 but it does not cover 1..10.
59
+ # 1..10 covers 1...11 but it does not cover 1...12.
52
60
  operator = exclude_end? && !value.exclude_end? ? :< : :<=
53
- super(value.first) && value.last.send(operator, last)
61
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
62
+ super(value.first) && value_max.send(operator, last)
54
63
  else
55
64
  super
56
65
  end
@@ -162,6 +162,11 @@ class String
162
162
 
163
163
  # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
164
164
  #
165
+ # If the optional parameter +locale+ is specified,
166
+ # the word will be parameterized as a word of that language.
167
+ # By default, this parameter is set to <tt>nil</tt> and it will use
168
+ # the configured <tt>I18n.locale</tt>.
169
+ #
165
170
  # class Person
166
171
  # def to_param
167
172
  # "#{id}-#{name.parameterize}"
@@ -187,8 +192,8 @@ class String
187
192
  #
188
193
  # <%= link_to(@person.name, person_path) %>
189
194
  # # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
190
- def parameterize(separator: "-", preserve_case: false)
191
- ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
195
+ def parameterize(separator: "-", preserve_case: false, locale: nil)
196
+ ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
192
197
  end
193
198
 
194
199
  # Creates the name of a table like Rails does for models to table names. This method
@@ -135,10 +135,12 @@ module ActiveSupport #:nodoc:
135
135
  class SafeBuffer < String
136
136
  UNSAFE_STRING_METHODS = %w(
137
137
  capitalize chomp chop delete delete_prefix delete_suffix
138
- downcase gsub lstrip next reverse rstrip slice squeeze strip
139
- sub succ swapcase tr tr_s unicode_normalize upcase
138
+ downcase lstrip next reverse rstrip slice squeeze strip
139
+ succ swapcase tr tr_s unicode_normalize upcase
140
140
  )
141
141
 
142
+ UNSAFE_STRING_METHODS_WITH_BACKREF = %w(gsub sub)
143
+
142
144
  alias_method :original_concat, :concat
143
145
  private :original_concat
144
146
 
@@ -199,14 +201,24 @@ module ActiveSupport #:nodoc:
199
201
  super(html_escape_interpolated_argument(value))
200
202
  end
201
203
 
202
- def []=(index, value)
203
- super(index, html_escape_interpolated_argument(value))
204
+ def []=(*args)
205
+ if args.count == 3
206
+ super(args[0], args[1], html_escape_interpolated_argument(args[2]))
207
+ else
208
+ super(args[0], html_escape_interpolated_argument(args[1]))
209
+ end
204
210
  end
205
211
 
206
212
  def +(other)
207
213
  dup.concat(other)
208
214
  end
209
215
 
216
+ def *(*)
217
+ new_safe_buffer = super
218
+ new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
219
+ new_safe_buffer
220
+ end
221
+
210
222
  def %(args)
211
223
  case args
212
224
  when Hash
@@ -249,11 +261,44 @@ module ActiveSupport #:nodoc:
249
261
  end
250
262
  end
251
263
 
264
+ UNSAFE_STRING_METHODS_WITH_BACKREF.each do |unsafe_method|
265
+ if unsafe_method.respond_to?(unsafe_method)
266
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
267
+ def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
268
+ if block # if block
269
+ to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params|
270
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
271
+ block.call(*params) # block.call(*params)
272
+ } # }
273
+ else # else
274
+ to_str.#{unsafe_method}(*args) # to_str.gsub(*args)
275
+ end # end
276
+ end # end
277
+
278
+ def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
279
+ @html_safe = false # @html_safe = false
280
+ if block # if block
281
+ super(*args) { |*params| # super(*args) { |*params|
282
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
283
+ block.call(*params) # block.call(*params)
284
+ } # }
285
+ else # else
286
+ super # super
287
+ end # end
288
+ end # end
289
+ EOT
290
+ end
291
+ end
292
+
252
293
  private
253
294
 
254
295
  def html_escape_interpolated_argument(arg)
255
296
  (!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
256
297
  end
298
+
299
+ def set_block_back_references(block, match_data)
300
+ block.binding.eval("proc { |m| $~ = m }").call(match_data)
301
+ end
257
302
  end
258
303
  end
259
304
 
@@ -170,8 +170,7 @@ class Time
170
170
  options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
171
171
  end
172
172
 
173
- d = to_date.advance(options)
174
- d = d.gregorian if d.julian?
173
+ d = to_date.gregorian.advance(options)
175
174
  time_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
176
175
  seconds_to_advance = \
177
176
  options.fetch(:seconds, 0) +
@@ -70,6 +70,11 @@ module ActiveSupport #:nodoc:
70
70
  # only once. All directories in this set must also be present in +autoload_paths+.
71
71
  mattr_accessor :autoload_once_paths, default: []
72
72
 
73
+ # This is a private set that collects all eager load paths during bootstrap.
74
+ # Useful for Zeitwerk integration. Its public interface is the config.* path
75
+ # accessors of each engine.
76
+ mattr_accessor :_eager_load_paths, default: Set.new
77
+
73
78
  # An array of qualified constant names that have been loaded. Adding a name
74
79
  # to this array will cause it to be unloaded the next time Dependencies are
75
80
  # cleared.
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
3
4
  require "active_support/core_ext/string/inflections"
4
5
 
5
6
  module ActiveSupport
@@ -9,6 +10,8 @@ module ActiveSupport
9
10
  def clear
10
11
  Dependencies.unload_interlock do
11
12
  Rails.autoloaders.main.reload
13
+ rescue Zeitwerk::ReloadingDisabledError
14
+ raise "reloading is disabled because config.cache_classes is true"
12
15
  end
13
16
  end
14
17
 
@@ -21,12 +24,12 @@ module ActiveSupport
21
24
  end
22
25
 
23
26
  def autoloaded_constants
24
- (Rails.autoloaders.main.loaded + Rails.autoloaders.once.loaded).to_a
27
+ Rails.autoloaders.main.unloadable_cpaths
25
28
  end
26
29
 
27
30
  def autoloaded?(object)
28
31
  cpath = object.is_a?(Module) ? object.name : object.to_s
29
- Rails.autoloaders.any? { |autoloader| autoloader.loaded?(cpath) }
32
+ Rails.autoloaders.main.unloadable_cpath?(cpath)
30
33
  end
31
34
 
32
35
  def verbose=(verbose)
@@ -46,42 +49,43 @@ module ActiveSupport
46
49
  end
47
50
 
48
51
  class << self
49
- def take_over
50
- setup_autoloaders
51
- freeze_autoload_paths
52
+ def take_over(enable_reloading:)
53
+ setup_autoloaders(enable_reloading)
54
+ freeze_paths
52
55
  decorate_dependencies
53
56
  end
54
57
 
55
58
  private
56
59
 
57
- def setup_autoloaders
58
- Rails.autoloaders.each do |autoloader|
59
- autoloader.inflector = Inflector
60
- end
61
-
60
+ def setup_autoloaders(enable_reloading)
62
61
  Dependencies.autoload_paths.each do |autoload_path|
63
62
  # Zeitwerk only accepts existing directories in `push_dir` to
64
63
  # prevent misconfigurations.
65
64
  next unless File.directory?(autoload_path)
66
65
 
67
- if autoload_once?(autoload_path)
68
- Rails.autoloaders.once.push_dir(autoload_path)
69
- else
70
- Rails.autoloaders.main.push_dir(autoload_path)
71
- end
66
+ autoloader = \
67
+ autoload_once?(autoload_path) ? Rails.autoloaders.once : Rails.autoloaders.main
68
+
69
+ autoloader.push_dir(autoload_path)
70
+ autoloader.do_not_eager_load(autoload_path) unless eager_load?(autoload_path)
72
71
  end
73
72
 
73
+ Rails.autoloaders.main.enable_reloading if enable_reloading
74
74
  Rails.autoloaders.each(&:setup)
75
75
  end
76
76
 
77
77
  def autoload_once?(autoload_path)
78
- Dependencies.autoload_once_paths.include?(autoload_path) ||
79
- Gem.path.any? { |gem_path| autoload_path.to_s.start_with?(gem_path) }
78
+ Dependencies.autoload_once_paths.include?(autoload_path)
79
+ end
80
+
81
+ def eager_load?(autoload_path)
82
+ Dependencies._eager_load_paths.member?(autoload_path)
80
83
  end
81
84
 
82
- def freeze_autoload_paths
85
+ def freeze_paths
83
86
  Dependencies.autoload_paths.freeze
84
87
  Dependencies.autoload_once_paths.freeze
88
+ Dependencies._eager_load_paths.freeze
85
89
  end
86
90
 
87
91
  def decorate_dependencies
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/array/extract_options"
4
+ require "active_support/core_ext/module/redefine_method"
4
5
 
5
6
  module ActiveSupport
6
7
  class Deprecation
@@ -52,29 +53,17 @@ module ActiveSupport
52
53
  options = method_names.extract_options!
53
54
  deprecator = options.delete(:deprecator) || self
54
55
  method_names += options.keys
55
- mod = Module.new
56
+ mod = nil
56
57
 
57
58
  method_names.each do |method_name|
58
59
  if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name)
59
- aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ""), $1
60
- with_method = "#{aliased_method}_with_deprecation#{punctuation}"
61
- without_method = "#{aliased_method}_without_deprecation#{punctuation}"
62
-
63
- target_module.define_method(with_method) do |*args, &block|
60
+ method = target_module.instance_method(method_name)
61
+ target_module.redefine_method(method_name) do |*args, &block|
64
62
  deprecator.deprecation_warning(method_name, options[method_name])
65
- send(without_method, *args, &block)
66
- end
67
-
68
- target_module.alias_method(without_method, method_name)
69
- target_module.alias_method(method_name, with_method)
70
-
71
- case
72
- when target_module.protected_method_defined?(without_method)
73
- target_module.send(:protected, method_name)
74
- when target_module.private_method_defined?(without_method)
75
- target_module.send(:private, method_name)
63
+ method.bind(self).call(*args, &block)
76
64
  end
77
65
  else
66
+ mod ||= Module.new
78
67
  mod.define_method(method_name) do |*args, &block|
79
68
  deprecator.deprecation_warning(method_name, options[method_name])
80
69
  super(*args, &block)
@@ -82,7 +71,7 @@ module ActiveSupport
82
71
  end
83
72
  end
84
73
 
85
- target_module.prepend(mod) unless mod.instance_methods(false).empty?
74
+ target_module.prepend(mod) if mod
86
75
  end
87
76
  end
88
77
  end