activesupport 6.0.4.4 → 7.0.4.1

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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +257 -532
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +2 -2
  6. data/lib/active_support/backtrace_cleaner.rb +5 -5
  7. data/lib/active_support/benchmarkable.rb +3 -3
  8. data/lib/active_support/cache/file_store.rb +16 -10
  9. data/lib/active_support/cache/mem_cache_store.rb +163 -42
  10. data/lib/active_support/cache/memory_store.rb +57 -29
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +79 -98
  13. data/lib/active_support/cache/strategy/local_cache.rb +49 -57
  14. data/lib/active_support/cache.rb +378 -179
  15. data/lib/active_support/callbacks.rb +230 -122
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +49 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  19. data/lib/active_support/concurrency/share_lock.rb +2 -2
  20. data/lib/active_support/configurable.rb +9 -6
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +1 -5
  23. data/lib/active_support/core_ext/array/conversions.rb +13 -12
  24. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  25. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  26. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  27. data/lib/active_support/core_ext/array.rb +1 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  30. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  31. data/lib/active_support/core_ext/class/subclasses.rb +9 -22
  32. data/lib/active_support/core_ext/date/blank.rb +1 -1
  33. data/lib/active_support/core_ext/date/calculations.rb +9 -9
  34. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  35. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  36. data/lib/active_support/core_ext/date.rb +1 -0
  37. data/lib/active_support/core_ext/date_and_time/calculations.rb +17 -4
  38. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  39. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  41. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  42. data/lib/active_support/core_ext/date_time.rb +1 -0
  43. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  44. data/lib/active_support/core_ext/enumerable.rb +164 -23
  45. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  46. data/lib/active_support/core_ext/hash/conversions.rb +2 -3
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  49. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  50. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  51. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  52. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  53. data/lib/active_support/core_ext/load_error.rb +1 -1
  54. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  57. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  58. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  59. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  60. data/lib/active_support/core_ext/name_error.rb +23 -2
  61. data/lib/active_support/core_ext/numeric/conversions.rb +80 -73
  62. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  63. data/lib/active_support/core_ext/numeric.rb +1 -0
  64. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  65. data/lib/active_support/core_ext/object/blank.rb +2 -2
  66. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  68. data/lib/active_support/core_ext/object/json.rb +42 -26
  69. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  70. data/lib/active_support/core_ext/object/try.rb +20 -20
  71. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  72. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  73. data/lib/active_support/core_ext/pathname.rb +3 -0
  74. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  75. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  76. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  77. data/lib/active_support/core_ext/range/each.rb +1 -1
  78. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  79. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  80. data/lib/active_support/core_ext/range.rb +1 -1
  81. data/lib/active_support/core_ext/regexp.rb +8 -1
  82. data/lib/active_support/core_ext/securerandom.rb +1 -1
  83. data/lib/active_support/core_ext/string/access.rb +5 -24
  84. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  85. data/lib/active_support/core_ext/string/filters.rb +1 -1
  86. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  87. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  88. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  89. data/lib/active_support/core_ext/string/output_safety.rb +92 -41
  90. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  92. data/lib/active_support/core_ext/symbol.rb +3 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +25 -7
  94. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  95. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  96. data/lib/active_support/core_ext/time/zones.rb +7 -22
  97. data/lib/active_support/core_ext/time.rb +1 -0
  98. data/lib/active_support/core_ext/uri.rb +3 -23
  99. data/lib/active_support/core_ext.rb +2 -1
  100. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  101. data/lib/active_support/current_attributes.rb +39 -16
  102. data/lib/active_support/dependencies/interlock.rb +10 -18
  103. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  104. data/lib/active_support/dependencies.rb +58 -769
  105. data/lib/active_support/deprecation/behaviors.rb +23 -7
  106. data/lib/active_support/deprecation/disallowed.rb +56 -0
  107. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  108. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
  110. data/lib/active_support/deprecation/reporting.rb +50 -7
  111. data/lib/active_support/deprecation.rb +7 -2
  112. data/lib/active_support/descendants_tracker.rb +174 -64
  113. data/lib/active_support/digest.rb +5 -3
  114. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  115. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  116. data/lib/active_support/duration.rb +134 -55
  117. data/lib/active_support/encrypted_configuration.rb +13 -2
  118. data/lib/active_support/encrypted_file.rb +32 -3
  119. data/lib/active_support/environment_inquirer.rb +20 -0
  120. data/lib/active_support/error_reporter.rb +117 -0
  121. data/lib/active_support/evented_file_update_checker.rb +72 -138
  122. data/lib/active_support/execution_context/test_helper.rb +13 -0
  123. data/lib/active_support/execution_context.rb +53 -0
  124. data/lib/active_support/execution_wrapper.rb +43 -21
  125. data/lib/active_support/executor/test_helper.rb +7 -0
  126. data/lib/active_support/fork_tracker.rb +71 -0
  127. data/lib/active_support/gem_version.rb +3 -3
  128. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  129. data/lib/active_support/html_safe_translation.rb +43 -0
  130. data/lib/active_support/i18n.rb +1 -0
  131. data/lib/active_support/i18n_railtie.rb +14 -19
  132. data/lib/active_support/inflector/inflections.rb +24 -9
  133. data/lib/active_support/inflector/methods.rb +29 -49
  134. data/lib/active_support/inflector/transliterate.rb +5 -5
  135. data/lib/active_support/isolated_execution_state.rb +72 -0
  136. data/lib/active_support/json/decoding.rb +4 -4
  137. data/lib/active_support/json/encoding.rb +8 -4
  138. data/lib/active_support/key_generator.rb +23 -6
  139. data/lib/active_support/lazy_load_hooks.rb +28 -4
  140. data/lib/active_support/locale/en.yml +8 -4
  141. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  142. data/lib/active_support/log_subscriber.rb +23 -5
  143. data/lib/active_support/logger.rb +1 -1
  144. data/lib/active_support/logger_silence.rb +2 -26
  145. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  146. data/lib/active_support/message_encryptor.rb +16 -13
  147. data/lib/active_support/message_verifier.rb +50 -18
  148. data/lib/active_support/messages/metadata.rb +2 -2
  149. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  150. data/lib/active_support/messages/rotator.rb +6 -5
  151. data/lib/active_support/multibyte/chars.rb +13 -52
  152. data/lib/active_support/multibyte/unicode.rb +1 -87
  153. data/lib/active_support/multibyte.rb +1 -1
  154. data/lib/active_support/notifications/fanout.rb +110 -69
  155. data/lib/active_support/notifications/instrumenter.rb +37 -29
  156. data/lib/active_support/notifications.rb +55 -28
  157. data/lib/active_support/number_helper/number_converter.rb +2 -4
  158. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  159. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  160. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  161. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  162. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  163. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  164. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  165. data/lib/active_support/number_helper.rb +29 -16
  166. data/lib/active_support/option_merger.rb +11 -18
  167. data/lib/active_support/ordered_hash.rb +1 -1
  168. data/lib/active_support/ordered_options.rb +9 -3
  169. data/lib/active_support/parameter_filter.rb +21 -11
  170. data/lib/active_support/per_thread_registry.rb +6 -1
  171. data/lib/active_support/rails.rb +1 -4
  172. data/lib/active_support/railtie.rb +77 -5
  173. data/lib/active_support/reloader.rb +1 -1
  174. data/lib/active_support/rescuable.rb +16 -16
  175. data/lib/active_support/ruby_features.rb +7 -0
  176. data/lib/active_support/secure_compare_rotator.rb +51 -0
  177. data/lib/active_support/security_utils.rb +19 -12
  178. data/lib/active_support/string_inquirer.rb +2 -2
  179. data/lib/active_support/subscriber.rb +19 -25
  180. data/lib/active_support/tagged_logging.rb +31 -6
  181. data/lib/active_support/test_case.rb +13 -21
  182. data/lib/active_support/testing/assertions.rb +50 -13
  183. data/lib/active_support/testing/deprecation.rb +52 -1
  184. data/lib/active_support/testing/isolation.rb +2 -2
  185. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  186. data/lib/active_support/testing/parallelization/server.rb +82 -0
  187. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  188. data/lib/active_support/testing/parallelization.rb +16 -95
  189. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  190. data/lib/active_support/testing/stream.rb +3 -5
  191. data/lib/active_support/testing/tagged_logging.rb +1 -1
  192. data/lib/active_support/testing/time_helpers.rb +53 -5
  193. data/lib/active_support/time_with_zone.rb +126 -62
  194. data/lib/active_support/values/time_zone.rb +54 -23
  195. data/lib/active_support/version.rb +1 -1
  196. data/lib/active_support/xml_mini/jdom.rb +1 -1
  197. data/lib/active_support/xml_mini/libxml.rb +5 -5
  198. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  199. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  200. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  201. data/lib/active_support/xml_mini/rexml.rb +9 -2
  202. data/lib/active_support/xml_mini.rb +5 -4
  203. data/lib/active_support.rb +29 -1
  204. metadata +46 -45
  205. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  206. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  207. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  208. data/lib/active_support/core_ext/marshal.rb +0 -24
  209. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  210. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  211. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  212. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/xml_mini"
