activesupport 7.0.8.7 → 7.2.2.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 (198) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +143 -459
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +3 -1
  7. data/lib/active_support/backtrace_cleaner.rb +39 -7
  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 +49 -17
  14. data/lib/active_support/cache/mem_cache_store.rb +94 -128
  15. data/lib/active_support/cache/memory_store.rb +80 -25
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +165 -152
  18. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +29 -14
  20. data/lib/active_support/cache.rb +363 -291
  21. data/lib/active_support/callbacks.rb +118 -134
  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 +1 -2
  28. data/lib/active_support/core_ext/array.rb +0 -1
  29. data/lib/active_support/core_ext/class/subclasses.rb +17 -34
  30. data/lib/active_support/core_ext/date/blank.rb +4 -0
  31. data/lib/active_support/core_ext/date/conversions.rb +1 -2
  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_and_time/compatibility.rb +28 -1
  35. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  36. data/lib/active_support/core_ext/date_time/conversions.rb +2 -2
  37. data/lib/active_support/core_ext/date_time.rb +0 -1
  38. data/lib/active_support/core_ext/digest/uuid.rb +7 -10
  39. data/lib/active_support/core_ext/enumerable.rb +3 -75
  40. data/lib/active_support/core_ext/erb/util.rb +201 -0
  41. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  42. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  43. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  44. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  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 +20 -119
  49. data/lib/active_support/core_ext/module/deprecation.rb +12 -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 +5 -3
  53. data/lib/active_support/core_ext/numeric.rb +0 -1
  54. data/lib/active_support/core_ext/object/blank.rb +45 -1
  55. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  56. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  57. data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
  58. data/lib/active_support/core_ext/object/json.rb +17 -7
  59. data/lib/active_support/core_ext/object/with.rb +46 -0
  60. data/lib/active_support/core_ext/object/with_options.rb +4 -4
  61. data/lib/active_support/core_ext/object.rb +1 -0
  62. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  63. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  64. data/lib/active_support/core_ext/pathname.rb +1 -0
  65. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  66. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  67. data/lib/active_support/core_ext/range.rb +1 -2
  68. data/lib/active_support/core_ext/securerandom.rb +1 -5
  69. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  70. data/lib/active_support/core_ext/string/filters.rb +21 -15
  71. data/lib/active_support/core_ext/string/indent.rb +1 -1
  72. data/lib/active_support/core_ext/string/inflections.rb +16 -5
  73. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  74. data/lib/active_support/core_ext/string/output_safety.rb +34 -177
  75. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  76. data/lib/active_support/core_ext/time/calculations.rb +36 -30
  77. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  78. data/lib/active_support/core_ext/time/conversions.rb +1 -3
  79. data/lib/active_support/core_ext/time/zones.rb +4 -4
  80. data/lib/active_support/core_ext/time.rb +0 -1
  81. data/lib/active_support/core_ext.rb +0 -1
  82. data/lib/active_support/current_attributes.rb +53 -46
  83. data/lib/active_support/deep_mergeable.rb +53 -0
  84. data/lib/active_support/delegation.rb +202 -0
  85. data/lib/active_support/dependencies/autoload.rb +9 -16
  86. data/lib/active_support/deprecation/behaviors.rb +65 -42
  87. data/lib/active_support/deprecation/constant_accessor.rb +47 -25
  88. data/lib/active_support/deprecation/deprecators.rb +104 -0
  89. data/lib/active_support/deprecation/disallowed.rb +3 -5
  90. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  91. data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
  92. data/lib/active_support/deprecation/reporting.rb +49 -27
  93. data/lib/active_support/deprecation.rb +39 -9
  94. data/lib/active_support/deprecator.rb +7 -0
  95. data/lib/active_support/descendants_tracker.rb +66 -172
  96. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  97. data/lib/active_support/duration/iso8601_serializer.rb +1 -4
  98. data/lib/active_support/duration.rb +13 -7
  99. data/lib/active_support/encrypted_configuration.rb +30 -9
  100. data/lib/active_support/encrypted_file.rb +9 -4
  101. data/lib/active_support/environment_inquirer.rb +22 -2
  102. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  103. data/lib/active_support/error_reporter.rb +160 -36
  104. data/lib/active_support/evented_file_update_checker.rb +0 -1
  105. data/lib/active_support/execution_wrapper.rb +4 -5
  106. data/lib/active_support/file_update_checker.rb +5 -3
  107. data/lib/active_support/fork_tracker.rb +4 -32
  108. data/lib/active_support/gem_version.rb +4 -4
  109. data/lib/active_support/gzip.rb +2 -0
  110. data/lib/active_support/hash_with_indifferent_access.rb +41 -25
  111. data/lib/active_support/html_safe_translation.rb +19 -6
  112. data/lib/active_support/i18n.rb +1 -1
  113. data/lib/active_support/i18n_railtie.rb +20 -13
  114. data/lib/active_support/inflector/inflections.rb +2 -0
  115. data/lib/active_support/inflector/methods.rb +23 -11
  116. data/lib/active_support/inflector/transliterate.rb +3 -1
  117. data/lib/active_support/isolated_execution_state.rb +26 -22
  118. data/lib/active_support/json/decoding.rb +2 -1
  119. data/lib/active_support/json/encoding.rb +25 -43
  120. data/lib/active_support/key_generator.rb +9 -1
  121. data/lib/active_support/lazy_load_hooks.rb +6 -4
  122. data/lib/active_support/locale/en.yml +2 -0
  123. data/lib/active_support/log_subscriber.rb +74 -34
  124. data/lib/active_support/logger.rb +22 -60
  125. data/lib/active_support/logger_thread_safe_level.rb +10 -32
  126. data/lib/active_support/message_encryptor.rb +197 -53
  127. data/lib/active_support/message_encryptors.rb +141 -0
  128. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  129. data/lib/active_support/message_pack/extensions.rb +305 -0
  130. data/lib/active_support/message_pack/serializer.rb +63 -0
  131. data/lib/active_support/message_pack.rb +50 -0
  132. data/lib/active_support/message_verifier.rb +220 -89
  133. data/lib/active_support/message_verifiers.rb +135 -0
  134. data/lib/active_support/messages/codec.rb +65 -0
  135. data/lib/active_support/messages/metadata.rb +111 -45
  136. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  137. data/lib/active_support/messages/rotator.rb +34 -32
  138. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  139. data/lib/active_support/multibyte/chars.rb +4 -2
  140. data/lib/active_support/multibyte/unicode.rb +9 -37
  141. data/lib/active_support/notifications/fanout.rb +248 -87
  142. data/lib/active_support/notifications/instrumenter.rb +93 -25
  143. data/lib/active_support/notifications.rb +29 -28
  144. data/lib/active_support/number_helper/number_converter.rb +16 -7
  145. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  146. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  147. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  148. data/lib/active_support/number_helper.rb +379 -318
  149. data/lib/active_support/option_merger.rb +2 -2
  150. data/lib/active_support/ordered_hash.rb +3 -3
  151. data/lib/active_support/ordered_options.rb +67 -15
  152. data/lib/active_support/parameter_filter.rb +84 -69
  153. data/lib/active_support/proxy_object.rb +8 -3
  154. data/lib/active_support/railtie.rb +25 -20
  155. data/lib/active_support/reloader.rb +12 -4
  156. data/lib/active_support/rescuable.rb +2 -0
  157. data/lib/active_support/secure_compare_rotator.rb +16 -9
  158. data/lib/active_support/string_inquirer.rb +4 -2
  159. data/lib/active_support/subscriber.rb +10 -27
  160. data/lib/active_support/syntax_error_proxy.rb +60 -0
  161. data/lib/active_support/tagged_logging.rb +64 -25
  162. data/lib/active_support/test_case.rb +156 -7
  163. data/lib/active_support/testing/assertions.rb +28 -12
  164. data/lib/active_support/testing/autorun.rb +0 -2
  165. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  166. data/lib/active_support/testing/deprecation.rb +20 -27
  167. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  168. data/lib/active_support/testing/isolation.rb +21 -9
  169. data/lib/active_support/testing/method_call_assertions.rb +7 -8
  170. data/lib/active_support/testing/parallelization/server.rb +3 -0
  171. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  172. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  173. data/lib/active_support/testing/stream.rb +1 -1
  174. data/lib/active_support/testing/strict_warnings.rb +43 -0
  175. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  176. data/lib/active_support/testing/time_helpers.rb +38 -16
  177. data/lib/active_support/time_with_zone.rb +12 -18
  178. data/lib/active_support/values/time_zone.rb +25 -14
  179. data/lib/active_support/version.rb +1 -1
  180. data/lib/active_support/xml_mini/jdom.rb +3 -10
  181. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  182. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  183. data/lib/active_support/xml_mini/rexml.rb +1 -1
  184. data/lib/active_support/xml_mini.rb +12 -3
  185. data/lib/active_support.rb +15 -3
  186. metadata +140 -19
  187. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  188. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
  189. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
  190. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  191. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
  192. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
  193. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  194. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
  195. data/lib/active_support/core_ext/uri.rb +0 -5
  196. data/lib/active_support/deprecation/instance_delegator.rb +0 -38
  197. data/lib/active_support/per_thread_registry.rb +0 -65
  198. data/lib/active_support/ruby_features.rb +0 -7
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ module ActiveSupport
6
+ # This is a class for wrapping syntax errors. The purpose of this class
7
+ # is to enhance the backtraces on SyntaxError exceptions to include the
8
+ # source location of the syntax error. That way we can display the error
9
+ # source on error pages in development.
10
+ class SyntaxErrorProxy < DelegateClass(SyntaxError) # :nodoc:
11
+ def backtrace
12
+ parse_message_for_trace + super
13
+ end
14
+
15
+ class BacktraceLocation < Struct.new(:path, :lineno, :to_s) # :nodoc:
16
+ def spot(_)
17
+ end
18
+
19
+ def label
20
+ end
21
+ end
22
+
23
+ class BacktraceLocationProxy < DelegateClass(Thread::Backtrace::Location) # :nodoc:
24
+ def initialize(loc, ex)
25
+ super(loc)
26
+ @ex = ex
27
+ end
28
+
29
+ def spot(_)
30
+ super(@ex.__getobj__)
31
+ end
32
+ end
33
+
34
+ def backtrace_locations
35
+ return nil if super.nil?
36
+
37
+ parse_message_for_trace.map { |trace|
38
+ file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
39
+ BacktraceLocation.new(file, line.to_i, trace)
40
+ # We have to wrap these backtrace locations because we need the
41
+ # spot information to come from the originating exception, not the
42
+ # proxy object that's generating these
43
+ } + super.map { |loc| BacktraceLocationProxy.new(loc, self) }
44
+ end
45
+
46
+ private
47
+ def parse_message_for_trace
48
+ if __getobj__.to_s.start_with?("(eval")
49
+ # If the exception is coming from a call to eval, we need to keep
50
+ # the path of the file in which eval was called to ensure we can
51
+ # return the right source fragment to show the location of the
52
+ # error
53
+ location = __getobj__.backtrace_locations[0]
54
+ ["#{location.path}:#{location.lineno}: #{__getobj__}"]
55
+ else
56
+ __getobj__.to_s.split("\n")
57
+ end
58
+ end
59
+ end
60
+ end
@@ -2,18 +2,19 @@
2
2
 
