railties 7.0.8.7 → 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 (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +723 -215
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.md +99 -0
  5. data/README.rdoc +4 -4
  6. data/lib/minitest/rails_plugin.rb +63 -0
  7. data/lib/rails/api/task.rb +35 -4
  8. data/lib/rails/app_updater.rb +14 -2
  9. data/lib/rails/application/bootstrap.rb +23 -4
  10. data/lib/rails/application/configuration.rb +190 -69
  11. data/lib/rails/application/default_middleware_stack.rb +8 -2
  12. data/lib/rails/application/dummy_config.rb +19 -0
  13. data/lib/rails/application/finisher.rb +43 -33
  14. data/lib/rails/application.rb +141 -33
  15. data/lib/rails/backtrace_cleaner.rb +5 -3
  16. data/lib/rails/cli.rb +5 -2
  17. data/lib/rails/command/actions.rb +10 -12
  18. data/lib/rails/command/base.rb +55 -53
  19. data/lib/rails/command/environment_argument.rb +32 -16
  20. data/lib/rails/command/helpers/editor.rb +17 -12
  21. data/lib/rails/command.rb +84 -33
  22. data/lib/rails/commands/about/about_command.rb +14 -0
  23. data/lib/rails/commands/application/application_command.rb +2 -0
  24. data/lib/rails/commands/console/console_command.rb +14 -14
  25. data/lib/rails/commands/credentials/USAGE +53 -55
  26. data/lib/rails/commands/credentials/credentials_command/diffing.rb +5 -3
  27. data/lib/rails/commands/credentials/credentials_command.rb +64 -70
  28. data/lib/rails/commands/db/system/change/change_command.rb +2 -1
  29. data/lib/rails/commands/dbconsole/dbconsole_command.rb +25 -115
  30. data/lib/rails/commands/destroy/destroy_command.rb +3 -2
  31. data/lib/rails/commands/dev/dev_command.rb +1 -6
  32. data/lib/rails/commands/encrypted/USAGE +15 -20
  33. data/lib/rails/commands/encrypted/encrypted_command.rb +46 -35
  34. data/lib/rails/commands/gem_help/USAGE +16 -0
  35. data/lib/rails/commands/gem_help/gem_help_command.rb +13 -0
  36. data/lib/rails/commands/generate/generate_command.rb +2 -2
  37. data/lib/rails/commands/help/USAGE +13 -13
  38. data/lib/rails/commands/help/help_command.rb +21 -2
  39. data/lib/rails/commands/initializers/initializers_command.rb +1 -4
  40. data/lib/rails/commands/middleware/middleware_command.rb +17 -0
  41. data/lib/rails/commands/new/new_command.rb +2 -0
  42. data/lib/rails/commands/notes/notes_command.rb +2 -1
  43. data/lib/rails/commands/plugin/plugin_command.rb +2 -0
  44. data/lib/rails/commands/rake/rake_command.rb +25 -22
  45. data/lib/rails/commands/restart/restart_command.rb +14 -0
  46. data/lib/rails/commands/routes/routes_command.rb +13 -1
  47. data/lib/rails/commands/runner/USAGE +14 -12
  48. data/lib/rails/commands/runner/runner_command.rb +32 -20
  49. data/lib/rails/commands/secret/secret_command.rb +13 -0
  50. data/lib/rails/commands/secrets/USAGE +44 -49
  51. data/lib/rails/commands/secrets/secrets_command.rb +20 -38
  52. data/lib/rails/commands/server/server_command.rb +33 -32
  53. data/lib/rails/commands/test/USAGE +14 -0
  54. data/lib/rails/commands/test/test_command.rb +56 -14
  55. data/lib/rails/commands/unused_routes/unused_routes_command.rb +75 -0
  56. data/lib/rails/commands/version/version_command.rb +1 -0
  57. data/lib/rails/configuration.rb +5 -5
  58. data/lib/rails/console/app.rb +1 -4
  59. data/lib/rails/deprecator.rb +7 -0
  60. data/lib/rails/engine/configuration.rb +50 -6
  61. data/lib/rails/engine.rb +49 -21
  62. data/lib/rails/gem_version.rb +4 -4
  63. data/lib/rails/generators/actions.rb +6 -15
  64. data/lib/rails/generators/active_model.rb +28 -14
  65. data/lib/rails/generators/app_base.rb +355 -82
  66. data/lib/rails/generators/app_name.rb +3 -14
  67. data/lib/rails/generators/base.rb +17 -9
  68. data/lib/rails/generators/database.rb +40 -2
  69. data/lib/rails/generators/erb/mailer/templates/layout.html.erb.tt +1 -1
  70. data/lib/rails/generators/generated_attribute.rb +12 -0
  71. data/lib/rails/generators/migration.rb +4 -5
  72. data/lib/rails/generators/model_helpers.rb +2 -1
  73. data/lib/rails/generators/rails/app/USAGE +22 -6
  74. data/lib/rails/generators/rails/app/app_generator.rb +85 -64
  75. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +103 -0
  76. data/lib/rails/generators/rails/app/templates/Gemfile.tt +9 -11
  77. data/lib/rails/generators/rails/app/templates/app/views/layouts/mailer.html.erb.tt +1 -1
  78. data/lib/rails/generators/rails/app/templates/bin/setup.tt +10 -1
  79. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +6 -17
  80. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +4 -4
  81. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +3 -3
  82. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +4 -6
  83. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +3 -3
  84. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +59 -0
  85. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +12 -2
  86. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +32 -28
  87. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +13 -9
  88. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +2 -0
  89. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +2 -2
  90. data/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt +1 -1
  91. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +3 -3
  92. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt +280 -0
  93. data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +11 -9
  94. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +11 -13
  95. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +21 -20
  96. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +5 -1
  97. data/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +6 -4
  98. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +10 -0
  99. data/lib/rails/generators/rails/app/templates/dockerignore.tt +43 -0
  100. data/lib/rails/generators/rails/app/templates/gitignore.tt +4 -8
  101. data/lib/rails/generators/rails/app/templates/node-version.tt +1 -0
  102. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +10 -8
  103. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +9 -7
  104. data/lib/rails/generators/rails/application_record/application_record_generator.rb +4 -0
  105. data/lib/rails/generators/rails/benchmark/benchmark_generator.rb +2 -1
  106. data/lib/rails/generators/rails/controller/USAGE +12 -4
  107. data/lib/rails/generators/rails/controller/controller_generator.rb +5 -0
  108. data/lib/rails/generators/rails/controller/templates/controller.rb.tt +1 -1
  109. data/lib/rails/generators/rails/credentials/credentials_generator.rb +29 -24
  110. data/lib/rails/generators/rails/credentials/templates/credentials.yml.tt +8 -0
  111. data/lib/rails/generators/rails/db/system/change/change_generator.rb +30 -0
  112. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +1 -2
  113. data/lib/rails/generators/rails/migration/USAGE +21 -11
  114. data/lib/rails/generators/rails/model/model_generator.rb +4 -0
  115. data/lib/rails/generators/rails/plugin/USAGE +17 -6
  116. data/lib/rails/generators/rails/plugin/plugin_generator.rb +5 -15
  117. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +2 -2
  118. data/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt +1 -1
  119. data/lib/rails/generators/rails/plugin/templates/bin/rails.tt +1 -17
  120. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +0 -2
  121. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +4 -4
  122. data/lib/rails/generators/rails/resource/resource_generator.rb +6 -0
  123. data/lib/rails/generators/rails/scaffold/scaffold_generator.rb +2 -1
  124. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +1 -1
  125. data/lib/rails/generators/test_case.rb +2 -2
  126. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +1 -1
  127. data/lib/rails/generators/testing/{behaviour.rb → behavior.rb} +4 -1
  128. data/lib/rails/generators.rb +6 -14
  129. data/lib/rails/health_controller.rb +55 -0
  130. data/lib/rails/info.rb +1 -1
  131. data/lib/rails/info_controller.rb +33 -11
  132. data/lib/rails/mailers_controller.rb +15 -5
  133. data/lib/rails/paths.rb +13 -10
  134. data/lib/rails/rack/logger.rb +15 -12
  135. data/lib/rails/rackup/server.rb +15 -0
  136. data/lib/rails/railtie/configuration.rb +14 -1
  137. data/lib/rails/railtie.rb +31 -31
  138. data/lib/rails/ruby_version_check.rb +2 -0
  139. data/lib/rails/source_annotation_extractor.rb +67 -18
  140. data/lib/rails/tasks/engine.rake +8 -8
  141. data/lib/rails/tasks/framework.rake +4 -10
  142. data/lib/rails/tasks/log.rake +1 -1
  143. data/lib/rails/tasks/misc.rake +3 -14
  144. data/lib/rails/tasks/statistics.rake +5 -4
  145. data/lib/rails/tasks/tmp.rake +5 -5
  146. data/lib/rails/tasks/zeitwerk.rake +15 -35
  147. data/lib/rails/tasks.rb +0 -2
  148. data/lib/rails/templates/rails/mailers/email.html.erb +32 -0
  149. data/lib/rails/templates/rails/mailers/index.html.erb +14 -7
  150. data/lib/rails/templates/rails/mailers/mailer.html.erb +11 -5
  151. data/lib/rails/templates/rails/welcome/index.html.erb +1 -0
  152. data/lib/rails/test_help.rb +9 -14
  153. data/lib/rails/test_unit/line_filtering.rb +1 -1
  154. data/lib/rails/test_unit/reporter.rb +6 -2
  155. data/lib/rails/test_unit/runner.rb +36 -18
  156. data/lib/rails/test_unit/test_parser.rb +88 -0
  157. data/lib/rails/test_unit/testing.rake +13 -33
  158. data/lib/rails/testing/maintain_test_schema.rb +16 -0
  159. data/lib/rails/version.rb +1 -1
  160. data/lib/rails/zeitwerk_checker.rb +15 -0
  161. data/lib/rails.rb +15 -15
  162. metadata +64 -27
  163. data/RDOC_MAIN.rdoc +0 -97
  164. data/lib/rails/application/dummy_erb_compiler.rb +0 -18
  165. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_0.rb.tt +0 -143
  166. data/lib/rails/generators/rails/model/USAGE +0 -113
  167. data/lib/rails/tasks/middleware.rake +0 -9
  168. data/lib/rails/tasks/restart.rake +0 -9
@@ -11,32 +11,32 @@ namespace :tmp do
11
11
 
12
12
  tmp_dirs.each { |d| directory d }
13
13
 
14
- desc "Creates tmp directories for cache, sockets, and pids"
14
+ desc "Create tmp directories for cache, sockets, and pids"
15
15
  task create: tmp_dirs
16
16
 
17
17
  namespace :cache do
18
- # desc "Clears all files and directories in tmp/cache"
18
+ # desc "Clear all files and directories in tmp/cache"
19
19
  task :clear do
20
20
  rm_rf Dir["tmp/cache/[^.]*"], verbose: false
21
21
  end
22
22
  end
23
23
 
24
24
  namespace :sockets do
25
- # desc "Clears all files in tmp/sockets"
25
+ # desc "Clear all files in tmp/sockets"
26
26
  task :clear do
27
27
  rm Dir["tmp/sockets/[^.]*"], verbose: false
28
28
  end
29
29
  end
30
30
 
31
31
  namespace :pids do
32
- # desc "Clears all files in tmp/pids"
32
+ # desc "Clear all files in tmp/pids"
33
33
  task :clear do
34
34
  rm Dir["tmp/pids/[^.]*"], verbose: false
35
35
  end
36
36
  end
37
37
 
38
38
  namespace :screenshots do
39
- # desc "Clears all files in tmp/screenshots"
39
+ # desc "Clear all files in tmp/screenshots"
40
40
  task :clear do
41
41
  rm Dir["tmp/screenshots/[^.]*"], verbose: false
42
42
  end
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- eager_load = ->() do
4
- puts "Hold on, I am eager loading the application."
5
- Zeitwerk::Loader.eager_load_all
6
- end
3
+ require "rails/zeitwerk_checker"
7
4
 
8
- report_not_checked = ->(not_checked) do
5
+ report_unchecked = ->(unchecked) do
9
6
  puts
10
7
  puts <<~EOS
11
8
  WARNING: The following directories will only be checked if you configure
@@ -13,7 +10,7 @@ report_not_checked = ->(not_checked) do
13
10
  EOS
14
11
  puts
15
12
 
16
- not_checked.each { |dir| puts " #{dir}" }
13
+ unchecked.each { |dir| puts " #{dir}" }
17
14
  puts
18
15
 
19
16
  puts <<~EOS
@@ -23,39 +20,22 @@ report_not_checked = ->(not_checked) do
23
20
  puts
24
21
  end
25
22
 
26
- report = ->(not_checked) do
27
- if not_checked.any?
28
- report_not_checked[not_checked]
29
- puts "Otherwise, all is good!"
30
- else
31
- puts "All is good!"
32
- end
33
- end
34
-
35
23
  namespace :zeitwerk do
36
- desc "Checks project structure for Zeitwerk compatibility"
24
+ desc "Check project structure for Zeitwerk compatibility"
37
25
  task check: :environment do
26
+ puts "Hold on, I am eager loading the application."
27
+
38
28
  begin
39
- eager_load[]
40
- rescue NameError => e
41
- if e.message =~ /expected file .*? to define constant [\w:]+/
42
- abort $&.sub(/expected file #{Regexp.escape(Rails.root.to_s)}./, "expected file ")
43
- else
44
- raise
45
- end
29
+ unchecked = Rails::ZeitwerkChecker.check
30
+ rescue Zeitwerk::NameError => e
31
+ abort e.message.sub(/#{Regexp.escape(Rails.root.to_s)}./, "")
46
32
  end
47
33
 
48
- require "active_support/core_ext/object/try"
49
- eager_load_paths = Rails.configuration.eager_load_namespaces.filter_map do |eln|
50
- # Quick regression fix for 6.0.3 to support namespaces that do not have
51
- # eager load paths, like the recently added i18n. I'll rewrite this task.
52
- eln.try(:config).try(:eager_load_paths)
53
- end.flatten
54
-
55
- not_checked = ActiveSupport::Dependencies.autoload_paths - eager_load_paths
56
- not_checked.select! { |dir| Dir.exist?(dir) }
57
- not_checked.reject! { |dir| Dir.empty?(dir) }
58
-
59
- report[not_checked]
34
+ if unchecked.empty?
35
+ puts "All is good!"
36
+ else
37
+ report_unchecked[unchecked]
38
+ puts "Otherwise, all is good!"
39
+ end
60
40
  end
61
41
  end
data/lib/rails/tasks.rb CHANGED
@@ -6,9 +6,7 @@ require "rake"
6
6
  %w(
7
7
  framework
8
8
  log
9
- middleware
10
9
  misc
11
- restart
12
10
  tmp
13
11
  yarn
14
12
  zeitwerk
@@ -44,6 +44,13 @@
44
44
  content: "\00a0"; // &nbsp;
45
45
  }
46
46
 
47
+ th {
48
+ font-weight: inherit;
49
+ color: #7f7f7f;
50
+ text-align: right;
51
+ white-space: nowrap;
52
+ }
53
+
47
54
  iframe {
48
55
  border: 0;
49
56
  width: 100%;
@@ -80,6 +87,11 @@
80
87
  <dd id="cc"><%= @email.header['cc'] %></dd>
81
88
  <% end %>
82
89
 
90
+ <% if @email.bcc %>
91
+ <dt>BCC:</dt>
92
+ <dd id="bcc"><%= @email.header['bcc'] %></dd>
93
+ <% end %>
94
+
83
95
  <dt>Date:</dt>
84
96
  <dd id="date"><%= Time.current.rfc2822 %></dd>
85
97
 
@@ -120,6 +132,26 @@
120
132
  </select>
121
133
  </dd>
122
134
  <% end %>
135
+
136
+ <% unless @email.header_fields.nil? || @email.header_fields.empty? %>
137
+ <dt>Headers:</dt>
138
+ <dd>
139
+ <details>
140
+ <summary>Show all headers</summary>
141
+ <table>
142
+ <% @email.header_fields.each do |field| %>
143
+ <tr>
144
+ <th><%= field.name %>:</th>
145
+ <td><%= field.value %></td>
146
+ </tr>
147
+ <% end %>
148
+ </table>
149
+ </details>
150
+ </dd>
151
+ <% end %>
152
+
153
+ <dt>EML File:</dt>
154
+ <dd><%= link_to "Download", action: :download %></dd>
123
155
  </dl>
124
156
  </header>
125
157
 
@@ -1,8 +1,15 @@
1
- <% @previews.each do |preview| %>
2
- <h3><%= link_to preview.preview_name.titleize, url_for(controller: "rails/mailers", action: "preview", path: preview.preview_name) %></h3>
3
- <ul>
4
- <% preview.emails.each do |email| %>
5
- <li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{preview.preview_name}/#{email}") %></li>
6
- <% end %>
7
- </ul>
1
+ <h1><%= @page_title %></h1>
2
+
3
+ <% if @previews.any? %>
4
+ <% @previews.each do |preview| %>
5
+ <h3><%= link_to preview.preview_name.titleize, url_for(controller: "rails/mailers", action: "preview", path: preview.preview_name) %></h3>
6
+ <ul>
7
+ <% preview.emails.each do |email| %>
8
+ <li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{preview.preview_name}/#{email}") %></li>
9
+ <% end %>
10
+ </ul>
11
+ <% end %>
12
+ <% else %>
13
+ <p>You have not defined any Action Mailer Previews.</p>
14
+ <p>Read <%= link_to "Action Mailer Basics", "https://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails" %> to learn how to define your first.</p>
8
15
  <% end %>
@@ -1,6 +1,12 @@
1
- <h3><%= @preview.preview_name.titleize %></h3>
2
- <ul>
3
- <% @preview.emails.each do |email| %>
4
- <li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{@preview.preview_name}/#{email}") %></li>
1
+ <h1><%= @page_title %></h1>
2
+
3
+ <% if @preview.emails.any? %>
4
+ <ul>
5
+ <% @preview.emails.each do |email| %>
6
+ <li><%= link_to email, url_for(controller: "rails/mailers", action: "preview", path: "#{@preview.preview_name}/#{email}") %></li>
7
+ <% end %>
8
+ </ul>
9
+ <% else %>
10
+ <p>You have not defined any actions for <%= @preview %>.</p>
11
+ <p>Read <%= link_to "Action Mailer Basics", "https://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails" %> to learn how to define your first.</p>
5
12
  <% end %>
6
- </ul>
@@ -50,6 +50,7 @@
50
50
  border-radius: 100%;
51
51
  display: flex;
52
52
  transition: background 0.25s cubic-bezier(0.33, 1, 0.68, 1);
53
+ filter: drop-shadow(0 20px 13px rgb(0 0 0 / 0.03)) drop-shadow(0 8px 5px rgb(0 0 0 / 0.08));
53
54
  }
54
55
 
55
56
  nav a:hover {
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :enddoc:
4
+
3
5
  # Make double-sure the RAILS_ENV is not set to production,
4
6
  # so fixtures aren't loaded into that environment
5
7
  abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?
@@ -12,24 +14,19 @@ require "rails/generators/test_case"
12
14
 
13
15
  require "active_support/testing/autorun"
14
16
 
15
- if defined?(ActiveRecord::Base)
16
- begin
17
- ActiveRecord::Migration.maintain_test_schema!
18
- rescue ActiveRecord::PendingMigrationError => e
19
- puts e.to_s.strip
20
- exit 1
21
- end
17
+ require "rails/testing/maintain_test_schema"
22
18
 
19
+ if defined?(ActiveRecord::Base)
23
20
  ActiveSupport.on_load(:active_support_test_case) do
24
21
  include ActiveRecord::TestDatabases
25
22
  include ActiveRecord::TestFixtures
26
23
 
27
- self.fixture_path = "#{Rails.root}/test/fixtures/"
28
- self.file_fixture_path = fixture_path + "files"
24
+ self.fixture_paths << "#{Rails.root}/test/fixtures/"
25
+ self.file_fixture_path = "#{Rails.root}/test/fixtures/files"
29
26
  end
30
27
 
31
28
  ActiveSupport.on_load(:action_dispatch_integration_test) do
32
- self.fixture_path = ActiveSupport::TestCase.fixture_path
29
+ self.fixture_paths += ActiveSupport::TestCase.fixture_paths
33
30
  end
34
31
  else
35
32
  ActiveSupport.on_load(:active_support_test_case) do
@@ -37,17 +34,15 @@ else
37
34
  end
38
35
  end
39
36
 
40
- # :enddoc:
41
-
42
37
  ActiveSupport.on_load(:action_controller_test_case) do
43
- def before_setup # :nodoc:
38
+ def before_setup
44
39
  @routes = Rails.application.routes
45
40
  super
46
41
  end
47
42
  end
48
43
 
49
44
  ActiveSupport.on_load(:action_dispatch_integration_test) do
50
- def before_setup # :nodoc:
45
+ def before_setup
51
46
  @routes = Rails.application.routes
52
47
  super
53
48
  end
@@ -5,7 +5,7 @@ require "rails/test_unit/runner"
5
5
  module Rails
6
6
  module LineFiltering # :nodoc:
7
7
  def run(reporter, options = {})
8
- options[:filter] = Rails::TestUnit::Runner.compose_filter(self, options[:filter])
8
+ options = options.merge(filter: Rails::TestUnit::Runner.compose_filter(self, options[:filter]))
9
9
 
10
10
  super
11
11
  end
@@ -6,7 +6,7 @@ require "minitest"
6
6
  module Rails
7
7
  class TestUnitReporter < Minitest::StatisticsReporter
8
8
  class_attribute :app_root
9
- class_attribute :executable, default: "rails test"
9
+ class_attribute :executable, default: "bin/rails test"
10
10
 
11
11
  def record(result)
12
12
  super
@@ -52,7 +52,11 @@ module Rails
52
52
  end
53
53
 
54
54
  def relative_path_for(file)
55
- file.sub(/^#{app_root}\/?/, "")
55
+ if app_root
56
+ file.sub(/^#{app_root}\/?/, "")
57
+ else
58
+ file
59
+ end
56
60
  end
57
61
 
58
62
  private
@@ -1,14 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shellwords"
4
- require "method_source"
5
4
  require "rake/file_list"
6
5
  require "active_support"
7
6
  require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/core_ext/range"
8
+ require "rails/test_unit/test_parser"
8
9
 
9
10
  module Rails
10
11
  module TestUnit
11
12
  class Runner
13
+ TEST_FOLDERS = [:models, :helpers, :channels, :controllers, :mailers, :integration, :jobs, :mailboxes]
14
+ PATH_ARGUMENT_PATTERN = %r"^(?!/.+/$)[.\w]*[/\\]"
12
15
  mattr_reader :filters, default: []
13
16
 
14
17
  class << self
@@ -30,9 +33,9 @@ module Rails
30
33
  $VERBOSE = argv.delete_at(w_index) if w_index
31
34
  end
32
35
 
33
- def rake_run(argv = [])
36
+ def run_from_rake(test_command, argv = [])
34
37
  # Ensure the tests run during the Rake Task action, not when the process exits
35
- success = system("rails", "test", *argv, *Shellwords.split(ENV["TESTOPTS"] || ""))
38
+ success = system("rails", test_command, *argv, *Shellwords.split(ENV["TESTOPTS"] || ""))
36
39
  success || exit(false)
37
40
  end
38
41
 
@@ -43,11 +46,14 @@ module Rails
43
46
  end
44
47
 
45
48
  def load_tests(argv)
46
- tests = list_tests(argv)
49
+ patterns = extract_filters(argv)
50
+ tests = list_tests(patterns)
47
51
  tests.to_a.each { |path| require File.expand_path(path) }
48
52
  end
49
53
 
50
54
  def compose_filter(runnable, filter)
55
+ filter = normalize_declarative_test_filter(filter)
56
+
51
57
  if filters.any? { |_, lines| lines.any? }
52
58
  CompositeFilter.new(runnable, filter, filters)
53
59
  else
@@ -59,11 +65,11 @@ module Rails
59
65
  def extract_filters(argv)
60
66
  # Extract absolute and relative paths but skip -n /.*/ regexp filters.
61
67
  argv.filter_map do |path|
62
- next unless path_argument?(path) && !regexp_filter?(path)
68
+ next unless path_argument?(path)
63
69
 
64
70
  path = path.tr("\\", "/")
65
71
  case
66
- when /(:\d+)+$/.match?(path)
72
+ when /(:\d+(-\d+)?)+$/.match?(path)
67
73
  file, *lines = path.split(":")
68
74
  filters << [ file, lines ]
69
75
  file
@@ -89,16 +95,27 @@ module Rails
89
95
  end
90
96
 
91
97
  def path_argument?(arg)
92
- %r"^[/\\]?\w+[/\\]".match?(arg)
98
+ PATH_ARGUMENT_PATTERN.match?(arg)
93
99
  end
94
100
 
95
- def list_tests(argv)
96
- patterns = extract_filters(argv)
97
-
101
+ def list_tests(patterns)
98
102
  tests = Rake::FileList[patterns.any? ? patterns : default_test_glob]
99
103
  tests.exclude(default_test_exclude_glob) if patterns.empty?
100
104
  tests
101
105
  end
106
+
107
+ def normalize_declarative_test_filter(filter)
108
+ if filter.is_a?(String)
109
+ if regexp_filter?(filter)
110
+ # Minitest::Spec::DSL#it does not replace whitespace in method
111
+ # names, so match unmodified method names as well.
112
+ filter = filter.gsub(/\s+/, "_").delete_suffix("/") + "|" + filter.delete_prefix("/")
113
+ elsif !filter.start_with?("test_")
114
+ filter = "test_#{filter.gsub(/\s+/, "_")}"
115
+ end
116
+ end
117
+ filter
118
+ end
102
119
  end
103
120
  end
104
121
 
@@ -139,17 +156,21 @@ module Rails
139
156
  end
140
157
 
141
158
  class Filter # :nodoc:
142
- def initialize(runnable, file, line)
159
+ def initialize(runnable, file, line_or_range)
143
160
  @runnable, @file = runnable, File.expand_path(file)
144
- @line = line.to_i if line
161
+ if line_or_range
162
+ first, last = line_or_range.split("-").map(&:to_i)
163
+ last ||= first
164
+ @line_range = Range.new(first, last)
165
+ end
145
166
  end
146
167
 
147
168
  def ===(method)
148
169
  return unless @runnable.method_defined?(method)
149
170
 
150
- if @line
171
+ if @line_range
151
172
  test_file, test_range = definition_for(@runnable.instance_method(method))
152
- test_file == @file && test_range.include?(@line)
173
+ test_file == @file && @line_range.overlaps?(test_range)
153
174
  else
154
175
  @runnable.instance_method(method).source_location.first == @file
155
176
  end
@@ -157,10 +178,7 @@ module Rails
157
178
 
158
179
  private
159
180
  def definition_for(method)
160
- file, start_line = method.source_location
161
- end_line = method.source.count("\n") + start_line - 1
162
-
163
- return file, start_line..end_line
181
+ TestParser.definition_for(method)
164
182
  end
165
183
  end
166
184
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ripper"
4
+
5
+ module Rails
6
+ module TestUnit
7
+ # Parse a test file to extract the line ranges of all tests in both
8
+ # method-style (def test_foo) and declarative-style (test "foo" do)
9
+ class TestParser < Ripper # :nodoc:
10
+ # Helper to translate a method object into the path and line range where
11
+ # the method was defined.
12
+ def self.definition_for(method_obj)
13
+ path, begin_line = method_obj.source_location
14
+ begins_to_ends = new(File.read(path), path).parse
15
+ return unless end_line = begins_to_ends[begin_line]
16
+ [path, (begin_line..end_line)]
17
+ end
18
+
19
+ def initialize(*)
20
+ # A hash mapping the 1-indexed line numbers that tests start on to where they end.
21
+ @begins_to_ends = {}
22
+ super
23
+ end
24
+
25
+ def parse
26
+ super
27
+ @begins_to_ends
28
+ end
29
+
30
+ # method test e.g. `def test_some_description`
31
+ # This event's first argument gets the `ident` node containing the method
32
+ # name, which we have overridden to return the line number of the ident
33
+ # instead.
34
+ def on_def(begin_line, *)
35
+ @begins_to_ends[begin_line] = lineno
36
+ end
37
+
38
+ # Everything past this point is to support declarative tests, which
39
+ # require more work to get right because of the many different ways
40
+ # methods can be invoked in ruby, all of which are parsed differently.
41
+ #
42
+ # The approach is just to store the current line number when the
43
+ # "test" method is called and pass it up the tree so it's available at
44
+ # the point when we also know the line where the associated block ends.
45
+
46
+ def on_method_add_block(begin_line, end_line)
47
+ if begin_line && end_line
48
+ @begins_to_ends[begin_line] = end_line
49
+ end
50
+ end
51
+
52
+ def on_command_call(*, begin_lineno, _args)
53
+ begin_lineno
54
+ end
55
+
56
+ def first_arg(arg, *)
57
+ arg
58
+ end
59
+
60
+ def just_lineno(*)
61
+ lineno
62
+ end
63
+
64
+ alias on_method_add_arg first_arg
65
+ alias on_command first_arg
66
+ alias on_stmts_add first_arg
67
+ alias on_arg_paren first_arg
68
+ alias on_bodystmt first_arg
69
+
70
+ alias on_ident just_lineno
71
+ alias on_do_block just_lineno
72
+ alias on_stmts_new just_lineno
73
+ alias on_brace_block just_lineno
74
+
75
+ def on_args_new
76
+ []
77
+ end
78
+
79
+ def on_args_add(parts, part)
80
+ parts << part
81
+ end
82
+
83
+ def on_args_add_block(args, *rest)
84
+ args.first
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,18 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- gem "minitest"
4
3
  require "minitest"
5
4
  require "rails/test_unit/runner"
6
5
 
7
6
  task default: :test
8
7
 
9
- desc "Runs all tests in test folder except system ones"
8
+ desc "Run all tests in test folder except system ones"
10
9
  task :test do
11
- if ENV.key?("TEST")
12
- Rails::TestUnit::Runner.rake_run([ENV["TEST"]])
13
- else
14
- Rails::TestUnit::Runner.rake_run
15
- end
10
+ Rails::TestUnit::Runner.run_from_rake("test", Array(ENV["TEST"]))
16
11
  end
17
12
 
18
13
  namespace :test do
@@ -23,37 +18,22 @@ namespace :test do
23
18
 
24
19
  task run: %w[test]
25
20
 
26
- desc "Run tests quickly, but also reset db"
21
+ desc "Reset the database and run `bin/rails test`"
27
22
  task :db do
28
23
  success = system({ "RAILS_ENV" => ENV.fetch("RAILS_ENV", "test") }, "rake", "db:test:prepare", "test")
29
24
  success || exit(false)
30
25
  end
31
26
 
32
- ["models", "helpers", "channels", "controllers", "mailers", "integration", "jobs", "mailboxes"].each do |name|
33
- task name => "test:prepare" do
34
- Rails::TestUnit::Runner.rake_run(["test/#{name}"])
27
+ [
28
+ *Rails::TestUnit::Runner::TEST_FOLDERS,
29
+ :all,
30
+ :generators,
31
+ :units,
32
+ :functionals,
33
+ :system,
34
+ ].each do |name|
35
+ task name do
36
+ Rails::TestUnit::Runner.run_from_rake("test:#{name}")
35
37
  end
36
38
  end
37
-
38
- desc "Runs all tests, including system tests"
39
- task all: "test:prepare" do
40
- Rails::TestUnit::Runner.rake_run(["test/**/*_test.rb"])
41
- end
42
-
43
- task generators: "test:prepare" do
44
- Rails::TestUnit::Runner.rake_run(["test/lib/generators"])
45
- end
46
-
47
- task units: "test:prepare" do
48
- Rails::TestUnit::Runner.rake_run(["test/models", "test/helpers", "test/unit"])
49
- end
50
-
51
- task functionals: "test:prepare" do
52
- Rails::TestUnit::Runner.rake_run(["test/controllers", "test/mailers", "test/functional"])
53
- end
54
-
55
- desc "Run system tests only"
56
- task system: "test:prepare" do
57
- Rails::TestUnit::Runner.rake_run(["test/system"])
58
- end
59
39
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(ActiveRecord::Base)
4
+ begin
5
+ ActiveRecord::Migration.maintain_test_schema!
6
+ rescue ActiveRecord::PendingMigrationError => e
7
+ puts e.to_s.strip
8
+ exit 1
9
+ end
10
+
11
+ if Rails.configuration.eager_load
12
+ ActiveRecord::Base.descendants.each do |model|
13
+ model.load_schema if !model.abstract_class? && model.table_exists?
14
+ end
15
+ end
16
+ end
data/lib/rails/version.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require_relative "gem_version"
4
4
 
5
5
  module Rails
6
- # Returns the currently loaded version of Rails as a string.
6
+ # Returns the currently loaded version of \Rails as a string.
7
7
  def self.version
8
8
  VERSION::STRING
9
9
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The actual (private) implementation of the Rake task zeitwerk:check.
4
+ class Rails::ZeitwerkChecker # :nodoc:
5
+ def self.check
6
+ Zeitwerk::Loader.eager_load_all
7
+
8
+ autoloaded = ActiveSupport::Dependencies.autoload_paths + ActiveSupport::Dependencies.autoload_once_paths
9
+ eager_loaded = ActiveSupport::Dependencies._eager_load_paths.to_a
10
+
11
+ unchecked = autoloaded - eager_loaded
12
+ unchecked.select! { |dir| Dir.exist?(dir) && !Dir.empty?(dir) }
13
+ unchecked
14
+ end
15
+ end