activesupport 6.1.7.2 → 7.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (222) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +866 -411
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -6
  5. data/lib/active_support/actionable_error.rb +4 -2
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +27 -7
  8. data/lib/active_support/benchmarkable.rb +3 -2
  9. data/lib/active_support/broadcast_logger.rb +250 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +52 -19
  14. data/lib/active_support/cache/mem_cache_store.rb +195 -60
  15. data/lib/active_support/cache/memory_store.rb +86 -24
  16. data/lib/active_support/cache/null_store.rb +16 -2
  17. data/lib/active_support/cache/redis_cache_store.rb +186 -193
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +63 -71
  20. data/lib/active_support/cache.rb +478 -247
  21. data/lib/active_support/callbacks.rb +227 -105
  22. data/lib/active_support/code_generator.rb +65 -0
  23. data/lib/active_support/concern.rb +9 -7
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/concurrency/share_lock.rb +2 -2
  27. data/lib/active_support/configurable.rb +18 -5
  28. data/lib/active_support/configuration_file.rb +1 -1
  29. data/lib/active_support/core_ext/array/access.rb +1 -5
  30. data/lib/active_support/core_ext/array/conversions.rb +15 -13
  31. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  33. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  34. data/lib/active_support/core_ext/class/subclasses.rb +37 -26
  35. data/lib/active_support/core_ext/date/blank.rb +1 -1
  36. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  37. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  38. data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
  39. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  41. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  42. data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
  43. data/lib/active_support/core_ext/digest/uuid.rb +30 -14
  44. data/lib/active_support/core_ext/enumerable.rb +85 -83
  45. data/lib/active_support/core_ext/erb/util.rb +196 -0
  46. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  47. data/lib/active_support/core_ext/hash/conversions.rb +1 -2
  48. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  49. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  50. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  51. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  52. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  53. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  54. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +8 -0
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +49 -22
  57. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  58. data/lib/active_support/core_ext/module/delegation.rb +41 -18
  59. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  60. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  61. data/lib/active_support/core_ext/name_error.rb +2 -8
  62. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  63. data/lib/active_support/core_ext/numeric/conversions.rb +82 -77
  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 +17 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +15 -4
  68. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  69. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  70. data/lib/active_support/core_ext/object/json.rb +40 -27
  71. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  72. data/lib/active_support/core_ext/object/try.rb +20 -20
  73. data/lib/active_support/core_ext/object/with.rb +44 -0
  74. data/lib/active_support/core_ext/object/with_options.rb +25 -6
  75. data/lib/active_support/core_ext/object.rb +1 -0
  76. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  77. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  78. data/lib/active_support/core_ext/pathname.rb +4 -0
  79. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  80. data/lib/active_support/core_ext/range/conversions.rb +34 -13
  81. data/lib/active_support/core_ext/range/each.rb +1 -1
  82. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  83. data/lib/active_support/core_ext/range.rb +1 -2
  84. data/lib/active_support/core_ext/securerandom.rb +25 -13
  85. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  86. data/lib/active_support/core_ext/string/filters.rb +21 -15
  87. data/lib/active_support/core_ext/string/indent.rb +1 -1
  88. data/lib/active_support/core_ext/string/inflections.rb +17 -10
  89. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  90. data/lib/active_support/core_ext/string/output_safety.rb +85 -193
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  92. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +29 -10
  94. data/lib/active_support/core_ext/time/conversions.rb +14 -13
  95. data/lib/active_support/core_ext/time/zones.rb +12 -28
  96. data/lib/active_support/core_ext.rb +1 -0
  97. data/lib/active_support/current_attributes.rb +46 -21
  98. data/lib/active_support/deep_mergeable.rb +53 -0
  99. data/lib/active_support/dependencies/autoload.rb +17 -12
  100. data/lib/active_support/dependencies/interlock.rb +10 -18
  101. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  102. data/lib/active_support/dependencies.rb +58 -788
  103. data/lib/active_support/deprecation/behaviors.rb +66 -40
  104. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  105. data/lib/active_support/deprecation/deprecators.rb +104 -0
  106. data/lib/active_support/deprecation/disallowed.rb +6 -8
  107. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  108. data/lib/active_support/deprecation/method_wrappers.rb +9 -26
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +38 -23
  110. data/lib/active_support/deprecation/reporting.rb +42 -25
  111. data/lib/active_support/deprecation.rb +32 -5
  112. data/lib/active_support/deprecator.rb +7 -0
  113. data/lib/active_support/descendants_tracker.rb +150 -72
  114. data/lib/active_support/digest.rb +4 -4
  115. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  116. data/lib/active_support/duration/iso8601_serializer.rb +9 -3
  117. data/lib/active_support/duration.rb +79 -49
  118. data/lib/active_support/encrypted_configuration.rb +72 -9
  119. data/lib/active_support/encrypted_file.rb +29 -13
  120. data/lib/active_support/environment_inquirer.rb +23 -3
  121. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  122. data/lib/active_support/error_reporter.rb +203 -0
  123. data/lib/active_support/evented_file_update_checker.rb +20 -7
  124. data/lib/active_support/execution_context/test_helper.rb +13 -0
  125. data/lib/active_support/execution_context.rb +53 -0
  126. data/lib/active_support/execution_wrapper.rb +31 -12
  127. data/lib/active_support/executor/test_helper.rb +7 -0
  128. data/lib/active_support/file_update_checker.rb +4 -2
  129. data/lib/active_support/fork_tracker.rb +28 -13
  130. data/lib/active_support/gem_version.rb +4 -4
  131. data/lib/active_support/gzip.rb +2 -0
  132. data/lib/active_support/hash_with_indifferent_access.rb +38 -18
  133. data/lib/active_support/html_safe_translation.rb +43 -0
  134. data/lib/active_support/i18n.rb +2 -1
  135. data/lib/active_support/i18n_railtie.rb +21 -14
  136. data/lib/active_support/inflector/inflections.rb +25 -7
  137. data/lib/active_support/inflector/methods.rb +50 -63
  138. data/lib/active_support/inflector/transliterate.rb +4 -2
  139. data/lib/active_support/isolated_execution_state.rb +76 -0
  140. data/lib/active_support/json/decoding.rb +2 -1
  141. data/lib/active_support/json/encoding.rb +27 -45
  142. data/lib/active_support/key_generator.rb +31 -6
  143. data/lib/active_support/lazy_load_hooks.rb +33 -7
  144. data/lib/active_support/locale/en.yml +3 -1
  145. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  146. data/lib/active_support/log_subscriber.rb +96 -35
  147. data/lib/active_support/logger.rb +9 -60
  148. data/lib/active_support/logger_thread_safe_level.rb +11 -34
  149. data/lib/active_support/message_encryptor.rb +206 -56
  150. data/lib/active_support/message_encryptors.rb +141 -0
  151. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  152. data/lib/active_support/message_pack/extensions.rb +292 -0
  153. data/lib/active_support/message_pack/serializer.rb +63 -0
  154. data/lib/active_support/message_pack.rb +50 -0
  155. data/lib/active_support/message_verifier.rb +235 -84
  156. data/lib/active_support/message_verifiers.rb +135 -0
  157. data/lib/active_support/messages/codec.rb +65 -0
  158. data/lib/active_support/messages/metadata.rb +112 -46
  159. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  160. data/lib/active_support/messages/rotator.rb +34 -32
  161. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  162. data/lib/active_support/multibyte/chars.rb +12 -11
  163. data/lib/active_support/multibyte/unicode.rb +9 -49
  164. data/lib/active_support/multibyte.rb +1 -1
  165. data/lib/active_support/notifications/fanout.rb +304 -114
  166. data/lib/active_support/notifications/instrumenter.rb +109 -35
  167. data/lib/active_support/notifications.rb +24 -24
  168. data/lib/active_support/number_helper/number_converter.rb +14 -7
  169. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  170. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  171. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -4
  172. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  173. data/lib/active_support/number_helper/rounding_helper.rb +1 -5
  174. data/lib/active_support/number_helper.rb +379 -319
  175. data/lib/active_support/option_merger.rb +10 -18
  176. data/lib/active_support/ordered_hash.rb +4 -4
  177. data/lib/active_support/ordered_options.rb +15 -1
  178. data/lib/active_support/parameter_filter.rb +104 -80
  179. data/lib/active_support/proxy_object.rb +2 -0
  180. data/lib/active_support/railtie.rb +83 -21
  181. data/lib/active_support/reloader.rb +12 -4
  182. data/lib/active_support/rescuable.rb +14 -12
  183. data/lib/active_support/ruby_features.rb +7 -0
  184. data/lib/active_support/secure_compare_rotator.rb +18 -11
  185. data/lib/active_support/string_inquirer.rb +3 -3
  186. data/lib/active_support/subscriber.rb +11 -40
  187. data/lib/active_support/syntax_error_proxy.rb +70 -0
  188. data/lib/active_support/tagged_logging.rb +60 -24
  189. data/lib/active_support/test_case.rb +166 -27
  190. data/lib/active_support/testing/assertions.rb +60 -14
  191. data/lib/active_support/testing/autorun.rb +0 -2
  192. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  193. data/lib/active_support/testing/deprecation.rb +53 -2
  194. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  195. data/lib/active_support/testing/isolation.rb +30 -29
  196. data/lib/active_support/testing/method_call_assertions.rb +24 -11
  197. data/lib/active_support/testing/parallelization/server.rb +4 -0
  198. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  199. data/lib/active_support/testing/parallelization.rb +4 -0
  200. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  201. data/lib/active_support/testing/stream.rb +4 -6
  202. data/lib/active_support/testing/strict_warnings.rb +39 -0
  203. data/lib/active_support/testing/tagged_logging.rb +1 -1
  204. data/lib/active_support/testing/time_helpers.rb +49 -16
  205. data/lib/active_support/time_with_zone.rb +39 -28
  206. data/lib/active_support/values/time_zone.rb +38 -17
  207. data/lib/active_support/version.rb +1 -1
  208. data/lib/active_support/xml_mini/jdom.rb +4 -11
  209. data/lib/active_support/xml_mini/libxml.rb +5 -5
  210. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  211. data/lib/active_support/xml_mini/nokogiri.rb +5 -5
  212. data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
  213. data/lib/active_support/xml_mini/rexml.rb +2 -2
  214. data/lib/active_support/xml_mini.rb +7 -6
  215. data/lib/active_support.rb +28 -1
  216. metadata +107 -18
  217. data/lib/active_support/core_ext/marshal.rb +0 -26
  218. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -28
  219. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  220. data/lib/active_support/core_ext/uri.rb +0 -29
  221. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -120
  222. data/lib/active_support/per_thread_registry.rb +0 -61
