activesupport 6.0.0.beta1 → 6.0.1.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +302 -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/file_store.rb +3 -10
  9. data/lib/active_support/cache/memory_store.rb +4 -2
  10. data/lib/active_support/cache/redis_cache_store.rb +9 -6
  11. data/lib/active_support/concern.rb +24 -1
  12. data/lib/active_support/configurable.rb +3 -3
  13. data/lib/active_support/core_ext/array/access.rb +18 -6
  14. data/lib/active_support/core_ext/class/attribute.rb +10 -15
  15. data/lib/active_support/core_ext/date_and_time/calculations.rb +0 -30
  16. data/lib/active_support/core_ext/digest.rb +3 -0
  17. data/lib/active_support/core_ext/enumerable.rb +24 -4
  18. data/lib/active_support/core_ext/hash.rb +1 -0
  19. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  20. data/lib/active_support/core_ext/hash/except.rb +1 -1
  21. data/lib/active_support/core_ext/kernel.rb +0 -1
  22. data/lib/active_support/core_ext/module/attribute_accessors.rb +5 -5
  23. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +5 -5
  24. data/lib/active_support/core_ext/module/delegation.rb +6 -0
  25. data/lib/active_support/core_ext/object/duplicable.rb +7 -117
  26. data/lib/active_support/core_ext/range/compare_range.rb +27 -12
  27. data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
  28. data/lib/active_support/core_ext/string/filters.rb +1 -1
  29. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  30. data/lib/active_support/core_ext/string/output_safety.rb +51 -4
  31. data/lib/active_support/core_ext/time/calculations.rb +31 -2
  32. data/lib/active_support/current_attributes.rb +6 -0
  33. data/lib/active_support/dependencies.rb +41 -5
  34. data/lib/active_support/dependencies/zeitwerk_integration.rb +118 -0
  35. data/lib/active_support/deprecation/method_wrappers.rb +7 -18
  36. data/lib/active_support/deprecation/proxy_wrappers.rb +24 -3
  37. data/lib/active_support/descendants_tracker.rb +52 -6
  38. data/lib/active_support/duration.rb +2 -3
  39. data/lib/active_support/encrypted_file.rb +2 -1
  40. data/lib/active_support/evented_file_update_checker.rb +14 -2
  41. data/lib/active_support/gem_version.rb +2 -2
  42. data/lib/active_support/hash_with_indifferent_access.rb +19 -3
  43. data/lib/active_support/i18n_railtie.rb +2 -1
  44. data/lib/active_support/inflector/transliterate.rb +43 -14
  45. data/lib/active_support/logger_thread_safe_level.rb +2 -1
  46. data/lib/active_support/message_encryptor.rb +1 -1
  47. data/lib/active_support/message_verifier.rb +1 -1
  48. data/lib/active_support/notifications.rb +9 -0
  49. data/lib/active_support/notifications/fanout.rb +60 -13
  50. data/lib/active_support/notifications/instrumenter.rb +11 -10
  51. data/lib/active_support/ordered_hash.rb +1 -1
  52. data/lib/active_support/ordered_options.rb +1 -1
  53. data/lib/active_support/parameter_filter.rb +6 -1
  54. data/lib/active_support/security_utils.rb +1 -1
  55. data/lib/active_support/subscriber.rb +55 -6
  56. data/lib/active_support/testing/parallelization.rb +21 -2
  57. metadata +27 -7
  58. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
@@ -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
@@ -275,6 +275,11 @@ class Module
275
275
  #
276
276
  # The delegated method must be public on the target, otherwise it will
277
277
  # raise +NoMethodError+.
278
+ #
279
+ # The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
280
+ # delegation due to possible interference when calling
281
+ # <tt>Marshal.dump(object)</tt>, should the delegation target method
282
+ # of <tt>object</tt> add or remove instance variables.
278
283
  def delegate_missing_to(target)
279
284
  target = target.to_s
280
285
  target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
@@ -284,6 +289,7 @@ class Module
284
289
  # It may look like an oversight, but we deliberately do not pass
285
290
  # +include_private+, because they do not get delegated.
286
291
 
292
+ return false if name == :marshal_dump || name == :_dump
287
293
  #{target}.respond_to?(name) || super