4
- require "active_support/time"
5
3
  require "active_support/core_ext/object/blank"
6
4
  require "active_support/core_ext/object/to_param"
7
5
  require "active_support/core_ext/object/to_query"
6
+ require "active_support/core_ext/object/try"
8
7
  require "active_support/core_ext/array/wrap"
9
8
  require "active_support/core_ext/hash/reverse_merge"
10
9
  require "active_support/core_ext/string/inflections"
@@ -208,7 +207,7 @@ module ActiveSupport
208
207
  elsif become_empty_string?(value)
209
208
  ""
210
209
  elsif become_hash?(value)
211
- xml_value = Hash[value.map { |k, v| [k, deep_to_h(v)] }]
210
+ xml_value = value.transform_values { |v| deep_to_h(v) }
212
211
 
213
212
  # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
214
213
  # how multipart uploaded files from HTML appear
@@ -21,7 +21,7 @@ class Hash
21
21
  end
22
22
 
23
23
  private
24
- # support methods for deep transforming nested hashes and arrays
24
+ # Support methods for deep transforming nested hashes and arrays.
25
25
  def _deep_transform_values_in_object(object, &block)
26
26
  case object
27
27
  when Hash
@@ -3,7 +3,7 @@
3
3
  require "active_support/hash_with_indifferent_access"