3
3
  require "active_support/core_ext/module/delegation"
4
4
  require "active_support/core_ext/object/blank"
5
- require "logger"
6
5
  require "active_support/logger"
7
6
 
8
7
  module ActiveSupport
8
+ # = Active Support Tagged Logging
9
+ #
9
10
  # Wraps any standard Logger object to provide tagging capabilities.
10
11
  #
11
12
  # May be called with a block:
12
13
  #
13
14
  # 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"
15
+ # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
16
+ # logger.tagged('BCX', "Jason") { |tagged_logger| tagged_logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
17
+ # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
17
18
  #
18
19
  # If called without a block, a new logger will be returned with applied tags:
19
20
  #
@@ -29,52 +30,86 @@ module ActiveSupport
29
30
  module Formatter # :nodoc:
30
31
  # This method is invoked when a log event occurs.
31
32
  def call(severity, timestamp, progname, msg)
32
- super(severity, timestamp, progname, "#{tags_text}#{msg}")
33
+ super(severity, timestamp, progname, tag_stack.format_message(msg))
33
34
  end
34
35
 
35
36
  def tagged(*tags)
36
- new_tags = push_tags(*tags)
37
+ pushed_count = tag_stack.push_tags(tags).size
37
38
  yield self
38
39
  ensure
39
- pop_tags(new_tags.size)
40
+ pop_tags(pushed_count)
40
41
  end