@@ -5,10 +5,10 @@ class Hash
5
5
  # This includes the values from the root hash and from all
6
6
  # nested hashes and arrays.
7
7
  #
8
- # hash = { person: { name: 'Rob', age: '28' } }
8
+ # hash = { person: { name: 'Rob', age: '28' } }
9
9
  #
10
- # hash.deep_transform_values{ |value| value.to_s.upcase }
11
- # # => {person: {name: "ROB", age: "28"}}
10
+ # hash.deep_transform_values{ |value| value.to_s.upcase }
11
+ # # => {person: {name: "ROB", age: "28"}}
12
12
  def deep_transform_values(&block)
13
13
  _deep_transform_values_in_object(self, &block)
14
14
  end
@@ -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 }
@@ -58,10 +58,10 @@ class Hash
58
58
  # This includes the keys from the root hash and from all
59
59
  # nested hashes and arrays.
60
60
  #
61
- # hash = { person: { name: 'Rob', age: '28' } }
61
+ # hash = { person: { name: 'Rob', age: '28' } }
62
62
  #
63
- # hash.deep_transform_keys{ |key| key.to_s.upcase }
64
- # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
63
+ # hash.deep_transform_keys{ |key| key.to_s.upcase }
64
+ # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
65
65
  def deep_transform_keys(&block)
