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
@@ -18,7 +18,6 @@ module ActiveSupport
18
18
  DIR_FORMATTER = "%03X"
19
19
  FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
20
20
  FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
21
- EXCLUDED_DIRS = [".", ".."].freeze
22
21
  GITKEEP_FILES = [".gitkeep", ".keep"].freeze
23
22
 
24
23
  def initialize(cache_path, options = nil)
@@ -35,7 +34,7 @@ module ActiveSupport
35
34
  # file store directory except for .keep or .gitkeep. Be careful which directory is specified in your
36
35
  # config file when using +FileStore+ because everything in that directory will be deleted.
37
36
  def clear(options = nil)
38
- root_dirs = exclude_from(cache_path, EXCLUDED_DIRS + GITKEEP_FILES)
37
+ root_dirs = (Dir.children(cache_path) - GITKEEP_FILES)
39
38
  FileUtils.rm_r(root_dirs.collect { |f| File.join(cache_path, f) })
40
39
  rescue Errno::ENOENT
41
40
  end
@@ -154,7 +153,7 @@ module ActiveSupport
154
153
  # Delete empty directories in the cache.
155
154
  def delete_empty_directories(dir)
156
155
  return if File.realpath(dir) == File.realpath(cache_path)
157
- if exclude_from(dir, EXCLUDED_DIRS).empty?
156
+ if Dir.children(dir).empty?
158
157
  Dir.delete(dir) rescue nil
159
158
  delete_empty_directories(File.dirname(dir))
160
159
  end
@@ -167,8 +166,7 @@ module ActiveSupport
167
166
 
168
167
  def search_dir(dir, &callback)
169
168
  return if !File.exist?(dir)
170
- Dir.foreach(dir) do |d|
171
- next if EXCLUDED_DIRS.include?(d)
169
+ Dir.each_child(dir) do |d|
172
170
  name = File.join(dir, d)
173
171
  if File.directory?(name)
174
172
  search_dir(name, &callback)
@@ -193,11 +191,6 @@ module ActiveSupport
193
191
  end
194
192
  end
195
193
  end
196
-
197
- # Exclude entries from source directory
198
- def exclude_from(source, excludes)
199
- Dir.entries(source).reject { |f| excludes.include?(f) }
200
- end
201
194
  end
202
195
  end
203
196
  end
@@ -62,13 +62,13 @@ module ActiveSupport
62
62
  return if pruning?
63
63
  @pruning = true
64
64
  begin
65
- start_time = Time.now
65
+ start_time = Concurrent.monotonic_time
66
66
  cleanup
67
67
  instrument(:prune, target_size, from: @cache_size) do
68
68
  keys = synchronize { @key_access.keys.sort { |a, b| @key_access[a].to_f <=> @key_access[b].to_f } }
69
69
  keys.each do |key|
70
70
  delete_entry(key, options)
71
- return if @cache_size <= target_size || (max_time && Time.now - start_time > max_time)
71
+ return if @cache_size <= target_size || (max_time && Concurrent.monotonic_time - start_time > max_time)
72
72
  end
73
73
  end
74
74
  ensure
@@ -125,6 +125,8 @@ module ActiveSupport
125
125
  entry = @data[key]
126
126
  synchronize do
127
127
  if entry
128
+ entry = entry.dup
129
+ entry.dup_value!
128
130
  @key_access[key] = Time.now.to_f
129
131
  else
130
132
  @key_access.delete(key)
@@ -152,12 +152,14 @@ module ActiveSupport
152
152
 
153
153
  # Creates a new Redis cache store.
154
154
  #
155
- # Handles three options: block provided to instantiate, single URL
156
- # provided, and multiple URLs provided.
155
+ # Handles four options: :redis block, :redis instance, single :url
156
+ # string, and multiple :url strings.
157
157
  #
158
- # :redis Proc -> options[:redis].call
159
- # :url String -> Redis.new(url: …)
160
- # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
158
+ # Option Class Result
159
+ # :redis Proc -> options[:redis].call
160
+ # :redis Object -> options[:redis]
161
+ # :url String -> Redis.new(url: …)
162
+ # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
161
163
  #
162
164
  # No namespace is set by default. Provide one if the Redis cache
163
165
  # server is shared with other apps: <tt>namespace: 'myapp-cache'</tt>.
@@ -361,6 +363,7 @@ module ActiveSupport
361
363
  def read_multi_mget(*names)
362
364
  options = names.extract_options!
363
365
  options = merged_options(options)
366
+ return {} if names == []
364
367
 
365
368
  keys = names.map { |name| normalize_key(name, options) }
366
369
 
@@ -468,7 +471,7 @@ module ActiveSupport
468
471
 
469
472
  def failsafe(method, returning: nil)
470
473
  yield