41
42
 
42
43
  def push_tags(*tags)
43
- tags.flatten!
44
- tags.reject!(&:blank?)
45
- current_tags.concat tags
46
- tags
44
+ tag_stack.push_tags(tags)
47
45
  end
48
46
 
49
- def pop_tags(size = 1)
50
- current_tags.pop size
47
+ def pop_tags(count = 1)
48
+ tag_stack.pop_tags(count)
51
49
  end
52
50
 
53
51
  def clear_tags!
54
- current_tags.clear
52
+ tag_stack.clear
55
53
  end
56
54
 
57
- def current_tags
55
+ def tag_stack
58
56
  # 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] ||= []
57
+ @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
58
+ IsolatedExecutionState[@thread_key] ||= TagStack.new
59
+ end
60
+
61
+ def current_tags
62
+ tag_stack.tags
61
63
  end
62
64
 
63
65
  def tags_text
64
- tags = current_tags
65
- if tags.one?
66
- "[#{tags[0]}] "
67
- elsif tags.any?
68
- tags.collect { |tag| "[#{tag}] " }.join
66
+ tag_stack.format_message("")
67
+ end
68
+ end
69
+
70
+ class TagStack # :nodoc:
71
+ attr_reader :tags
72
+
73
+ def initialize
74
+ @tags = []
75
+ @tags_string = nil
76
+ end
77
+
78
+ def push_tags(tags)
79
+ @tags_string = nil
80
+ tags.flatten!
81
+ tags.reject!(&:blank?)
82
+ @tags.concat(tags)
83
+ tags
84
+ end
85
+
86
+ def pop_tags(count)
87
+ @tags_string = nil
88
+ @tags.pop(count)
89
+ end
90
+
91
+ def clear
92
+ @tags_string = nil
93
+ @tags.clear
94
+ end
95
+
96
+ def format_message(message)
97
+ if @tags.empty?
98
+ message
99
+ elsif @tags.size == 1
100
+ "[#{@tags[0]}] #{message}"
101
+ else
102
+ @tags_string ||= "[#{@tags.join("] [")}] "
103
+ "#{@tags_string}#{message}"
69
104
  end
70
105
  end
71
106
  end
72
107
 
73
108
  module LocalTagStorage # :nodoc:
74
- attr_accessor :current_tags
109
+ attr_accessor :tag_stack
75
110
 
76
111
  def self.extended(base)
77
- base.current_tags = []
112
+ base.tag_stack = TagStack.new
78
113
  end
79
114
  end
80
115
 
@@ -82,7 +117,11 @@ module ActiveSupport
82
117
  logger = logger.clone
83
118
 
84
119
  if logger.formatter
85
- logger.formatter = logger.formatter.dup
120
+ logger.formatter = logger.formatter.clone
121
+
122
+ # Workaround for https://bugs.ruby-lang.org/issues/20250
123
+ # Can be removed when Ruby 3.4 is the least supported version.
124
+ logger.formatter.object_id if logger.formatter.is_a?(Proc)
86
125
  else
87
126
  # Ensure we set a default formatter so we aren't extending nil!
88
127
  logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
@@ -1,15 +1,17 @@
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"
6
+ require "active_support/testing/tests_without_assertions"
7
7
  require "active_support/testing/assertions"
8
+ require "active_support/testing/error_reporter_assertions"
8
9
  require "active_support/testing/deprecation"
9
10
  require "active_support/testing/declarative"
10
11
  require "active_support/testing/isolation"
11
12
  require "active_support/testing/constant_lookup"
12
13
  require "active_support/testing/time_helpers"
14
+ require "active_support/testing/constant_stubbing"
13
15
  require "active_support/testing/file_fixtures"
14
16
  require "active_support/testing/parallelization"
15
17
  require "active_support/testing/parallelize_executor"
@@ -66,7 +68,7 @@ module ActiveSupport
66
68
  # The default parallelization method is to fork processes. If you'd like to
67
69
  # use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+
68
70
  # method. Note the threaded parallelization does not create multiple
69
- # database and will not work with system tests at this time.
71
+ # databases and will not work with system tests.
70
72
  #
71
73
  # parallelize(workers: :number_of_processors, with: :threads)
72
74
  #
@@ -77,11 +79,9 @@ module ActiveSupport
77
79
  # number of tests to run is above the +threshold+ param. The default value is
78
80
  # 50, and it's configurable via +config.active_support.test_parallelization_threshold+.
79
81
  def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold)