66
66
  _deep_transform_keys_in_object(self, &block)
67
67
  end
@@ -116,7 +116,7 @@ class Hash
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
@@ -6,12 +6,12 @@ class Integer
6
6
  # Ordinalize turns a number into an ordinal string used to denote the
7
7
  # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
8
8
  #
9
- # 1.ordinalize # => "1st"
10
- # 2.ordinalize # => "2nd"
11
- # 1002.ordinalize # => "1002nd"
12
- # 1003.ordinalize # => "1003rd"
13
- # -11.ordinalize # => "-11th"
14
- # -1001.ordinalize # => "-1001st"
9
+ # 1.ordinalize # => "1st"
10
+ # 2.ordinalize # => "2nd"
11
+ # 1002.ordinalize # => "1002nd"
12
+ # 1003.ordinalize # => "1003rd"
13
+ # -11.ordinalize # => "-11th"
14
+ # -1001.ordinalize # => "-1001st"
15
15
  def ordinalize
16
16
  ActiveSupport::Inflector.ordinalize(self)
17
17
  end
@@ -19,12 +19,12 @@ class Integer
19
19
  # Ordinal returns the suffix used to denote the position
20
20
  # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
21
21
  #
22
- # 1.ordinal # => "st"
23
- # 2.ordinal # => "nd"
24
- # 1002.ordinal # => "nd"
25
- # 1003.ordinal # => "rd"
26
- # -11.ordinal # => "th"
27
- # -1001.ordinal # => "st"
22
+ # 1.ordinal # => "st"
23
+ # 2.ordinal # => "nd"
24
+ # 1002.ordinal # => "nd"
25
+ # 1003.ordinal # => "rd"
26
+ # -11.ordinal # => "th"
27
+ # -1001.ordinal # => "st"
28
28
  def ordinal