288
294
  end
289
295
 
@@ -28,96 +28,6 @@ class Object
28
28
  end
29
29
  end
30
30
 
31
- class NilClass
32
- begin
33
- nil.dup
34
- rescue TypeError
35
-
36
- # +nil+ is not duplicable:
37
- #
38
- # nil.duplicable? # => false
39
- # nil.dup # => TypeError: can't dup NilClass
40
- def duplicable?
41
- false
42
- end
43
- end
44
- end
45
-
46
- class FalseClass
47
- begin
48
- false.dup
49
- rescue TypeError
50
-
51
- # +false+ is not duplicable:
52
- #
53
- # false.duplicable? # => false
54
- # false.dup # => TypeError: can't dup FalseClass
55
- def duplicable?
56
- false
57
- end
58
- end
59
- end
60
-
61
- class TrueClass
62
- begin
63
- true.dup
64
- rescue TypeError
65
-
66
- # +true+ is not duplicable:
67
- #
68
- # true.duplicable? # => false
69
- # true.dup # => TypeError: can't dup TrueClass
70
- def duplicable?
71
- false
72
- end
73
- end
74
- end
75
-
76
- class Symbol
77
- begin
78
- :symbol.dup
79
-
80
- # Some symbols couldn't be duped in Ruby 2.4.0 only, due to a bug.
81
- # This feature check catches any regression.
82
- "symbol_from_string".to_sym.dup
83
- rescue TypeError
84
-
85
- # Symbols are not duplicable:
86
- #
87
- # :my_symbol.duplicable? # => false
88
- # :my_symbol.dup # => TypeError: can't dup Symbol
89
- def duplicable?
90
- false
91
- end
92
- end
93
- end
94
-
95
- class Numeric
96
- begin
97
- 1.dup
98
- rescue TypeError
99
-
100
- # Numbers are not duplicable:
101
- #
102
- # 3.duplicable? # => false
103
- # 3.dup # => TypeError: can't dup Integer
104
- def duplicable?
105
- false
106
- end
107
- end
108
- end
109
-
110
- require "bigdecimal"
111
- class BigDecimal
112
- # BigDecimals are duplicable:
113
- #
114
- # BigDecimal("1.2").duplicable? # => true
115
- # BigDecimal("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)>
116
- def duplicable?
117
- true
118
- end
119
- end
120
-
121
31
  class Method
122
32
  # Methods are not duplicable:
123
33
  #
@@ -128,32 +38,12 @@ class Method
128
38
  end
129
39
  end
130
40
 
131
- class Complex
132
- begin
133
- Complex(1).dup
134
- rescue TypeError
135
-
136
- # Complexes are not duplicable:
137
- #
138
- # Complex(1).duplicable? # => false
139
- # Complex(1).dup # => TypeError: can't copy Complex
140
- def duplicable?
141
- false
142
- end
143
- end
144
- end
145
-
146
- class Rational
147
- begin
148
- Rational(1).dup
149
- rescue TypeError
150
-
151
- # Rationals are not duplicable:
152
- #
153
- # Rational(1).duplicable? # => false
154
- # Rational(1).dup # => TypeError: can't copy Rational
155
- def duplicable?
156
- false
157
- end
41
+ class UnboundMethod
42
+ # Unbound methods are not duplicable:
43
+ #
44
+ # method(:puts).unbind.duplicable? # => false
45
+ # method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
46
+ def duplicable?
47
+ false
158
48
  end
159
49
  end
@@ -3,54 +3,69 @@
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
12
13
  # (5..9) === (11) # => false
14
+ #
15
+ # The given range must be fully bounded, with both start and end.
13
16
  def ===(value)
14
17
  if value.is_a?(::Range)
15
18
  # 1...10 includes 1..9 but it does not include 1..10.
19
+ # 1..10 includes 1...11 but it does not include 1...12.
16
20
  operator = exclude_end? && !value.exclude_end? ? :< : :<=
17
- super(value.first) && value.last.send(operator, last)
21
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
22
+ super(value.first) && (self.end.nil? || value_max.send(operator, last))
18
23
  else
19
24
  super
20
25
  end
21
26
  end
22
27
 
23
28
  # 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