4
4
 
5
5
  class Hash
6
- # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
6
+ # Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver:
7
7
  #
8
8
  # { a: 1 }.with_indifferent_access['a'] # => 1
9
9
  def with_indifferent_access
@@ -13,8 +13,8 @@ class Hash
13
13
  # Called when object is nested under an object that receives
14
14
  # #with_indifferent_access. This method will be called on the current object
15
15
  # by the enclosing object and is aliased to #with_indifferent_access by
16
- # default. Subclasses of Hash may overwrite this method to return +self+ if
17
- # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
16
+ # default. Subclasses of Hash may override this method to return +self+ if
17
+ # converting to an ActiveSupport::HashWithIndifferentAccess would not be
18
18
  # desirable.
19
19
  #
20
20
  # b = { b: 1 }
@@ -112,11 +112,11 @@ class Hash
112
112
  end
113
113
 
114
114
  private
115
- # support methods for deep transforming nested hashes and arrays
115
+ # Support methods for deep transforming nested hashes and arrays.
116
116
  def _deep_transform_keys_in_object(object, &block)
117
117
  case object
118
118
  when Hash
119
- object.each_with_object({}) do |(key, value), result|
119
+ object.each_with_object(self.class.new) do |(key, value), result|
120
120
  result[yield(key)] = _deep_transform_keys_in_object(value, &block)
121
121
  end
122
122
  when Array
@@ -18,8 +18,9 @@ class Hash
18
18
 
19
19
  # Removes and returns the key/value pairs matching the given keys.
20
20
  #
21
- # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
22
- # { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
21
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
22
+ # hash.extract!(:a, :b) # => {:a=>1, :b=>2}
23
+ # hash # => {:c=>3, :d=>4}
23
24
  def extract!(*keys)
24
25
  keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
25
26
  end
@@ -11,14 +11,14 @@ module Kernel
11
11
  # end
12
12
  #
13
13
  # noisy_call # warning voiced
14
- def silence_warnings
15
- with_warnings(nil) { yield }
14
+ def silence_warnings(&block)
15
+ with_warnings(nil, &block)
16
16
  end
17
17
 