29
29
  ActiveSupport::Inflector.ordinal(self)
30
30
  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
@@ -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.
@@ -41,6 +43,7 @@ class Module
41
43
  #
42
44
  # module HairColors
43
45
  # mattr_reader :hair_colors, default: [:brown, :black, :blonde, :red]
46
+ # mattr_reader(:hair_styles) { [:long, :short] }
44
47
  # end
45
48
  #
46
49
  # class Person
@@ -48,6 +51,7 @@ class Module
48
51
  # end
49
52
  #
50
53
  # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
54
+ # Person.new.hair_styles # => [:long, :short]
51
55
  def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
52
56
  raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
53
57
  location ||= caller_locations(1, 1).first
@@ -105,6 +109,7 @@ class Module
105
109
  #
106
110
  # module HairColors
107
111
  # mattr_writer :hair_colors, default: [:brown, :black, :blonde, :red]
112
+ # mattr_writer(:hair_styles) { [:long, :short] }
108
113
  # end
109
114
  #
110
115
  # class Person
@@ -112,6 +117,7 @@ class Module
112
117
  # end
113
118
  #
114
119
  # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
120
+ # Person.class_variable_get("@@hair_styles") # => [:long, :short]
115
121
  def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
116
122
  raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
117
123
  location ||= caller_locations(1, 1).first
@@ -190,6 +196,7 @@ class Module
190
196
  #
191
197
  # module HairColors
192
198
  # mattr_accessor :hair_colors, default: [:brown, :black, :blonde, :red]
199
+ # mattr_accessor(:hair_styles) { [:long, :short] }
193
200
  # end
194
201
  #
195
202
  # class Person
@@ -197,6 +204,7 @@ class Module
197
204
  # end
198
205
  #
199
206
  # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
207
+ # Person.class_variable_get("@@hair_styles") # => [:long, :short]
200
208
  def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
201
209
  location = caller_locations(1, 1).first
202
210
  mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
@@ -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
  #
@@ -37,13 +42,32 @@ class Module
37
42
  syms.each do |sym|
38
43
  raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
39
44
 