80
- workers = Concurrent.physical_processor_count if workers == :number_of_processors
82
+ workers = Concurrent.processor_count if workers == :number_of_processors
81
83
  workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
82
84
 
83
- return if workers <= 1
84
-
85
85
  Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
86
86
  end
87
87
 
@@ -118,32 +118,181 @@ module ActiveSupport
118
118
  def parallelize_teardown(&block)
119
119
  ActiveSupport::Testing::Parallelization.run_cleanup_hook(&block)
120
120
  end
121
+
122
+ # :singleton-method: fixture_paths
123
+ #
124
+ # Returns the ActiveRecord::FixtureSet collection.
125
+ #
126
+ # In your +test_helper.rb+ you must have <tt>require "rails/test_help"</tt>.
127
+
128
+ # :singleton-method: fixture_paths=
129
+ #
130
+ # :call-seq:
131
+ # fixture_paths=(fixture_paths)
132
+ #
133
+ # Sets the given path to the fixture set.
134
+ #
135
+ # Can also append multiple paths.
136
+ #
137
+ # ActiveSupport::TestCase.fixture_paths << "component1/test/fixtures"
138
+ #
139
+ # In your +test_helper.rb+ you must have <tt>require "rails/test_help"</tt>.
121
140
  end
122
141
 
123
142
  alias_method :method_name, :name