18
18
  # Sets $VERBOSE to +true+ for the duration of the block and back to its
19
19
  # original value afterwards.
20
- def enable_warnings
21
- with_warnings(true) { yield }
20
+ def enable_warnings(&block)
21
+ with_warnings(true, &block)
22
22
  end
23
23
 
24
24
  # Sets $VERBOSE for the duration of the block and back to its original
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kernel
4
- # class_eval on an object acts like singleton_class.class_eval.
4
+ # class_eval on an object acts like +singleton_class.class_eval+.
5
5
  def class_eval(*args, &block)
6
6
  singleton_class.class_eval(*args, &block)
7
7
  end
@@ -4,6 +4,6 @@ class LoadError
4
4
  # Returns true if the given path name (except perhaps for the ".rb"
5
5
  # extension) is the missing file which caused the exception to be raised.
6
6
  def is_missing?(location)
7
- location.sub(/\.rb$/, "") == path.to_s.sub(/\.rb$/, "")
7
+ location.delete_suffix(".rb") == path.to_s.delete_suffix(".rb")
8
8
  end
9
9
  end
@@ -28,9 +28,9 @@ class Module
28
28
  end
29
29
 
30
30
  def attr_internal_define(attr_name, type)
31
- internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, "")
31
+ internal_name = attr_internal_ivar_name(attr_name).delete_prefix("@")
32
32
  # use native attr_* methods as they are faster on some Ruby implementations
33
- send("attr_#{type}", internal_name)
33
+ public_send("attr_#{type}", internal_name)
34
34
  attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
35
35
  alias_method attr_name, internal_name
36
36
  remove_method internal_name
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # == Attribute Accessors
4
+ #
3
5
  # Extends the module object with class/module and instance accessors for
4
6
  # class/module attributes, just like the native attr* accessors for instance
5
7
  # attributes.
@@ -48,28 +50,25 @@ class Module
48
50
  # end
49
51
  #
50
52
  # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
51
- def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil)
53
+ def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
54
+ raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
55
+ location ||= caller_locations(1, 1).first
56
+
57
+ definition = []
52
58
  syms.each do |sym|
53
59
  raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
54
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
55
- @@#{sym} = nil unless defined? @@#{sym}
56
60
 
57
- def self.#{sym}
58
- @@#{sym}
59
- end
60
- EOS
61
+ definition << "def self.#{sym}; @@#{sym}; end"
61
62
 
62
63
  if instance_reader && instance_accessor
63
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
64
- def #{sym}
65
- @@#{sym}
66
- end
67
- EOS
64
+ definition << "def #{sym}; @@#{sym}; end"
68
65
  end
69
66
 
70
67
  sym_default_value = (block_given? && default.nil?) ? yield : default
71
- class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil?
68
+ class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
72
69
  end
70
+
71
+ module_eval(definition.join(";"), location.path, location.lineno)
73
72
  end
74
73
  alias :cattr_reader :mattr_reader
75
74
 
@@ -115,28 +114,24 @@ class Module
115
114
  # end
116
115
  #
117
116
  # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
118
- def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil)
117
+ def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
118
+ raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
119
+ location ||= caller_locations(1, 1).first
120
+
121
+ definition = []
119
122
  syms.each do |sym|
120
123
  raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
121
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
122
- @@#{sym} = nil unless defined? @@#{sym}
123
-
124
- def self.#{sym}=(obj)
125
- @@#{sym} = obj
126
- end
127
- EOS
124
+ definition << "def self.#{sym}=(val); @@#{sym} = val; end"
128
125
 
129
126
  if instance_writer && instance_accessor
130
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
131
- def #{sym}=(obj)
132
- @@#{sym} = obj
133
- end
134
- EOS
127
+ definition << "def #{sym}=(val); @@#{sym} = val; end"
135
128
  end
136
129
 
137
130
  sym_default_value = (block_given? && default.nil?) ? yield : default
138
- send("#{sym}=", sym_default_value) unless sym_default_value.nil?
131
+ class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
139
132
  end
133
+
134
+ module_eval(definition.join(";"), location.path, location.lineno)
140
135
  end
141
136
  alias :cattr_writer :mattr_writer
142
137
 
@@ -205,8 +200,9 @@ class Module
205
200
  #