40
- # The following generated method concatenates `name` because we want it
41
- # to work with inheritance via polymorphism.
42
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
43
- def self.#{sym}
44
- Thread.current["attr_" + name + "_#{sym}"]
45
- end
46
- EOS
45
+ # The following generated method concatenates `object_id` because we want
46
+ # subclasses to maintain independent values.
47
+ if default.nil?
48
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
49
+ def self.#{sym}
50
+ @__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
51
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
52
+ end
53
+ EOS
54
+ else
55
+ default = default.dup.freeze unless default.frozen?
56
+ singleton_class.define_method("#{sym}_default_value") { default }
57
+
58
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
59
+ def self.#{sym}
60
+ @__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
61
+ value = ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
62
+
63
+ if value.nil? && !::ActiveSupport::IsolatedExecutionState.key?(@__thread_mattr_#{sym})
64
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = #{sym}_default_value
65
+ else
66
+ value
67
+ end
68
+ end
69
+ EOS
70
+ end
47
71
 
48
72
  if instance_reader && instance_accessor
49
73
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@ -52,8 +76,6 @@ class Module
52
76
  end
53
77
  EOS
54
78
  end
55
-
56
- Thread.current["attr_" + name + "_#{sym}"] = default unless default.nil?
57
79
  end
58
80
  end
59
81
  alias :thread_cattr_reader :thread_mattr_reader
@@ -76,15 +98,16 @@ class Module
76
98
  # end
77
99
  #
78
100
  # Current.new.user = "DHH" # => NoMethodError
79
- def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
101
+ def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true) # :nodoc:
80
102
  syms.each do |sym|
81
103
  raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
82
104
 
83
- # The following generated method concatenates `name` because we want it
84
- # to work with inheritance via polymorphism.
105
+ # The following generated method concatenates `object_id` because we want
106
+ # subclasses to maintain independent values.
85
107
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
86
108
  def self.#{sym}=(obj)
87
- Thread.current["attr_" + name + "_#{sym}"] = obj
109
+ @__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
110
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj
88
111
  end
89
112
  EOS
90
113
 
@@ -95,8 +118,6 @@ class Module
95
118
  end
96
119
  EOS
97
120
  end
98
-
99
- public_send("#{sym}=", default) unless default.nil?
100
121
  end
101
122
  end
102
123
  alias :thread_cattr_writer :thread_mattr_writer
@@ -111,16 +132,18 @@ class Module
111
132
  # Account.user # => "DHH"
112
133
  # Account.new.user # => "DHH"
113
134
  #
135
+ # Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes.
114
136
  # If a subclass changes the value, the parent class' value is not changed.
115
- # Similarly, if the parent class changes the value, the value of subclasses
116
- # is not changed.
137
+ # If the parent class changes the value, the value of subclasses is not changed.
117
138
  #
118
139
  # class Customer < Account
119
140
  # end
120
141
  #
121
- # Customer.user = "Rafael"
122
- # Customer.user # => "Rafael"
123
- # Account.user # => "DHH"
142
+ # Account.user # => "DHH"
143
+ # Customer.user # => nil
144
+ # Customer.user = "Rafael"
145
+ # Customer.user # => "Rafael"
146
+ # Account.user # => "DHH"
124
147
  #
125
148
  # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
126
149
  # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
@@ -140,6 +163,10 @@ class Module
140
163
  #
141
164
  # Current.new.user = "DHH" # => NoMethodError
142
165
  # Current.new.user # => NoMethodError
166
+ #
167
+ # A default value may be specified using the +:default+ option. Because
168
+ # multiple threads can access the default value, non-frozen default values
169
+ # will be <tt>dup</tt>ed and frozen.
143
170
  def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
144
171
  thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
145
172
  thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
@@ -3,7 +3,7 @@
3
3
  require "active_support/concern"
4
4
 
5
5
  class Module
6
- # = Bite-sized separation of concerns
6
+ # == Bite-sized separation of concerns
7
7
  #
8
8
  # We often find ourselves with a medium-sized chunk of behavior that we'd
9
9
  # like to extract, but only mix in to a single class.
@@ -18,9 +18,9 @@ class Module
18
18
  # with a comment, as a least-bad alternative. Using modules in separate files
19
19
  # means tedious sifting to get a big-picture view.
20
20
  #
21
- # = Dissatisfying ways to separate small concerns
21
+ # == Dissatisfying ways to separate small concerns
22
22
  #
23
- # == Using comments:
23
+ # === Using comments:
24
24
  #
25
25
  # class Todo < ApplicationRecord
26
26
  # # Other todo implementation
@@ -37,7 +37,7 @@ class Module
37
37
  # end
38
38
  # end
39
39
  #
40
- # == With an inline module:
40
+ # === With an inline module:
41
41
  #
