activesupport 7.0.4 → 7.1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1076 -230
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -6
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +2 -0
  7. data/lib/active_support/backtrace_cleaner.rb +30 -5
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/broadcast_logger.rb +251 -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 +37 -10
  14. data/lib/active_support/cache/mem_cache_store.rb +100 -76
  15. data/lib/active_support/cache/memory_store.rb +78 -24
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +153 -141
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +29 -14
  20. data/lib/active_support/cache.rb +333 -253
  21. data/lib/active_support/callbacks.rb +44 -21
  22. data/lib/active_support/code_generator.rb +15 -10
  23. data/lib/active_support/concern.rb +4 -2
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/configurable.rb +10 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +2 -1
  28. data/lib/active_support/core_ext/array.rb +0 -1
  29. data/lib/active_support/core_ext/class/subclasses.rb +13 -10
  30. data/lib/active_support/core_ext/date/calculations.rb +15 -0
  31. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  32. data/lib/active_support/core_ext/date.rb +0 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  34. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  35. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  36. data/lib/active_support/core_ext/date_time.rb +0 -1
  37. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  38. data/lib/active_support/core_ext/enumerable.rb +8 -75
  39. data/lib/active_support/core_ext/erb/util.rb +196 -0
  40. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  41. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  42. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  43. data/lib/active_support/core_ext/hash/keys.rb +3 -3
  44. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  45. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  46. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  47. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  48. data/lib/active_support/core_ext/module/delegation.rb +81 -37
  49. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  50. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  51. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  52. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  53. data/lib/active_support/core_ext/numeric.rb +0 -1
  54. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  55. data/lib/active_support/core_ext/object/duplicable.rb +25 -16
  56. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  57. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  58. data/lib/active_support/core_ext/object/json.rb +16 -6
  59. data/lib/active_support/core_ext/object/to_query.rb +0 -2
  60. data/lib/active_support/core_ext/object/with.rb +44 -0
  61. data/lib/active_support/core_ext/object/with_options.rb +9 -9
  62. data/lib/active_support/core_ext/object.rb +1 -0
  63. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  64. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  65. data/lib/active_support/core_ext/pathname.rb +1 -0
  66. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  67. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  68. data/lib/active_support/core_ext/range.rb +1 -2
  69. data/lib/active_support/core_ext/securerandom.rb +24 -12
  70. data/lib/active_support/core_ext/string/filters.rb +20 -14
  71. data/lib/active_support/core_ext/string/indent.rb +1 -1
  72. data/lib/active_support/core_ext/string/inflections.rb +16 -9
  73. data/lib/active_support/core_ext/string/output_safety.rb +42 -174
  74. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  75. data/lib/active_support/core_ext/time/calculations.rb +22 -2
  76. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  77. data/lib/active_support/core_ext/time/zones.rb +7 -8
  78. data/lib/active_support/core_ext/time.rb +0 -1
  79. data/lib/active_support/current_attributes.rb +15 -6
  80. data/lib/active_support/deep_mergeable.rb +53 -0
  81. data/lib/active_support/dependencies/autoload.rb +17 -12
  82. data/lib/active_support/deprecation/behaviors.rb +65 -42
  83. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  84. data/lib/active_support/deprecation/deprecators.rb +104 -0
  85. data/lib/active_support/deprecation/disallowed.rb +6 -8
  86. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  87. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  88. data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
  89. data/lib/active_support/deprecation/reporting.rb +43 -26
  90. data/lib/active_support/deprecation.rb +32 -5
  91. data/lib/active_support/deprecator.rb +7 -0
  92. data/lib/active_support/descendants_tracker.rb +104 -132
  93. data/lib/active_support/duration/iso8601_serializer.rb +0 -2
  94. data/lib/active_support/duration.rb +2 -1
  95. data/lib/active_support/encrypted_configuration.rb +63 -11
  96. data/lib/active_support/encrypted_file.rb +16 -12
  97. data/lib/active_support/environment_inquirer.rb +22 -2
  98. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  99. data/lib/active_support/error_reporter.rb +121 -35
  100. data/lib/active_support/evented_file_update_checker.rb +17 -2
  101. data/lib/active_support/execution_wrapper.rb +4 -4
  102. data/lib/active_support/file_update_checker.rb +4 -2
  103. data/lib/active_support/fork_tracker.rb +10 -2
  104. data/lib/active_support/gem_version.rb +4 -4
  105. data/lib/active_support/gzip.rb +2 -0
  106. data/lib/active_support/hash_with_indifferent_access.rb +35 -17
  107. data/lib/active_support/html_safe_translation.rb +16 -6
  108. data/lib/active_support/i18n.rb +1 -1
  109. data/lib/active_support/i18n_railtie.rb +20 -13
  110. data/lib/active_support/inflector/inflections.rb +2 -0
  111. data/lib/active_support/inflector/methods.rb +28 -18
  112. data/lib/active_support/inflector/transliterate.rb +3 -1
  113. data/lib/active_support/isolated_execution_state.rb +26 -22
  114. data/lib/active_support/json/decoding.rb +2 -1
  115. data/lib/active_support/json/encoding.rb +25 -43
  116. data/lib/active_support/key_generator.rb +9 -1
  117. data/lib/active_support/lazy_load_hooks.rb +7 -5
  118. data/lib/active_support/locale/en.yml +2 -0
  119. data/lib/active_support/log_subscriber.rb +85 -33
  120. data/lib/active_support/logger.rb +9 -60
  121. data/lib/active_support/logger_thread_safe_level.rb +10 -24
  122. data/lib/active_support/message_encryptor.rb +197 -53
  123. data/lib/active_support/message_encryptors.rb +141 -0
  124. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  125. data/lib/active_support/message_pack/extensions.rb +292 -0
  126. data/lib/active_support/message_pack/serializer.rb +63 -0
  127. data/lib/active_support/message_pack.rb +50 -0
  128. data/lib/active_support/message_verifier.rb +212 -93
  129. data/lib/active_support/message_verifiers.rb +135 -0
  130. data/lib/active_support/messages/codec.rb +65 -0
  131. data/lib/active_support/messages/metadata.rb +111 -45
  132. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  133. data/lib/active_support/messages/rotator.rb +34 -32
  134. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  135. data/lib/active_support/multibyte/chars.rb +2 -0
  136. data/lib/active_support/multibyte/unicode.rb +9 -37
  137. data/lib/active_support/notifications/fanout.rb +245 -81
  138. data/lib/active_support/notifications/instrumenter.rb +87 -22
  139. data/lib/active_support/notifications.rb +3 -3
  140. data/lib/active_support/number_helper/number_converter.rb +14 -5
  141. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  142. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  143. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  144. data/lib/active_support/number_helper.rb +379 -317
  145. data/lib/active_support/ordered_hash.rb +3 -3
  146. data/lib/active_support/ordered_options.rb +14 -0
  147. data/lib/active_support/parameter_filter.rb +103 -84
  148. data/lib/active_support/proxy_object.rb +2 -0
  149. data/lib/active_support/railtie.rb +33 -21
  150. data/lib/active_support/reloader.rb +12 -4
  151. data/lib/active_support/rescuable.rb +2 -0
  152. data/lib/active_support/secure_compare_rotator.rb +16 -9
  153. data/lib/active_support/string_inquirer.rb +3 -1
  154. data/lib/active_support/subscriber.rb +9 -27
  155. data/lib/active_support/syntax_error_proxy.rb +60 -0
  156. data/lib/active_support/tagged_logging.rb +64 -24
  157. data/lib/active_support/test_case.rb +153 -6
  158. data/lib/active_support/testing/assertions.rb +26 -10
  159. data/lib/active_support/testing/autorun.rb +0 -2
  160. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  161. data/lib/active_support/testing/deprecation.rb +25 -25
  162. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  163. data/lib/active_support/testing/isolation.rb +29 -28
  164. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  165. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  166. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  167. data/lib/active_support/testing/stream.rb +1 -1
  168. data/lib/active_support/testing/strict_warnings.rb +39 -0
  169. data/lib/active_support/testing/time_helpers.rb +37 -15
  170. data/lib/active_support/time_with_zone.rb +8 -37
  171. data/lib/active_support/values/time_zone.rb +18 -7
  172. data/lib/active_support/version.rb +1 -1
  173. data/lib/active_support/xml_mini/jdom.rb +3 -10
  174. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  175. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  176. data/lib/active_support/xml_mini/rexml.rb +1 -1
  177. data/lib/active_support/xml_mini.rb +2 -2
  178. data/lib/active_support.rb +14 -3
  179. metadata +148 -19
  180. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  181. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -26
  182. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -22
  183. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  184. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -26
  185. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -7
  186. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  187. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -22
  188. data/lib/active_support/core_ext/uri.rb +0 -5
  189. data/lib/active_support/per_thread_registry.rb +0 -65