206
201
  # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
207
202
  def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
208
- mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, &blk)
209
- mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default)
203
+ location = caller_locations(1, 1).first
204
+ mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
205
+ mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location)
210
206
  end
211
207
  alias :cattr_accessor :mattr_accessor
212
208
  end
@@ -1,11 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # == Attribute Accessors per Thread
4
+ #
3
5
  # Extends the module object with class/module and instance accessors for
4
6
  # class/module attributes, just like the native attr* accessors for instance
5
7
  # attributes, but does so on a per-thread basis.
6
8
  #
7
9
  # So the values are scoped within the Thread.current space under the class name
8
10
  # of the module.
11
+ #
12
+ # Note that it can also be scoped per-fiber if +Rails.application.config.active_support.isolation_level+
13
+ # is set to +:fiber+.
9
14
  class Module
10
15
  # Defines a per-thread class attribute and creates class and instance reader methods.
11
16
  # The underlying per-thread class variable is set to +nil+, if it is not previously defined.
@@ -14,9 +19,9 @@ class Module
14
19
  # thread_mattr_reader :user
15
20
  # end
16
21
  #
17
- # Current.user # => nil
18
- # Thread.current[:attr_Current_user] = "DHH"
22
+ # Current.user = "DHH"
19
23
  # Current.user # => "DHH"
24
+ # Thread.new { Current.user }.value # => nil
20
25
  #
21
26
  # The attribute name must be a valid method name in Ruby.
22
27
  #
@@ -33,7 +38,7 @@ class Module
33
38
  # end
34
39
  #
35
40
  # Current.new.user # => NoMethodError
36
- def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true) # :nodoc:
41
+ def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
37
42
  syms.each do |sym|
38
43
  raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
39
44
 
@@ -41,7 +46,8 @@ class Module
41
46
  # to work with inheritance via polymorphism.
42
47
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
43
48
  def self.#{sym}
44
- Thread.current["attr_" + name + "_#{sym}"]
49
+ @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}"
50
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
45
51
  end
46
52
  EOS
47
53
 
@@ -52,6 +58,8 @@ class Module
52
58
  end
53
59
  EOS
54
60
  end
61
+
62
+ ::ActiveSupport::IsolatedExecutionState["attr_#{name}_#{sym}"] = default unless default.nil?
55
63
  end
56
64
  end
57
65
  alias :thread_cattr_reader :thread_mattr_reader
@@ -74,7 +82,7 @@ class Module
74
82
  # end
75
83
  #
76
84
  # Current.new.user = "DHH" # => NoMethodError
77
- def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true) # :nodoc:
85
+ def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
78
86
  syms.each do |sym|
79
87
  raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
80
88
 
@@ -82,7 +90,8 @@ class Module
82
90
  # to work with inheritance via polymorphism.
83
91
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
84
92
  def self.#{sym}=(obj)
85
- Thread.current["attr_" + name + "_#{sym}"] = obj
93
+ @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}"
94
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj
86
95
  end
87
96
  EOS
88
97
 
@@ -93,6 +102,8 @@ class Module
93
102
  end
94
103
  EOS
95
104
  end
105
+
106
+ public_send("#{sym}=", default) unless default.nil?
96
107
  end
97
108
  end
98
109
  alias :thread_cattr_writer :thread_mattr_writer
@@ -107,16 +118,18 @@ class Module
107
118
  # Account.user # => "DHH"
108
119
  # Account.new.user # => "DHH"
109
120
  #
121
+ # Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes.
110
122
  # If a subclass changes the value, the parent class' value is not changed.
111
- # Similarly, if the parent class changes the value, the value of subclasses
112
- # is not changed.
123
+ # If the parent class changes the value, the value of subclasses is not changed.
113
124
  #
114
125
  # class Customer < Account
115
126
  # end
116
127
  #
117
- # Customer.user = "Rafael"
118
- # Customer.user # => "Rafael"
119
- # Account.user # => "DHH"
128
+ # Account.user # => "DHH"
129
+ # Customer.user # => nil
130
+ # Customer.user = "Rafael"
131
+ # Customer.user # => "Rafael"
132
+ # Account.user # => "DHH"
120
133
  #
121
134
  # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
122
135
  # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