29
+ # (1..5).include?(1..5) # => true
30
+ # (1..5).include?(2..3) # => true
31
+ # (1..5).include?(1...6) # => true
32
+ # (1..5).include?(2..6) # => false
27
33
  #
28
34
  # The native Range#include? behavior is untouched.
29
35
  # ('a'..'f').include?('c') # => true
30
36
  # (5..9).include?(11) # => false
37
+ #
38
+ # The given range must be fully bounded, with both start and end.
31
39
  def include?(value)
32
40
  if value.is_a?(::Range)
33
41
  # 1...10 includes 1..9 but it does not include 1..10.
42
+ # 1..10 includes 1...11 but it does not include 1...12.
34
43
  operator = exclude_end? && !value.exclude_end? ? :< : :<=
35
- super(value.first) && value.last.send(operator, last)
44
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
45
+ super(value.first) && (self.end.nil? || value_max.send(operator, last))
36
46
  else
37
47
  super
38
48
  end
39
49
  end
40
50
 
41
51
  # 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
52
+ # (1..5).cover?(1..5) # => true
53
+ # (1..5).cover?(2..3) # => true
54
+ # (1..5).cover?(1...6) # => true
55
+ # (1..5).cover?(2..6) # => false
45
56
  #
46
57
  # The native Range#cover? behavior is untouched.
47
58
  # ('a'..'f').cover?('c') # => true
48
59
  # (5..9).cover?(11) # => false
60
+ #
61
+ # The given range must be fully bounded, with both start and end.
49
62
  def cover?(value)
50
63
  if value.is_a?(::Range)
51
64
  # 1...10 covers 1..9 but it does not cover 1..10.
65
+ # 1..10 covers 1...11 but it does not cover 1...12.
52
66
  operator = exclude_end? && !value.exclude_end? ? :< : :<=
53
- super(value.first) && value.last.send(operator, last)
67
+ value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
68
+ super(value.first) && (self.end.nil? || value_max.send(operator, last))
54
69
  else
55
70
  super
56
71
  end
@@ -9,9 +9,9 @@ module ActiveSupport
9
9
  # (1.hour.ago..1.hour.from_now).include?(Time.current) # => true
10
10
  #
11
11
  def include?(value)
12
- if first.is_a?(TimeWithZone)
12
+ if self.begin.is_a?(TimeWithZone)
13
13
  cover?(value)
14
- elsif last.is_a?(TimeWithZone)
14
+ elsif self.end.is_a?(TimeWithZone)
15
15
  cover?(value)
16
16
  else
17
17
  super
@@ -75,7 +75,7 @@ class String
75
75
  length_with_room_for_omission
76
76
  end
77
77
 
78
- "#{self[0, stop]}#{omission}"
78
+ +"#{self[0, stop]}#{omission}"
79
79
  end
80
80
 
81
81
  # Truncates +text+ to at most <tt>bytesize</tt> bytes in length without
@@ -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,46 @@ 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
+ rescue ArgumentError
302
+ # Can't create binding from C level Proc
303
+ end
257
304
  end
258
305
  end
259
306
 
@@ -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) +
@@ -312,4 +311,34 @@ class Time
312
311
  end
313
312
  alias_method :eql_without_coercion, :eql?
314
313
  alias_method :eql?, :eql_with_coercion
314
+
315
+ # Returns a new time the specified number of days ago.
316
+ def prev_day(days = 1)
317
+ advance(days: -days)
318
+ end
319
+
320
+ # Returns a new time the specified number of days in the future.
321
+ def next_day(days = 1)
322
+ advance(days: days)
323
+ end
324
+
325
+ # Returns a new time the specified number of months ago.
326
+ def prev_month(months = 1)
327
+ advance(months: -months)
328
+ end
329
+
330
+ # Returns a new time the specified number of months in the future.
331
+ def next_month(months = 1)
332
+ advance(months: months)
333
+ end
334
+
335
+ # Returns a new time the specified number of years ago.
336
+ def prev_year(years = 1)
337
+ advance(years: -years)
338
+ end
339
+
340
+ # Returns a new time the specified number of years in the future.
341
+ def next_year(years = 1)
342
+ advance(years: years)
343
+ end
315
344
  end