42
42
  # Noisy syntax.
43
43
  #
@@ -61,7 +61,7 @@ class Module
61
61
  # include EventTracking
62
62
  # end
63
63
  #
64
- # == Mix-in noise exiled to its own file:
64
+ # === Mix-in noise exiled to its own file:
65
65
  #
66
66
  # Once our chunk of behavior starts pushing the scroll-to-understand-it
67
67
  # boundary, we give in and move it to a separate file. At this size, the
@@ -75,7 +75,7 @@ class Module
75
75
  # include TodoEventTracking
76
76
  # end
77
77
  #
78
- # = Introducing Module#concerning
78
+ # == Introducing Module#concerning
79
79
  #
80
80
  # By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
81
81
  # separate bite-sized concerns.
@@ -7,9 +7,9 @@ class Module
7
7
  # option is not used.
8
8
  class DelegationError < NoMethodError; end
9
9
 
10
- RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do
11
- else elsif END end ensure false for if in module next nil not or redo rescue retry
12
- return self super then true undef unless until when while yield)
10
+ RUBY_RESERVED_KEYWORDS = %w(__ENCODING__ __LINE__ __FILE__ alias and BEGIN begin break
11
+ case class def defined? do else elsif END end ensure false for if in module next nil
12
+ not or redo rescue retry return self super then true undef unless until when while yield)
13
13
  DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
14
14
  DELEGATION_RESERVED_METHOD_NAMES = Set.new(
15
15
  RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
@@ -187,25 +187,49 @@ class Module
187
187
  location = caller_locations(1, 1).first
188
188
  file, line = location.path, location.lineno
189
189
 
190
- to = to.to_s
191
- to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
190
+ receiver = to.to_s
191
+ receiver = "self.#{receiver}" if DELEGATION_RESERVED_METHOD_NAMES.include?(receiver)
192
192
 
193
193
  method_def = []
194
194
  method_names = []
195
195
 
196
- methods.map do |method|
196
+ method_def << "self.private" if private
197
+
198
+ methods.each do |method|
197
199
  method_name = prefix ? "#{method_prefix}#{method}" : method
198
200
  method_names << method_name.to_sym
199
201
 
200
202
  # Attribute writer methods only accept one argument. Makes sure []=
201
203
  # methods still accept two arguments.
202
- definition = if /[^\]]=$/.match?(method)
203
- "arg"
204
- elsif RUBY_VERSION >= "2.7"
205
- "..."
206
- else
207
- "*args, &block"
208
- end
204
+ definition = \
205
+ if /[^\]]=\z/.match?(method)
206
+ "arg"
207
+ else
208
+ method_object =
209
+ begin
210
+ if to.is_a?(Module)
211
+ to.method(method)
212
+ elsif receiver == "self.class"
213
+ method(method)
214
+ end
215
+ rescue NameError
216
+ # Do nothing. Fall back to `"..."`
217
+ end
218
+
219
+ if method_object
220
+ parameters = method_object.parameters
221
+
222
+ if (parameters.map(&:first) & [:opt, :rest, :keyreq, :key, :keyrest]).any?
223
+ "..."
224
+ else
225
+ defn = parameters.filter_map { |type, arg| arg if type == :req }
226
+ defn << "&block"
227
+ defn.join(", ")
228
+ end
229
+ else
230
+ "..."
231
+ end
232
+ end
209
233
 
210
234
  # The following generated method calls the target exactly once, storing
211
235
  # the returned value in a dummy variable.
@@ -219,7 +243,7 @@ class Module
219
243
 
220
244
  method_def <<
221
245
  "def #{method_name}(#{definition})" <<
222
- " _ = #{to}" <<
246
+ " _ = #{receiver}" <<
223
247
  " if !_.nil? || nil.respond_to?(:#{method})" <<
224
248
  " _.#{method}(#{definition})" <<
225
249
  " end" <<
@@ -230,11 +254,11 @@ class Module
230
254
 
231
255
  method_def <<
232
256
  "def #{method_name}(#{definition})" <<
233
- " _ = #{to}" <<
257
+ " _ = #{receiver}" <<
234
258
  " _.#{method}(#{definition})" <<