471
- rescue ::Redis::BaseConnectionError => e
474
+ rescue ::Redis::BaseError => e
472
475
  handle_exception exception: e, method: method, returning: returning
473
476
  returning
474
477
  end
@@ -110,7 +110,7 @@ module ActiveSupport
110
110
  base.instance_variable_set(:@_dependencies, [])
111
111
  end
112
112
 
113
- def append_features(base)
113
+ def append_features(base) #:nodoc:
114
114
  if base.instance_variable_defined?(:@_dependencies)
115
115
  base.instance_variable_get(:@_dependencies) << self
116
116
  false
@@ -123,6 +123,9 @@ module ActiveSupport
123
123
  end
124
124
  end
125
125
 
126
+ # Evaluate given block in context of base class,
127
+ # so that you can write class macros here.
128
+ # When you define more than one +included+ block, it raises an exception.
126
129
  def included(base = nil, &block)
127
130
  if base.nil?
128
131
  if instance_variable_defined?(:@_included_block)
@@ -137,6 +140,26 @@ module ActiveSupport
137
140
  end
138
141
  end
139
142
 
143
+ # Define class methods from given block.
144
+ # You can define private class methods as well.
145
+ #
146
+ # module Example
147
+ # extend ActiveSupport::Concern
148
+ #
149
+ # class_methods do
150
+ # def foo; puts 'foo'; end
151
+ #
152
+ # private
153
+ # def bar; puts 'bar'; end
154
+ # end
155
+ # end
156
+ #
157
+ # class Buzz
158
+ # include Example
159
+ # end
160
+ #
161
+ # Buzz.foo # => "foo"
162
+ # Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
140
163
  def class_methods(&class_methods_module_definition)
141
164
  mod = const_defined?(:ClassMethods, false) ?
142
165
  const_get(:ClassMethods) :
@@ -67,8 +67,8 @@ module ActiveSupport
67
67
  # end
68
68
  # # => NameError: invalid config attribute name
69
69
  #
70
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
71
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
70
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
71
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
72
72
  #
73
73
  # class User
74
74
  # include ActiveSupport::Configurable
@@ -81,7 +81,7 @@ module ActiveSupport
81
81
  # User.new.allowed_access = true # => NoMethodError
82
82
  # User.new.allowed_access # => NoMethodError
83
83
  #
84
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
84
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
85
85
  #
86
86
  # class User
87
87
  # include ActiveSupport::Configurable
@@ -29,16 +29,28 @@ class Array
29
29
  end
30
30
  end
31
31
 
32
- # Returns a copy of the Array without the specified elements.
32
+ # Returns a new array that includes the passed elements.
33
33
  #
34
- # people = ["David", "Rafael", "Aaron", "Todd"]
35
- # people.without "Aaron", "Todd"
36
- # # => ["David", "Rafael"]
34
+ # [ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
35
+ # [ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]
36
+ def including(*elements)
37
+ self + elements.flatten(1)
38
+ end
39
+
40
+ # Returns a copy of the Array excluding the specified elements.
41
+ #
42
+ # ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
43
+ # [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]
37
44
  #
38
- # Note: This is an optimization of <tt>Enumerable#without</tt> that uses <tt>Array#-</tt>
45
+ # Note: This is an optimization of <tt>Enumerable#excluding</tt> that uses <tt>Array#-</tt>
39
46
  # instead of <tt>Array#reject</tt> for performance reasons.
47
+ def excluding(*elements)
48
+ self - elements.flatten(1)
49
+ end
50
+
51
+ # Alias for #excluding.
40
52
  def without(*elements)
41
- self - elements
53
+ excluding(*elements)
42
54
  end
43
55
 
44
56
  # Equal to <tt>self[1]</tt>.
@@ -84,16 +84,17 @@ class Class
84
84
  # To set a default value for the attribute, pass <tt>default:</tt>, like so:
85
85
  #
86
86
  # class_attribute :settings, default: {}
87
- def class_attribute(*attrs)
88
- options = attrs.extract_options!
89
- instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
90
- instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
91
- instance_predicate = options.fetch(:instance_predicate, true)
92
- default_value = options.fetch(:default, nil)
93
-
87
+ def class_attribute(
88
+ *attrs,
89
+ instance_accessor: true,
90
+ instance_reader: instance_accessor,
91
+ instance_writer: instance_accessor,
92
+ instance_predicate: true,
93
+ default: nil
94
+ )
94
95
  attrs.each do |name|
95
96
  singleton_class.silence_redefinition_of_method(name)
96
- define_singleton_method(name) { nil }
97
+ define_singleton_method(name) { default }
97
98
 
98
99
  singleton_class.silence_redefinition_of_method("#{name}?")
99
100
  define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
@@ -102,9 +103,7 @@ class Class
102
103
 
103
104
  singleton_class.silence_redefinition_of_method("#{name}=")
104
105
  define_singleton_method("#{name}=") do |val|