@@ -6,14 +6,16 @@ require "logger"
6
6
  require "active_support/logger"
7
7
 
8
8
  module ActiveSupport
9
+ # = Active Support Tagged Logging
10
+ #
9
11
  # Wraps any standard Logger object to provide tagging capabilities.
10
12
  #
11
13
  # May be called with a block:
12
14
  #
13
15
  # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
14
- # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
15
- # logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
16
- # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
16
+ # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
17
+ # logger.tagged('BCX', "Jason") { |tagged_logger| tagged_logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
18
+ # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
17
19
  #
18
20
  # If called without a block, a new logger will be returned with applied tags:
19
21
  #
@@ -29,52 +31,86 @@ module ActiveSupport
29
31
  module Formatter # :nodoc:
30
32
  # This method is invoked when a log event occurs.
31
33
  def call(severity, timestamp, progname, msg)
32
- super(severity, timestamp, progname, "#{tags_text}#{msg}")
34
+ super(severity, timestamp, progname, tag_stack.format_message(msg))
33
35
  end
34
36
 
35
37
  def tagged(*tags)
36
- new_tags = push_tags(*tags)
38
+ pushed_count = tag_stack.push_tags(tags).size
37
39
  yield self
38
40
  ensure
39
- pop_tags(new_tags.size)
41
+ pop_tags(pushed_count)
40
42
  end