235
259
  "rescue NoMethodError => e" <<
236
260
  " if _.nil? && e.name == :#{method}" <<
237
- %( raise DelegationError, "#{self}##{method_name} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") <<
261
+ %( raise DelegationError, "#{self}##{method_name} delegated to #{receiver}.#{method}, but #{receiver} is nil: \#{self.inspect}") <<
238
262
  " else" <<
239
263
  " raise" <<
240
264
  " end" <<
@@ -242,7 +266,6 @@ class Module
242
266
  end
243
267
  end
244
268
  module_eval(method_def.join(";"), file, line)
245
- private(*method_names) if private
246
269
  method_names
247
270
  end
248
271
 
@@ -324,7 +347,7 @@ class Module
324
347
  end
325
348
  end
326
349
  end
327
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
350
+ ruby2_keywords(:method_missing)
328
351
  RUBY
329
352
  end
330
353
  end
@@ -1,17 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Module
4
- # deprecate :foo
5
- # deprecate bar: 'message'
6
- # deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
4
+ # deprecate :foo, deprecator: MyLib.deprecator
5
+ # deprecate :foo, bar: "warning!", deprecator: MyLib.deprecator
7
6
  #
8
- # You can also use custom deprecator instance:
9
- #
10
- # deprecate :foo, deprecator: MyLib::Deprecator.new
11
- # deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
12
- #
13
- # \Custom deprecators must respond to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt>
14
- # method where you can implement your custom warning behavior.
7
+ # A deprecator is typically an instance of ActiveSupport::Deprecation, but you can also pass any object that responds
8
+ # to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt> where you can implement your
9
+ # custom warning behavior.
15
10
  #
16
11
  # class MyLib::Deprecator
17
12
  # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
@@ -19,7 +14,15 @@ class Module
19
14
  # Kernel.warn message
20
15
  # end
21
16
  # end
22
- def deprecate(*method_names)
23
- ActiveSupport::Deprecation.deprecate_methods(self, *method_names)
17
+ def deprecate(*method_names, deprecator: nil, **options)
18
+ if deprecator.is_a?(ActiveSupport::Deprecation)
19
+ deprecator.deprecate_methods(self, *method_names, **options)
20
+ elsif deprecator
21
+ # we just need any instance to call deprecate_methods, but the deprecation will be emitted by deprecator
22
+ ActiveSupport.deprecator.deprecate_methods(self, *method_names, **options, deprecator: deprecator)
23
+ else
24
+ ActiveSupport.deprecator.warn("Module.deprecate without a deprecator is deprecated")
25
+ ActiveSupport::Deprecation._instance.deprecate_methods(self, *method_names, **options)
26
+ end
24
27
  end
25
28
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/string/filters"
4
3
  require "active_support/inflector"
5
4
 
6
5
  class Module
@@ -53,13 +53,7 @@ class NameError
53
53
  UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
54
54
  private_constant :UNBOUND_METHOD_MODULE_NAME
55
55
 
56
- if UnboundMethod.method_defined?(:bind_call)
57
- def real_mod_name(mod)
58
- UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
59
- end
60
- else
61
- def real_mod_name(mod)
62
- UNBOUND_METHOD_MODULE_NAME.bind(mod).call
63
- end
56
+ def real_mod_name(mod)
57
+ UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
64
58
  end
65
59
  end
@@ -7,6 +7,7 @@ class Numeric
7
7
  TERABYTE = GIGABYTE * 1024
8
8
  PETABYTE = TERABYTE * 1024
9
9
  EXABYTE = PETABYTE * 1024
10
+ ZETTABYTE = EXABYTE * 1024
10
11
 
11
12
  # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
12
13
  #
@@ -63,4 +64,12 @@ class Numeric
63
64
  self * EXABYTE
64
65
  end
65
66
  alias :exabyte :exabytes
67
+
68
+ # Returns the number of bytes equivalent to the zettabytes provided.
69
+ #
70
+ # 2.zettabytes # => 2_361_183_241_434_822_606_848
71
+ def zettabytes
72
+ self * ZETTABYTE
73
+ end
74
+ alias :zettabyte :zettabytes
66
75
  end