124
143
 
125
144
  include ActiveSupport::Testing::TaggedLogging
126
145
  prepend ActiveSupport::Testing::SetupAndTeardown
146
+ prepend ActiveSupport::Testing::TestsWithoutAssertions
127
147
  include ActiveSupport::Testing::Assertions
148
+ include ActiveSupport::Testing::ErrorReporterAssertions
128
149
  include ActiveSupport::Testing::Deprecation
150
+ include ActiveSupport::Testing::ConstantStubbing
129
151
  include ActiveSupport::Testing::TimeHelpers
130
152
  include ActiveSupport::Testing::FileFixtures
131
153
  extend ActiveSupport::Testing::Declarative
132
154
 
133
- # test/unit backwards compatibility methods
134
- alias :assert_raise :assert_raises
155
+ ##
156
+ # :method: assert_not_empty
157
+ #
158
+ # :call-seq:
159
+ # assert_not_empty(obj, msg = nil)
160
+ #
161
+ # Alias for: refute_empty[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_empty]
162
+
163
+ #
135
164
  alias :assert_not_empty :refute_empty
165
+
166
+ ##
167
+ # :method: assert_not_equal
168
+ #
169
+ # :call-seq:
170
+ # assert_not_equal(exp, act, msg = nil)
171
+ #
172
+ # Alias for: refute_equal[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_equal]
173
+
174
+ #
136
175
  alias :assert_not_equal :refute_equal