105
- singleton_class.class_eval do
106
- redefine_method(name) { val }
107
- end
106
+ redefine_singleton_method(name) { val }
108
107
 
109
108
  if singleton_class?
110
109
  class_eval do
@@ -137,10 +136,6 @@ class Class
137
136
  instance_variable_set ivar, val
138
137
  end
139
138
  end
140
-
141
- unless default_value.nil?
142
- self.send("#{name}=", default_value)
143
- end
144
139
  end
145
140
  end
146
141
  end
@@ -20,21 +20,11 @@ module DateAndTime
20
20
  advance(days: -1)
21
21
  end
22
22
 
23
- # Returns a new date/time the specified number of days ago.
24
- def prev_day(days = 1)
25
- advance(days: -days)
26
- end
27
-
28
23
  # Returns a new date/time representing tomorrow.
29
24
  def tomorrow
30
25
  advance(days: 1)
31
26
  end
32
27
 
33
- # Returns a new date/time the specified number of days in the future.
34
- def next_day(days = 1)
35
- advance(days: days)
36
- end
37
-
38
28
  # Returns true if the date/time is today.
39
29
  def today?
40
30
  to_date == ::Date.current
@@ -198,21 +188,11 @@ module DateAndTime
198
188
  end
199
189
  end
200
190
 
201
- # Returns a new date/time the specified number of months in the future.
202
- def next_month(months = 1)
203
- advance(months: months)
204
- end
205
-
206
191
  # Short-hand for months_since(3)
207
192
  def next_quarter
208
193
  months_since(3)
209
194
  end
210
195
 
211
- # Returns a new date/time the specified number of years in the future.
212
- def next_year(years = 1)
213
- advance(years: years)
214
- end
215
-
216
196
  # Returns a new date/time representing the given day in the previous week.
217
197
  # Week is assumed to start on +start_day+, default is
218
198
  # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
@@ -233,11 +213,6 @@ module DateAndTime
233
213
  end
234
214
  alias_method :last_weekday, :prev_weekday
235
215
 
236
- # Returns a new date/time the specified number of months ago.
237
- def prev_month(months = 1)
238
- advance(months: -months)
239
- end
240
-
241
216
  # Short-hand for months_ago(1).
242
217
  def last_month
243
218
  months_ago(1)
@@ -249,11 +224,6 @@ module DateAndTime
249
224
  end
250
225
  alias_method :last_quarter, :prev_quarter
251
226
 
252
- # Returns a new date/time the specified number of years ago.
253
- def prev_year(years = 1)
254
- advance(years: -years)
255
- end
256
-
257
227
  # Short-hand for years_ago(1).
258
228
  def last_year
259
229
  years_ago(1)
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/digest/uuid"
@@ -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)
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "active_support/core_ext/hash/conversions"
4
4
  require "active_support/core_ext/hash/deep_merge"
5
+ require "active_support/core_ext/hash/deep_transform_values"
5
6
  require "active_support/core_ext/hash/except"
6
7
  require "active_support/core_ext/hash/indifferent_access"
7
8
  require "active_support/core_ext/hash/keys"
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ # Returns a new hash with all values converted by the block operation.
5
+ # This includes the values from the root hash and from all
6
+ # nested hashes and arrays.
7
+ #
8
+ # hash = { person: { name: 'Rob', age: '28' } }
9
+ #
10
+ # hash.deep_transform_values{ |value| value.to_s.upcase }
11
+ # # => {person: {name: "ROB", age: "28"}}
12
+ def deep_transform_values(&block)
13
+ _deep_transform_values_in_object(self, &block)
14
+ end
15
+
16
+ # Destructively converts all values by using the block operation.
17
+ # This includes the values from the root hash and from all
18
+ # nested hashes and arrays.
19
+ def deep_transform_values!(&block)
20
+ _deep_transform_values_in_object!(self, &block)
21
+ end
22
+
23
+ private
24
+ # support methods for deep transforming nested hashes and arrays
25
+ def _deep_transform_values_in_object(object, &block)
26
+ case object
27
+ when Hash
28
+ object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
29
+ when Array
30
+ object.map { |e| _deep_transform_values_in_object(e, &block) }
31
+ else
32
+ yield(object)
33
+ end
34
+ end
35
+
36
+ def _deep_transform_values_in_object!(object, &block)
37
+ case object
38
+ when Hash
39
+ object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) }
40
+ when Array
41
+ object.map! { |e| _deep_transform_values_in_object!(e, &block) }
42
+ else
43
+ yield(object)
44
+ end
45
+ end
46
+ end
@@ -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.
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/kernel/agnostics"
4
3
  require "active_support/core_ext/kernel/concern"
5
4
  require "active_support/core_ext/kernel/reporting"
6
5
  require "active_support/core_ext/kernel/singleton_class"