@@ -136,8 +149,8 @@ class Module
136
149
  #
137
150
  # Current.new.user = "DHH" # => NoMethodError
138
151
  # Current.new.user # => NoMethodError
139
- def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true)
140
- thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor)
152
+ def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
153
+ thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
141
154
  thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
142
155
  end
143
156
  alias :thread_cattr_accessor :thread_mattr_accessor
@@ -104,10 +104,16 @@ class Module
104
104
  # * grok the behavior of our class in one glance,
105
105
  # * clean up monolithic junk-drawer classes by separating their concerns, and
106
106
  # * stop leaning on protected/private for crude "this is internal stuff" modularity.
107
+ #
108
+ # === Prepending concerning
109
+ #
110
+ # <tt>concerning</tt> supports a <tt>prepend: true</tt> argument which will <tt>prepend</tt> the
111
+ # concern instead of using <tt>include</tt> for it.
107
112
  module Concerning
108
113
  # Define a new concern and mix it in.
109
- def concerning(topic, &block)
110
- include concern(topic, &block)
114
+ def concerning(topic, prepend: false, &block)
115
+ method = prepend ? :prepend : :include
116
+ __send__(method, concern(topic, &block))
111
117
  end
112
118
 
113
119
  # A low-cruft shortcut to define a concern.
@@ -170,7 +170,7 @@ class Module
170
170
  # The target method must be public, otherwise it will raise +NoMethodError+.
171
171
  def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
172
172
  unless to
173
- raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter)."
173
+ raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
174
174
  end
175
175
 
176
176
  if prefix == true && /^[^a-z_]/.match?(to)
@@ -190,16 +190,16 @@ class Module
190
190
  to = to.to_s
191
191
  to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
192
192
 
193
- method_names = methods.map do |method|
193
+ method_def = []
194
+ method_names = []
195
+
196
+ methods.map do |method|
197
+ method_name = prefix ? "#{method_prefix}#{method}" : method
198
+ method_names << method_name.to_sym
199
+
194
200
  # Attribute writer methods only accept one argument. Makes sure []=
195
201
  # methods still accept two arguments.
196
- definition = if /[^\]]=$/.match?(method)
197
- "arg"
198
- elsif RUBY_VERSION >= "2.7"
199
- "..."
200
- else
201
- "*args, &block"
202
- end
202
+ definition = /[^\]]=\z/.match?(method) ? "arg" : "..."
203
203
 
204
204
  # The following generated method calls the target exactly once, storing
205
205
  # the returned value in a dummy variable.
@@ -209,34 +209,33 @@ class Module
209
209
  # whereas conceptually, from the user point of view, the delegator should
210
210
  # be doing one call.
211
211
  if allow_nil
212
- method_def = [
213
- "def #{method_prefix}#{method}(#{definition})",
214
- "_ = #{to}",
215
- "if !_.nil? || nil.respond_to?(:#{method})",
216
- " _.#{method}(#{definition})",
217
- "end",
218
- "end"
219
- ].join ";"
212
+ method = method.to_s
213
+
214
+ method_def <<
215
+ "def #{method_name}(#{definition})" <<
216
+ " _ = #{to}" <<
217
+ " if !_.nil? || nil.respond_to?(:#{method})" <<
218
+ " _.#{method}(#{definition})" <<
219
+ " end" <<
220
+ "end"
220
221
  else
221
- exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
222
+ method = method.to_s
223
+ method_name = method_name.to_s
222
224
 
223
- method_def = [
224
- "def #{method_prefix}#{method}(#{definition})",
225
- " _ = #{to}",
226
- " _.#{method}(#{definition})",
227
- "rescue NoMethodError => e",
228
- " if _.nil? && e.name == :#{method}",
229
- " #{exception}",
230
- " else",
231
- " raise",
232
- " end",
225
+ method_def <<
226
+ "def #{method_name}(#{definition})" <<
227
+ " _ = #{to}" <<
228
+ " _.#{method}(#{definition})" <<
229
+ "rescue NoMethodError => e" <<
230
+ " if _.nil? && e.name == :#{method}" <<
231
+ %( raise DelegationError, "#{self}##{method_name} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") <<
232
+ " else" <<
233
+ " raise" <<
234
+ " end" <<
233
235
  "end"
234
- ].join ";"
235
236
  end