176
+
177
+ ##
178
+ # :method: assert_not_in_delta
179
+ #
180
+ # :call-seq:
181
+ # assert_not_in_delta(exp, act, delta = 0.001, msg = nil)
182
+ #
183
+ # Alias for: refute_in_delta[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_in_delta]
184
+
185
+ #
137
186
  alias :assert_not_in_delta :refute_in_delta
187
+
188
+ ##
189
+ # :method: assert_not_in_epsilon
190
+ #
191
+ # :call-seq:
192
+ # assert_not_in_epsilon(a, b, epsilon = 0.001, msg = nil)
193
+ #
194
+ # Alias for: refute_in_epsilon[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_in_epsilon]
195
+
196
+ #
138
197
  alias :assert_not_in_epsilon :refute_in_epsilon
198
+
199
+ ##
200
+ # :method: assert_not_includes
201
+ #
202
+ # :call-seq:
203
+ # assert_not_includes(collection, obj, msg = nil)
204
+ #
205
+ # Alias for: refute_includes[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_includes]
206
+
207
+ #
139
208
  alias :assert_not_includes :refute_includes
209
+
210
+ ##
211
+ # :method: assert_not_instance_of
212
+ #
213
+ # :call-seq:
214
+ # assert_not_instance_of(cls, obj, msg = nil)
215
+ #
216
+ # Alias for: refute_instance_of[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_instance_of]
217
+
218
+ #
140
219
  alias :assert_not_instance_of :refute_instance_of
220
+
221
+ ##
222
+ # :method: assert_not_kind_of
223
+ #
224
+ # :call-seq:
225
+ # assert_not_kind_of(cls, obj, msg = nil)
226
+ #
227
+ # Alias for: refute_kind_of[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_kind_of]
228
+
229
+ #
141
230
  alias :assert_not_kind_of :refute_kind_of
231
+
232
+ ##
233
+ # :method: assert_no_match
234
+ #
235
+ # :call-seq:
236
+ # assert_no_match(matcher, obj, msg = nil)
237
+ #
238
+ # Alias for: refute_match[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_match]
239
+
240
+ #
142
241
  alias :assert_no_match :refute_match
242
+
243
+ ##
244
+ # :method: assert_not_nil
245
+ #
246
+ # :call-seq:
247
+ # assert_not_nil(obj, msg = nil)
248
+ #
249
+ # Alias for: refute_nil[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_nil]
250
+
251
+ #
143
252
  alias :assert_not_nil :refute_nil
253
+
254
+ ##
255
+ # :method: assert_not_operator
256
+ #
257
+ # :call-seq:
258
+ # assert_not_operator(o1, op, o2 = UNDEFINED, msg = nil)
259
+ #
260
+ # Alias for: refute_operator[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_operator]
261
+
262
+ #
144
263
  alias :assert_not_operator :refute_operator
264
+
265
+ ##
266
+ # :method: assert_not_predicate
267
+ #
268
+ # :call-seq:
269
+ # assert_not_predicate(o1, op, msg = nil)
270
+ #
271
+ # Alias for: refute_predicate[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_predicate]
272
+
273
+ #
145
274
  alias :assert_not_predicate :refute_predicate
275
+
276
+ ##
277
+ # :method: assert_not_respond_to
278
+ #
279
+ # :call-seq:
280
+ # assert_not_respond_to(obj, meth, msg = nil)
281
+ #
282
+ # Alias for: refute_respond_to[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_respond_to]
283
+
284
+ #
146
285
  alias :assert_not_respond_to :refute_respond_to
286
+
287
+ ##
288
+ # :method: assert_not_same
289
+ #
290
+ # :call-seq:
291
+ # assert_not_same(exp, act, msg = nil)
292
+ #
293
+ # Alias for: refute_same[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_same]
294
+
295
+ #
147
296
  alias :assert_not_same :refute_same