41
43
 
42
44
  def push_tags(*tags)
43
- tags.flatten!
44
- tags.reject!(&:blank?)
45
- current_tags.concat tags
46
- tags
45
+ tag_stack.push_tags(tags)
47
46
  end
48
47
 
49
- def pop_tags(size = 1)
50
- current_tags.pop size
48
+ def pop_tags(count = 1)
49
+ tag_stack.pop_tags(count)
51
50
  end
52
51
 
53
52
  def clear_tags!
54
- current_tags.clear
53
+ tag_stack.clear
55
54
  end
56
55
 
57
- def current_tags
56
+ def tag_stack
58
57
  # We use our object ID here to avoid conflicting with other instances
59
- thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
60
- IsolatedExecutionState[thread_key] ||= []
58
+ @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
59
+ IsolatedExecutionState[@thread_key] ||= TagStack.new
60
+ end
61
+
62
+ def current_tags
63
+ tag_stack.tags
61
64
  end
62
65
 
63
66
  def tags_text
64
- tags = current_tags
65
- if tags.one?
66
- "[#{tags[0]}] "
67
- elsif tags.any?
68
- tags.collect { |tag| "[#{tag}] " }.join
67
+ tag_stack.format_message("")
68
+ end
69
+ end
70
+
71
+ class TagStack # :nodoc:
72
+ attr_reader :tags
73
+
74
+ def initialize
75
+ @tags = []
76
+ @tags_string = nil
77
+ end
78
+
79
+ def push_tags(tags)
80
+ @tags_string = nil
81
+ tags.flatten!
82
+ tags.reject!(&:blank?)
83
+ @tags.concat(tags)
84
+ tags
85
+ end
86
+
87
+ def pop_tags(count)
88
+ @tags_string = nil
89
+ @tags.pop(count)
90
+ end
91
+
92
+ def clear
93
+ @tags_string = nil
94
+ @tags.clear
95
+ end
96
+
97
+ def format_message(message)
98
+ if @tags.empty?
99
+ message
100
+ elsif @tags.size == 1
101
+ "[#{@tags[0]}] #{message}"
102
+ else
103
+ @tags_string ||= "[#{@tags.join("] [")}] "
104
+ "#{@tags_string}#{message}"
69
105
  end
70
106
  end
71
107
  end
72
108
 
73
109
  module LocalTagStorage # :nodoc:
74
- attr_accessor :current_tags
110
+ attr_accessor :tag_stack
75
111
 
76
112
  def self.extended(base)
77
- base.current_tags = []
113
+ base.tag_stack = TagStack.new
78
114
  end
79
115
  end
80
116
 
@@ -82,7 +118,11 @@ module ActiveSupport
82
118
  logger = logger.clone
83
119
 
84
120
  if logger.formatter
85
- logger.formatter = logger.formatter.dup
121
+ logger.formatter = logger.formatter.clone
122
+
123
+ # Workaround for https://bugs.ruby-lang.org/issues/20250
124
+ # Can be removed when Ruby 3.4 is the least supported version.
125
+ logger.formatter.object_id if logger.formatter.is_a?(Proc)
86
126
  else
87
127
  # Ensure we set a default formatter so we aren't extending nil!
88
128
  logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
@@ -1,15 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- gem "minitest" # make sure we get the gem, not stdlib
4
3
  require "minitest"
5
4
  require "active_support/testing/tagged_logging"
6
5
  require "active_support/testing/setup_and_teardown"
7
6
  require "active_support/testing/assertions"
7
+ require "active_support/testing/error_reporter_assertions"
8
8
  require "active_support/testing/deprecation"
9
9
  require "active_support/testing/declarative"
10
10
  require "active_support/testing/isolation"
11
11
  require "active_support/testing/constant_lookup"
12
12
  require "active_support/testing/time_helpers"
13
+ require "active_support/testing/constant_stubbing"
13
14
  require "active_support/testing/file_fixtures"
14
15
  require "active_support/testing/parallelization"
15
16
  require "active_support/testing/parallelize_executor"
@@ -66,7 +67,7 @@ module ActiveSupport
66
67
  # The default parallelization method is to fork processes. If you'd like to
67
68
  # use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+
68
69
  # method. Note the threaded parallelization does not create multiple
69
- # database and will not work with system tests at this time.
70
+ # databases and will not work with system tests.
70
71
  #
71
72
  # parallelize(workers: :number_of_processors, with: :threads)
72
73
  #
@@ -80,8 +81,6 @@ module ActiveSupport
80
81
  workers = Concurrent.physical_processor_count if workers == :number_of_processors
81
82
  workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
82
83
 
83
- return if workers <= 1
84
-
85
84
  Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
86
85
  end
87
86
 
@@ -118,6 +117,25 @@ module ActiveSupport
118
117
  def parallelize_teardown(&block)
119
118
  ActiveSupport::Testing::Parallelization.run_cleanup_hook(&block)
120
119
  end
120
+
121
+ # :singleton-method: fixture_paths
122
+ #
123
+ # Returns the ActiveRecord::FixtureSet collection.
124
+ #
125
+ # In your +test_helper.rb+ you must have <tt>require "rails/test_help"</tt>.
126
+
127
+ # :singleton-method: fixture_paths=
128
+ #
129
+ # :call-seq:
130
+ # fixture_paths=(fixture_paths)
131
+ #
132
+ # Sets the given path to the fixture set.
133
+ #
134
+ # Can also append multiple paths.
135
+ #
136
+ # ActiveSupport::TestCase.fixture_paths << "component1/test/fixtures"
137
+ #
138
+ # In your +test_helper.rb+ you must have <tt>require "rails/test_help"</tt>.
121
139
  end
122
140
 
123
141
  alias_method :method_name, :name
@@ -125,25 +143,154 @@ module ActiveSupport
125
143
  include ActiveSupport::Testing::TaggedLogging
126
144
  prepend ActiveSupport::Testing::SetupAndTeardown
127
145
  include ActiveSupport::Testing::Assertions
146
+ include ActiveSupport::Testing::ErrorReporterAssertions
128
147
  include ActiveSupport::Testing::Deprecation
148
+ include ActiveSupport::Testing::ConstantStubbing
129
149
  include ActiveSupport::Testing::TimeHelpers
130
150
  include ActiveSupport::Testing::FileFixtures
131
151
  extend ActiveSupport::Testing::Declarative
132
152
 
133
- # test/unit backwards compatibility methods
134
- alias :assert_raise :assert_raises
153
+ ##
154
+ # :method: assert_not_empty
155
+ #
156
+ # :call-seq:
157
+ # assert_not_empty(obj, msg = nil)
158
+ #
159
+ # Alias for: refute_empty[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_empty]
160
+
161
+ #
135
162
  alias :assert_not_empty :refute_empty
163
+
164
+ ##
165
+ # :method: assert_not_equal
166
+ #
167
+ # :call-seq:
168
+ # assert_not_equal(exp, act, msg = nil)
169
+ #
170
+ # Alias for: refute_equal[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_equal]
171
+
172
+ #
136
173
  alias :assert_not_equal :refute_equal
174
+
175
+ ##
176
+ # :method: assert_not_in_delta
177
+ #
178
+ # :call-seq:
179
+ # assert_not_in_delta(exp, act, delta = 0.001, msg = nil)
180
+ #
181
+ # Alias for: refute_in_delta[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_in_delta]
182
+
183
+ #
137
184
  alias :assert_not_in_delta :refute_in_delta
185
+
186
+ ##
187
+ # :method: assert_not_in_epsilon
188
+ #
189
+ # :call-seq:
190
+ # assert_not_in_epsilon(a, b, epsilon = 0.001, msg = nil)
191
+ #
192
+ # Alias for: refute_in_epsilon[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_in_epsilon]
193
+
194
+ #
138
195
  alias :assert_not_in_epsilon :refute_in_epsilon
196
+
197
+ ##
198
+ # :method: assert_not_includes
199
+ #
200
+ # :call-seq:
201
+ # assert_not_includes(collection, obj, msg = nil)
202
+ #
203
+ # Alias for: refute_includes[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_includes]
204
+
205
+ #
139
206
  alias :assert_not_includes :refute_includes
207
+
208
+ ##
209
+ # :method: assert_not_instance_of
210
+ #
211
+ # :call-seq:
212
+ # assert_not_instance_of(cls, obj, msg = nil)
213
+ #
214
+ # Alias for: refute_instance_of[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_instance_of]
215
+
216
+ #
140
217
  alias :assert_not_instance_of :refute_instance_of
218
+
219
+ ##
220
+ # :method: assert_not_kind_of
221
+ #
222
+ # :call-seq:
223
+ # assert_not_kind_of(cls, obj, msg = nil)
224
+ #
225
+ # Alias for: refute_kind_of[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_kind_of]
226
+
227
+ #
141
228
  alias :assert_not_kind_of :refute_kind_of
229
+
230
+ ##
231
+ # :method: assert_no_match
232
+ #
233
+ # :call-seq:
234
+ # assert_no_match(matcher, obj, msg = nil)
235
+ #
236
+ # Alias for: refute_match[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_match]
237
+
238
+ #
142
239
  alias :assert_no_match :refute_match
240
+
241
+ ##
242
+ # :method: assert_not_nil
243
+ #
244
+ # :call-seq:
245
+ # assert_not_nil(obj, msg = nil)
246
+ #
247
+ # Alias for: refute_nil[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_nil]
248
+
249
+ #
143
250
  alias :assert_not_nil :refute_nil
251
+
252
+ ##
253
+ # :method: assert_not_operator
254
+ #
255
+ # :call-seq:
256
+ # assert_not_operator(o1, op, o2 = UNDEFINED, msg = nil)
257
+ #
258
+ # Alias for: refute_operator[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_operator]
259
+
260
+ #
144
261
  alias :assert_not_operator :refute_operator
262
+
263
+ ##
264
+ # :method: assert_not_predicate
265
+ #
266
+ # :call-seq:
267
+ # assert_not_predicate(o1, op, msg = nil)
268
+ #
269
+ # Alias for: refute_predicate[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_predicate]
270
+
271
+ #
145
272
  alias :assert_not_predicate :refute_predicate
273
+
274
+ ##
275
+ # :method: assert_not_respond_to
276
+ #
277
+ # :call-seq:
278
+ # assert_not_respond_to(obj, meth, msg = nil)
279
+ #
280
+ # Alias for: refute_respond_to[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_respond_to]
281
+
282
+ #
146
283
  alias :assert_not_respond_to :refute_respond_to
284
+
285
+ ##
286
+ # :method: assert_not_same
287
+ #
288
+ # :call-seq:
289
+ # assert_not_same(exp, act, msg = nil)
290
+ #
291
+ # Alias for: refute_same[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_same]
292
+
293
+ #
147
294
  alias :assert_not_same :refute_same
148
295
 
149
296
  ActiveSupport.run_load_hooks(:active_support_test_case, self)
@@ -7,9 +7,9 @@ module ActiveSupport
7
7
  module Assertions
8
8
  UNTRACKED = Object.new # :nodoc:
9
9
 
10
- # Asserts that an expression is not truthy. Passes if <tt>object</tt> is
11
- # +nil+ or +false+. "Truthy" means "considered true in a conditional"
12
- # like <tt>if foo</tt>.
10
+ # Asserts that an expression is not truthy. Passes if +object+ is +nil+ or
11
+ # +false+. "Truthy" means "considered true in a conditional" like <tt>if
12
+ # foo</tt>.
13
13
  #
14
14
  # assert_not nil # => true
15
15
  # assert_not false # => true
@@ -23,6 +23,21 @@ module ActiveSupport
23
23
  assert !object, message
24
24
  end
25
25
 
26
+ # Asserts that a block raises one of +exp+. This is an enhancement of the
27
+ # standard Minitest assertion method with the ability to test error
28
+ # messages.
29
+ #
30
+ # assert_raises(ArgumentError, match: /incorrect param/i) do
31
+ # perform_service(param: 'exception')
32
+ # end
33
+ #
34
+ def assert_raises(*exp, match: nil, &block)
35
+ error = super(*exp, &block)
36
+ assert_match(match, error.message) if match
37
+ error
38
+ end
39
+ alias :assert_raise :assert_raises
40
+
26
41
  # Assertion that the block should not raise an exception.
27
42
  #
28
43
  # Passes if evaluated code in the yielded block raises no exception.
@@ -31,7 +46,7 @@ module ActiveSupport
31
46
  # perform_service(param: 'no_exception')
32
47
  # end
33
48
  def assert_nothing_raised
34
- yield
49
+ yield.tap { assert(true) }
35
50
  rescue => error
36
51
  raise Minitest::UnexpectedError.new(error)
37
52
  end
@@ -50,7 +65,7 @@ module ActiveSupport
50
65
  # end
51
66
  #
52
67
  # An arbitrary positive or negative difference can be specified.
53
- # The default is <tt>1</tt>.
68
+ # The default is +1+.
54
69
  #
55
70
  # assert_difference 'Article.count', -1 do
56
71
  # post :delete, params: { id: ... }
@@ -102,9 +117,10 @@ module ActiveSupport
102
117
  retval = _assert_nothing_raised_or_warn("assert_difference", &block)
103
118
 
104
119
  expressions.zip(exps, before) do |(code, diff), exp, before_value|
105
- error = "#{code.inspect} didn't change by #{diff}"
120
+ actual = exp.call
121
+ error = "#{code.inspect} didn't change by #{diff}, but by #{actual - before_value}"
106
122
  error = "#{message}.\n#{error}" if message
107
- assert_equal(before_value + diff, exp.call, error)
123
+ assert_equal(before_value + diff, actual, error)
108
124
  end
109
125
 
110
126
  retval
@@ -179,7 +195,7 @@ module ActiveSupport
179
195
  retval = _assert_nothing_raised_or_warn("assert_changes", &block)
180
196
 
181
197
  unless from == UNTRACKED
182
- error = "Expected change from #{from.inspect}"
198
+ error = "Expected change from #{from.inspect}, got #{before}"
183
199
  error = "#{message}.\n#{error}" if message
184
200
  assert from === before, error
185
201
  end
@@ -192,7 +208,7 @@ module ActiveSupport
192
208
  refute_equal before, after, error
193
209
 
194
210
  unless to == UNTRACKED
195
- error = "Expected change to #{to}\n"
211
+ error = "Expected change to #{to}, got #{after}\n"
196
212
  error = "#{message}.\n#{error}" if message
197
213
  assert to === after, error
198
214
  end
@@ -207,7 +223,7 @@ module ActiveSupport
207
223
  # post :create, params: { status: { ok: true } }
208
224
  # end
209
225
  #
210
- # Provide the optional keyword argument :from to specify the expected
226
+ # Provide the optional keyword argument +:from+ to specify the expected
211
227
  # initial value.
212
228
  #
213
229
  # assert_no_changes -> { Status.all_good? }, from: true do
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- gem "minitest"
4
-
5
3
  require "minitest"
6
4
 
7
5
  Minitest.autorun
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Testing
5
+ module ConstantStubbing
6
+ # Changes the value of a constant for the duration of a block. Example:
7
+ #
8
+ # # World::List::Import::LARGE_IMPORT_THRESHOLD = 5000
9
+ # stub_const(World::List::Import, :LARGE_IMPORT_THRESHOLD, 1) do
10
+ # assert_equal 1, World::List::Import::LARGE_IMPORT_THRESHOLD
11
+ # end
12
+ #
13
+ # assert_equal 5000, World::List::Import::LARGE_IMPORT_THRESHOLD
14
+ #
15
+ # Using this method rather than forcing <tt>World::List::Import::LARGE_IMPORT_THRESHOLD = 5000</tt> prevents
16
+ # warnings from being thrown, and ensures that the old value is returned after the test has completed.
17
+ #
18
+ # Note: Stubbing a const will stub it across all threads. So if you have concurrent threads
19
+ # (like separate test suites running in parallel) that all depend on the same constant, it's possible
20
+ # divergent stubbing will trample on each other.
21
+ def stub_const(mod, constant, new_value)
22
+ old_value = mod.const_get(constant, false)
23
+ mod.send(:remove_const, constant)
24
+ mod.const_set(constant, new_value)
25
+ yield
26
+ ensure
27
+ mod.send(:remove_const, constant)
28
+ mod.const_set(constant, old_value)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -5,6 +5,11 @@ require "active_support/deprecation"
5
5
  module ActiveSupport
6
6
  module Testing
7
7
  module Deprecation
8
+ ##
9
+ # :call-seq:
10
+ # assert_deprecated(deprecator, &block)
11
+ # assert_deprecated(match, deprecator, &block)
12
+ #
8
13
  # Asserts that a matching deprecation warning was emitted by the given deprecator during the execution of the yielded block.
9
14
  #
10
15
  # assert_deprecated(/foo/, CustomDeprecator) do
@@ -19,16 +24,15 @@ module ActiveSupport
19
24
  #
20
25
  # If the +match+ is omitted (or explicitly +nil+), any deprecation warning will match.
21
26
  #
22
- # assert_deprecated(nil, CustomDeprecator) do
27
+ # assert_deprecated(CustomDeprecator) do
23
28
  # CustomDeprecator.warn "foo should no longer be used"
24
29
  # end
25
- #
26
- # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
27
- #
28
- # assert_deprecated do
29
- # ActiveSupport::Deprecation.warn "foo should no longer be used"
30
- # end
31
30
  def assert_deprecated(match = nil, deprecator = nil, &block)
31
+ match, deprecator = nil, match if match.is_a?(ActiveSupport::Deprecation)
32
+ unless deprecator
33
+ ActiveSupport.deprecator.warn("assert_deprecated without a deprecator is deprecated")
34
+ deprecator = ActiveSupport::Deprecation._instance
35
+ end
32
36
  result, warnings = collect_deprecations(deprecator, &block)
33
37
  assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
34
38
  if match
@@ -44,36 +48,32 @@ module ActiveSupport
44
48
  # CustomDeprecator.warn "message" # fails assertion
45
49
  # end
46
50
  #
47
- # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
48
- #
49
- # assert_not_deprecated do
50
- # ActiveSupport::Deprecation.warn "message" # fails assertion
51
- # end
52
- #
53
- # assert_not_deprecated do
54
- # CustomDeprecator.warn "message" # passes assertion
51
+ # assert_not_deprecated(ActiveSupport::Deprecation.new) do
52
+ # CustomDeprecator.warn "message" # passes assertion, different deprecator
55
53
  # end
56
54
  def assert_not_deprecated(deprecator = nil, &block)
55
+ unless deprecator
56
+ ActiveSupport.deprecator.warn("assert_not_deprecated without a deprecator is deprecated")
57
+ deprecator = ActiveSupport::Deprecation._instance
58
+ end
57
59
  result, deprecations = collect_deprecations(deprecator, &block)
58
60
  assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
59
61
  result
60
62
  end
61
63
 
62
- # Returns an array of all the deprecation warnings emitted by the given
64
+ # Returns the return value of the block and an array of all the deprecation warnings emitted by the given
63
65
  # +deprecator+ during the execution of the yielded block.
64
66
  #
65
67
  # collect_deprecations(CustomDeprecator) do
66
68
  # CustomDeprecator.warn "message"
67
- # end # => ["message"]
68
- #
69
- # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
70
- #
71
- # collect_deprecations do
72
- # CustomDeprecator.warn "custom message"
73
- # ActiveSupport::Deprecation.warn "message"
74
- # end # => ["message"]
69
+ # ActiveSupport::Deprecation.new.warn "other message"
70
+ # :result
71
+ # end # => [:result, ["message"]]
75
72
  def collect_deprecations(deprecator = nil)
76
- deprecator ||= ActiveSupport::Deprecation
73
+ unless deprecator
74
+ ActiveSupport.deprecator.warn("collect_deprecations without a deprecator is deprecated")
75
+ deprecator = ActiveSupport::Deprecation._instance
76
+ end
77
77
  old_behavior = deprecator.behavior
78
78
  deprecations = []
79
79
  deprecator.behavior = Proc.new do |message, callstack|
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Testing
5
+ module ErrorReporterAssertions
6
+ module ErrorCollector # :nodoc:
7
+ @subscribed = false
8
+ @mutex = Mutex.new
9
+
10
+ Report = Struct.new(:error, :handled, :severity, :context, :source, keyword_init: true)
11
+ class Report
12
+ alias_method :handled?, :handled
13
+ end
14
+
15
+ class << self
16
+ def record
17
+ subscribe
18
+ recorders = ActiveSupport::IsolatedExecutionState[:active_support_error_reporter_assertions] ||= []
19
+ reports = []
20
+ recorders << reports
21
+ begin
22
+ yield
23
+ reports
24
+ ensure
25
+ recorders.delete_if { |r| reports.equal?(r) }
26
+ end
27
+ end
28
+
29
+ def report(error, **kwargs)
30
+ report = Report.new(error: error, **kwargs)
31
+ ActiveSupport::IsolatedExecutionState[:active_support_error_reporter_assertions]&.each do |reports|
32
+ reports << report
33
+ end
34
+ true
35
+ end
36
+
37
+ private
38
+ def subscribe
39
+ return if @subscribed
40
+ @mutex.synchronize do
41
+ return if @subscribed
42
+
43
+ if ActiveSupport.error_reporter
44
+ ActiveSupport.error_reporter.subscribe(self)
45
+ @subscribed = true
46
+ else
47
+ raise Minitest::Assertion, "No error reporter is configured"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ # Assertion that the block should not cause an exception to be reported
55
+ # to +Rails.error+.
56
+ #
57
+ # Passes if evaluated code in the yielded block reports no exception.
58
+ #
59
+ # assert_no_error_reported do
60
+ # perform_service(param: 'no_exception')
61
+ # end
62
+ def assert_no_error_reported(&block)
63
+ reports = ErrorCollector.record do
64
+ _assert_nothing_raised_or_warn("assert_no_error_reported", &block)
65
+ end
66
+ assert_predicate(reports, :empty?)
67
+ end
68
+
69
+ # Assertion that the block should cause at least one exception to be reported
70
+ # to +Rails.error+.
71
+ #
72
+ # Passes if the evaluated code in the yielded block reports a matching exception.
73
+ #
74
+ # assert_error_reported(IOError) do
75
+ # Rails.error.report(IOError.new("Oops"))
76
+ # end
77
+ #
78
+ # To test further details about the reported exception, you can use the return
79
+ # value.
80
+ #
81
+ # report = assert_error_reported(IOError) do
82
+ # # ...
83
+ # end
84
+ # assert_equal "Oops", report.error.message
85
+ # assert_equal "admin", report.context[:section]
86
+ # assert_equal :warning, report.severity
87
+ # assert_predicate report, :handled?
88
+ def assert_error_reported(error_class = StandardError, &block)
89
+ reports = ErrorCollector.record do
90
+ _assert_nothing_raised_or_warn("assert_error_reported", &block)
91
+ end
92
+
93
+ if reports.empty?
94
+ assert(false, "Expected a #{error_class.name} to be reported, but there were no errors reported.")
95
+ elsif (report = reports.find { |r| error_class === r.error })
96
+ self.assertions += 1
97
+ report
98
+ else
99
+ message = "Expected a #{error_class.name} to be reported, but none of the " \
100
+ "#{reports.size} reported errors matched: \n" \
101
+ "#{reports.map { |r| r.error.class.name }.join("\n ")}"
102
+ assert(false, message)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end