236
-
237
- module_eval(method_def, file, line)
238
237
  end
239
-
238
+ module_eval(method_def.join(";"), file, line)
240
239
  private(*method_names) if private
241
240
  method_names
242
241
  end
@@ -280,13 +279,14 @@ class Module
280
279
  # variables, methods, constants, etc.
281
280
  #
282
281
  # The delegated method must be public on the target, otherwise it will
283
- # raise +NoMethodError+.
282
+ # raise +DelegationError+. If you wish to instead return +nil+,
283
+ # use the <tt>:allow_nil</tt> option.
284
284
  #
285
285
  # The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
286
286
  # delegation due to possible interference when calling
287
287
  # <tt>Marshal.dump(object)</tt>, should the delegation target method
288
288
  # of <tt>object</tt> add or remove instance variables.
289
- def delegate_missing_to(target)
289
+ def delegate_missing_to(target, allow_nil: nil)
290
290
  target = target.to_s
291
291
  target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
292
292
 
@@ -307,14 +307,18 @@ class Module
307
307
  super
308
308
  rescue NoMethodError
309
309
  if #{target}.nil?
310
- raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
310
+ if #{allow_nil == true}
311
+ nil
312
+ else
313
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
314
+ end
311
315
  else
312
316
  raise
313
317
  end
314
318
  end
315
319
  end
316
320
  end
317
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
321
+ ruby2_keywords(:method_missing)
318
322
  RUBY
319
323
  end
320
324
  end
@@ -11,20 +11,12 @@ class Module
11
11
  if defined?(@parent_name)
12
12
  @parent_name
13
13
  else
14
- parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
14
+ parent_name = name =~ /::[^:]+\z/ ? -$` : nil
15
15
  @parent_name = parent_name unless frozen?
16
16
  parent_name
17
17
  end
18
18
  end
19
19
 
20
- def parent_name
21
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
22
- `Module#parent_name` has been renamed to `module_parent_name`.
23
- `parent_name` is deprecated and will be removed in Rails 6.1.
24
- MSG
25
- module_parent_name
26
- end
27
-
28
20
  # Returns the module which contains this one according to its name.
29
21
  #
30
22
  # module M
@@ -44,14 +36,6 @@ class Module
44
36
  module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object
45
37
  end
46
38
 
47
- def parent
48
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
49
- `Module#parent` has been renamed to `module_parent`.
50
- `parent` is deprecated and will be removed in Rails 6.1.
51
- MSG
52
- module_parent
53
- end
54
-
55
39
  # Returns all the parents of this module according to its name, ordered from
56
40
  # nested outwards. The receiver is not contained within the result.
57
41
  #
@@ -76,12 +60,4 @@ class Module
76
60
  parents << Object unless parents.include? Object
77
61
  parents
78
62
  end
79
-
80
- def parents
81
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
82
- `Module#parents` has been renamed to `module_parents`.
83
- `parents` is deprecated and will be removed in Rails 6.1.
84
- MSG
85
- module_parents
86
- end
87
63
  end
@@ -14,9 +14,22 @@ class NameError
14
14
  # It extends NameError#message with spell corrections which are SLOW.
15
15
  # We should use original_message message instead.
16
16
  message = respond_to?(:original_message) ? original_message : self.message
17
+ return unless message.start_with?("uninitialized constant ")
17
18
 
18
- if /undefined local variable or method/ !~ message
19
- $1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
19
+ receiver = begin
20
+ self.receiver
21
+ rescue ArgumentError
22
+ nil
23
+ end
24
+
25
+ if receiver == Object
26
+ name.to_s
27
+ elsif receiver
28
+ "#{real_mod_name(receiver)}::#{self.name}"
29
+ else
30
+ if match = message.match(/((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/)
31
+ match[1]
32
+ end
20
33
  end
21
34
  end
22
35
 
@@ -35,4 +48,12 @@ class NameError
35
48
  missing_name == name.to_s
36
49
  end
37
50
  end
51
+
52
+ private
53
+ UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
54
+ private_constant :UNBOUND_METHOD_MODULE_NAME
55
+
56
+ def real_mod_name(mod)
57
+ UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
58
+ end
38
59
  end