148
297
 
149
298
  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.inspect}"
183
199
  error = "#{message}.\n#{error}" if message
184
200
  assert from === before, error
185
201
  end
@@ -187,12 +203,12 @@ module ActiveSupport
187
203
  after = exp.call
188
204
 
189
205
  error = "#{expression.inspect} didn't change"
190
- error = "#{error}. It was already #{to}" if before == to
206
+ error = "#{error}. It was already #{to.inspect}" if before == to
191
207
  error = "#{message}.\n#{error}" if message
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.inspect}, got #{after.inspect}\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
@@ -226,7 +242,7 @@ module ActiveSupport
226
242
  retval = _assert_nothing_raised_or_warn("assert_no_changes", &block)
227
243
 
228
244
  unless from == UNTRACKED
229
- error = "Expected initial value of #{from.inspect}"
245
+ error = "Expected initial value of #{from.inspect}, got #{before.inspect}"
230
246
  error = "#{message}.\n#{error}" if message
231
247
  assert from === before, error
232
248
  end
@@ -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,54 @@
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
+ # If the constant doesn't already exists, but you need it set for the duration of the block
19
+ # you can do so by passing `exists: false`.
20
+ #
21
+ # stub_const(object, :SOME_CONST, 1, exists: false) do
22
+ # assert_equal 1, SOME_CONST
23
+ # end
24
+ #
25
+ # Note: Stubbing a const will stub it across all threads. So if you have concurrent threads
26
+ # (like separate test suites running in parallel) that all depend on the same constant, it's possible
27
+ # divergent stubbing will trample on each other.
28
+ def stub_const(mod, constant, new_value, exists: true)
29
+ if exists
30
+ begin
31
+ old_value = mod.const_get(constant, false)
32
+ mod.send(:remove_const, constant)
33
+ mod.const_set(constant, new_value)
34
+ yield
35
+ ensure
36
+ mod.send(:remove_const, constant)
37
+ mod.const_set(constant, old_value)
38
+ end
39
+ else
40
+ if mod.const_defined?(constant)
41
+ raise NameError, "already defined constant #{constant} in #{mod.name}"
42
+ end
43
+
44
+ begin
45
+ mod.const_set(constant, new_value)
46
+ yield
47
+ ensure
48
+ mod.send(:remove_const, constant)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ 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,16 @@ 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
+
33
+ unless deprecator
34
+ raise ArgumentError, "No deprecator given"
35
+ end
36
+
32
37
  result, warnings = collect_deprecations(deprecator, &block)
33
38
  assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
34
39
  if match
@@ -44,36 +49,24 @@ module ActiveSupport
44
49
  # CustomDeprecator.warn "message" # fails assertion
45
50
  # end
46
51
  #
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
52
+ # assert_not_deprecated(ActiveSupport::Deprecation.new) do
53
+ # CustomDeprecator.warn "message" # passes assertion, different deprecator
55
54
  # end
56
- def assert_not_deprecated(deprecator = nil, &block)
55
+ def assert_not_deprecated(deprecator, &block)
57
56
  result, deprecations = collect_deprecations(deprecator, &block)
58
57
  assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
59
58
  result
60
59
  end
61
60
 
62
- # Returns an array of all the deprecation warnings emitted by the given
61
+ # Returns the return value of the block and an array of all the deprecation warnings emitted by the given
63
62
  # +deprecator+ during the execution of the yielded block.
64
63
  #
65
64
  # collect_deprecations(CustomDeprecator) do
66
65
  # 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"]
75
- def collect_deprecations(deprecator = nil)
76
- deprecator ||= ActiveSupport::Deprecation
66
+ # ActiveSupport::Deprecation.new.warn "other message"
67
+ # :result
68
+ # end # => [:result, ["message"]]
69
+ def collect_deprecations(deprecator)
77
70
  old_behavior = deprecator.behavior
78
71
  deprecations = []
79
72
  deprecator.behavior = Proc.new do |message, callstack|