railties 7.2.3 → 8.0.0.beta1

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -324
  3. data/README.rdoc +1 -1
  4. data/lib/minitest/rails_plugin.rb +2 -2
  5. data/lib/rails/application/bootstrap.rb +3 -1
  6. data/lib/rails/application/configuration.rb +22 -7
  7. data/lib/rails/application/default_middleware_stack.rb +4 -0
  8. data/lib/rails/application/finisher.rb +2 -2
  9. data/lib/rails/application/routes_reloader.rb +11 -1
  10. data/lib/rails/application.rb +14 -12
  11. data/lib/rails/backtrace_cleaner.rb +1 -1
  12. data/lib/rails/code_statistics.rb +128 -86
  13. data/lib/rails/code_statistics_calculator.rb +78 -76
  14. data/lib/rails/command/helpers/editor.rb +1 -1
  15. data/lib/rails/commands/app/update_command.rb +1 -9
  16. data/lib/rails/commands/console/irb_console.rb +6 -3
  17. data/lib/rails/commands/credentials/USAGE +4 -4
  18. data/lib/rails/commands/credentials/credentials_command.rb +5 -1
  19. data/lib/rails/commands/dev/dev_command.rb +1 -1
  20. data/lib/rails/commands/devcontainer/devcontainer_command.rb +1 -1
  21. data/lib/rails/commands/stats/stats_command.rb +19 -0
  22. data/lib/rails/commands/test/test_command.rb +2 -0
  23. data/lib/rails/dev_caching.rb +2 -2
  24. data/lib/rails/engine/configuration.rb +3 -1
  25. data/lib/rails/engine/lazy_route_set.rb +109 -0
  26. data/lib/rails/engine.rb +8 -3
  27. data/lib/rails/gem_version.rb +4 -4
  28. data/lib/rails/generators/actions.rb +5 -9
  29. data/lib/rails/generators/app_base.rb +49 -31
  30. data/lib/rails/generators/database.rb +101 -67
  31. data/lib/rails/generators/erb/authentication/authentication_generator.rb +15 -0
  32. data/lib/rails/generators/erb/authentication/templates/views/passwords/edit.html.erb +9 -0
  33. data/lib/rails/generators/erb/authentication/templates/views/passwords/new.html.erb +8 -0
  34. data/lib/rails/generators/erb/authentication/templates/views/sessions/new.html.erb +11 -0
  35. data/lib/rails/generators/generated_attribute.rb +16 -11
  36. data/lib/rails/generators/rails/app/app_generator.rb +19 -28
  37. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +12 -3
  38. data/lib/rails/generators/rails/app/templates/Gemfile.tt +23 -8
  39. data/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt +6 -11
  40. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +10 -3
  41. data/lib/rails/generators/rails/app/templates/bin/dev.tt +1 -0
  42. data/lib/rails/generators/rails/app/templates/bin/setup.tt +5 -7
  43. data/lib/rails/generators/rails/app/templates/bin/thrust.tt +4 -0
  44. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +23 -0
  45. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +23 -0
  46. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +40 -0
  47. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +23 -0
  48. data/lib/rails/generators/rails/app/templates/config/deploy.yml.tt +124 -0
  49. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +12 -23
  50. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +31 -51
  51. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +5 -19
  52. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +0 -7
  53. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +1 -1
  54. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_0.rb.tt +25 -0
  55. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +11 -2
  56. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +3 -3
  57. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +4 -3
  58. data/lib/rails/generators/rails/app/templates/dockerignore.tt +1 -2
  59. data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +1 -3
  60. data/lib/rails/generators/rails/app/templates/gitignore.tt +1 -2
  61. data/lib/rails/generators/rails/app/templates/kamal-secrets.tt +17 -0
  62. data/lib/rails/generators/rails/app/templates/public/400.html +114 -0
  63. data/lib/rails/generators/rails/app/templates/public/404.html +113 -66
  64. data/lib/rails/generators/rails/app/templates/public/406-unsupported-browser.html +113 -65
  65. data/lib/rails/generators/rails/app/templates/public/422.html +113 -66
  66. data/lib/rails/generators/rails/app/templates/public/500.html +113 -65
  67. data/lib/rails/generators/rails/app/templates/public/icon.png +0 -0
  68. data/lib/rails/generators/rails/app/templates/public/icon.svg +2 -2
  69. data/lib/rails/generators/rails/authentication/USAGE +6 -0
  70. data/lib/rails/generators/rails/authentication/authentication_generator.rb +54 -0
  71. data/lib/rails/generators/rails/authentication/templates/controllers/concerns/authentication.rb +55 -0
  72. data/lib/rails/generators/rails/authentication/templates/controllers/passwords_controller.rb +33 -0
  73. data/lib/rails/generators/rails/authentication/templates/controllers/sessions_controller.rb +21 -0
  74. data/lib/rails/generators/rails/authentication/templates/mailers/passwords_mailer.rb +6 -0
  75. data/lib/rails/generators/rails/authentication/templates/models/current.rb +4 -0
  76. data/lib/rails/generators/rails/authentication/templates/models/session.rb +3 -0
  77. data/lib/rails/generators/rails/authentication/templates/models/user.rb +6 -0
  78. data/lib/rails/generators/rails/authentication/templates/test/mailers/previews/passwords_mailer_preview.rb +7 -0
  79. data/lib/rails/generators/rails/authentication/templates/views/passwords_mailer/reset.html.erb +4 -0
  80. data/lib/rails/generators/rails/authentication/templates/views/passwords_mailer/reset.text.erb +2 -0
  81. data/lib/rails/generators/rails/credentials/templates/credentials.yml.tt +4 -0
  82. data/lib/rails/generators/rails/db/system/change/change_generator.rb +1 -1
  83. data/lib/rails/generators/rails/devcontainer/devcontainer_generator.rb +4 -2
  84. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/devcontainer.json.tt +1 -1
  85. data/lib/rails/generators/rails/plugin/plugin_generator.rb +7 -9
  86. data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +2 -3
  87. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +4 -4
  88. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +5 -5
  89. data/lib/rails/generators/rails/script/USAGE +18 -0
  90. data/lib/rails/generators/rails/script/script_generator.rb +18 -0
  91. data/lib/rails/generators/rails/script/templates/script.rb.tt +3 -0
  92. data/lib/rails/generators.rb +6 -5
  93. data/lib/rails/info_controller.rb +10 -4
  94. data/lib/rails/rack/silence_request.rb +33 -0
  95. data/lib/rails/rack.rb +1 -0
  96. data/lib/rails/source_annotation_extractor.rb +31 -14
  97. data/lib/rails/tasks/statistics.rake +13 -28
  98. data/lib/rails/templates/rails/info/notes.html.erb +65 -0
  99. data/lib/rails/test_unit/runner.rb +3 -7
  100. data/lib/rails/test_unit/test_parser.rb +15 -18
  101. metadata +45 -50
  102. data/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt +0 -2
  103. data/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb.tt +0 -4
  104. data/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb.tt +0 -4
  105. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_2.rb.tt +0 -70
  106. data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +0 -13
  107. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +0 -13
  108. data/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js.tt +0 -10
  109. data/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js.tt +0 -6
  110. data/lib/rails/generators/rails/plugin/templates/rails/javascripts.js.tt +0 -17
@@ -122,9 +122,12 @@ module Rails
122
122
  def generate_test_dummy(force = false)
123
123
  opts = options.transform_keys(&:to_sym).except(*DUMMY_IGNORE_OPTIONS)
124
124
  opts[:force] = force
125
+ opts[:skip_thruster] = true
125
126
  opts[:skip_brakeman] = true
126
127
  opts[:skip_bundle] = true
127
128
  opts[:skip_ci] = true
129
+ opts[:skip_kamal] = true
130
+ opts[:skip_solid] = true
128
131
  opts[:skip_git] = true
129
132
  opts[:skip_hotwire] = true
130
133
  opts[:skip_rubocop] = true
@@ -149,9 +152,8 @@ module Rails
149
152
  end
150
153
  end
151
154
 
152
- def test_dummy_sprocket_assets
153
- template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
154
- template "rails/dummy_manifest.js", "#{dummy_path}/app/assets/config/manifest.js", force: true
155
+ def test_dummy_assets
156
+ template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
155
157
  end
156
158
 
157
159
  def test_dummy_clean
@@ -159,7 +161,7 @@ module Rails
159
161
  remove_file ".ruby-version"
160
162
  remove_dir "db"
161
163
  remove_file "Gemfile"
162
- remove_file "lib/tasks"
164
+ remove_dir "lib"
163
165
  remove_file "public/robots.txt"
164
166
  remove_file "README.md"
165
167
  remove_file "test"
@@ -167,10 +169,6 @@ module Rails
167
169
  end
168
170
  end
169
171
 
170
- def assets_manifest
171
- template "rails/engine_manifest.js", "app/assets/config/#{underscored_name}_manifest.js"
172
- end
173
-
174
172
  def stylesheets
175
173
  if mountable?
176
174
  copy_file "rails/stylesheets.css",
@@ -361,7 +359,7 @@ module Rails
361
359
  mute do
362
360
  build(:generate_test_dummy)
363
361
  build(:test_dummy_config)
364
- build(:test_dummy_sprocket_assets) unless skip_sprockets?
362
+ build(:test_dummy_assets) unless skip_asset_pipeline?
365
363
  build(:test_dummy_clean)
366
364
  # ensure that bin/rails has proper dummy_path
367
365
  build(:bin)
@@ -84,10 +84,8 @@ jobs:
84
84
  - name: Run tests
85
85
  env:
86
86
  RAILS_ENV: test
87
- <%- if options[:database] == "mysql" -%>
87
+ <%- if options[:database] == "mysql" || options[:database] == "trilogy" -%>
88
88
  DATABASE_URL: mysql2://127.0.0.1:3306
89
- <%- elsif options[:database] == "trilogy" -%>
90
- DATABASE_URL: trilogy://127.0.0.1:3306
91
89
  <%- elsif options[:database] == "postgresql" -%>
92
90
  DATABASE_URL: postgres://postgres:postgres@localhost:5432
93
91
  <%- end -%>
@@ -102,3 +100,4 @@ jobs:
102
100
  path: ${{ github.workspace }}/tmp/screenshots
103
101
  if-no-files-found: ignore
104
102
  <% end -%>
103
+
@@ -21,7 +21,7 @@ class <%= controller_class_name %>Controller < ApplicationController
21
21
  if @<%= orm_instance.save %>
22
22
  render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %>
23
23
  else
24
- render json: <%= "@#{orm_instance.errors}" %>, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
24
+ render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
25
25
  end
26
26
  end
27
27
 
@@ -30,7 +30,7 @@ class <%= controller_class_name %>Controller < ApplicationController
30
30
  if @<%= orm_instance.update("#{singular_table_name}_params") %>
31
31
  render json: <%= "@#{singular_table_name}" %>
32
32
  else
33
- render json: <%= "@#{orm_instance.errors}" %>, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
33
+ render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
34
34
  end
35
35
  end
36
36
 
@@ -42,7 +42,7 @@ class <%= controller_class_name %>Controller < ApplicationController
42
42
  private
43
43
  # Use callbacks to share common setup or constraints between actions.
44
44
  def set_<%= singular_table_name %>
45
- @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
45
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params.expect(:id)") %>
46
46
  end
47
47
 
48
48
  # Only allow a list of trusted parameters through.
@@ -50,7 +50,7 @@ class <%= controller_class_name %>Controller < ApplicationController
50
50
  <%- if attributes_names.empty? -%>
51
51
  params.fetch(:<%= singular_table_name %>, {})
52
52
  <%- else -%>
53
- params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>)
53
+ params.expect(<%= singular_table_name %>: [ <%= permitted_params %> ])
54
54
  <%- end -%>
55
55
  end
56
56
  end
@@ -27,7 +27,7 @@ class <%= controller_class_name %>Controller < ApplicationController
27
27
  if @<%= orm_instance.save %>
28
28
  redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully created.") %>
29
29
  else
30
- render :new, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
30
+ render :new, status: :unprocessable_entity
31
31
  end
32
32
  end
33
33
 
@@ -36,20 +36,20 @@ class <%= controller_class_name %>Controller < ApplicationController
36
36
  if @<%= orm_instance.update("#{singular_table_name}_params") %>
37
37
  redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully updated.") %>, status: :see_other
38
38
  else
39
- render :edit, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
39
+ render :edit, status: :unprocessable_entity
40
40
  end
41
41
  end
42
42
 
43
43
  # DELETE <%= route_url %>/1
44
44
  def destroy
45
45
  @<%= orm_instance.destroy %>
46
- redirect_to <%= index_helper %>_url, notice: <%= %("#{human_name} was successfully destroyed.") %>, status: :see_other
46
+ redirect_to <%= index_helper %>_path, notice: <%= %("#{human_name} was successfully destroyed.") %>, status: :see_other
47
47
  end
48
48
 
49
49
  private
50
50
  # Use callbacks to share common setup or constraints between actions.
51
51
  def set_<%= singular_table_name %>
52
- @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
52
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params.expect(:id)") %>
53
53
  end
54
54
 
55
55
  # Only allow a list of trusted parameters through.
@@ -57,7 +57,7 @@ class <%= controller_class_name %>Controller < ApplicationController
57
57
  <%- if attributes_names.empty? -%>
58
58
  params.fetch(:<%= singular_table_name %>, {})
59
59
  <%- else -%>
60
- params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>)
60
+ params.expect(<%= singular_table_name %>: [ <%= permitted_params %> ])
61
61
  <%- end -%>
62
62
  end
63
63
  end
@@ -0,0 +1,18 @@
1
+ Description:
2
+ Generate a one-off or general purpose script, such as a data migration
3
+ script, cleanup script, etc.
4
+
5
+ Example:
6
+ `bin/rails generate script my_script`
7
+
8
+ This will create:
9
+ script/my_script.rb
10
+
11
+ You can run the script using:
12
+ `ruby script/my_script.rb`
13
+
14
+ You can specify a folder:
15
+ `bin/rails generate script cleanup/my_script`
16
+
17
+ This will create:
18
+ script/cleanup/my_script.rb
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/named_base"
4
+
5
+ module Rails
6
+ module Generators
7
+ class ScriptGenerator < NamedBase
8
+ def generate_script
9
+ template("script.rb.tt", "script/#{file_path}.rb")
10
+ end
11
+
12
+ private
13
+ def depth
14
+ class_path.size + 1
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ require_relative "<%= '../' * depth %>config/environment"
2
+
3
+ # Your code goes here
@@ -97,11 +97,11 @@ module Rails
97
97
  # generator group to fall back to another group in case of missing generators,
98
98
  # they can add a fallback.
99
99
  #
100
- # For example, shoulda is considered a +test_framework+ and is an extension
101
- # of +test_unit+. However, most part of shoulda generators are similar to
102
- # +test_unit+ ones.
100
+ # For example, shoulda is considered a test_framework and is an extension
101
+ # of test_unit. However, most part of shoulda generators are similar to
102
+ # test_unit ones.
103
103
  #
104
- # Shoulda then can tell generators to search for +test_unit+ generators when
104
+ # Shoulda then can tell generators to search for test_unit generators when
105
105
  # some of them are not available by adding a fallback:
106
106
  #
107
107
  # Rails::Generators.fallbacks[:shoulda] = :test_unit
@@ -272,6 +272,7 @@ module Rails
272
272
  #{error.detailed_message}
273
273
  Run `bin/rails generate --help` for more options.
274
274
  MSG
275
+ exit 1
275
276
  end
276
277
  end
277
278
 
@@ -316,7 +317,7 @@ module Rails
316
317
 
317
318
  def run_after_generate_callback
318
319
  if defined?(@@generated_files) && !@@generated_files.empty?
319
- after_generate_callbacks.each do |callback|
320
+ @after_generate_callbacks.each do |callback|
320
321
  callback.call(@@generated_files)
321
322
  end
322
323
  @@generated_files = []
@@ -7,8 +7,6 @@ class Rails::InfoController < Rails::ApplicationController # :nodoc:
7
7
  prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATHS
8
8
  layout -> { request.xhr? ? false : "application" }
9
9
 
10
- RFC2396_PARSER = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new
11
-
12
10
  before_action :require_local!
13
11
 
14
12
  def index
@@ -22,7 +20,7 @@ class Rails::InfoController < Rails::ApplicationController # :nodoc:
22
20
 
23
21
  def routes
24
22
  if query = params[:query]
25
- query = RFC2396_PARSER.escape query
23
+ query = URI::RFC2396_PARSER.escape query
26
24
 
27
25
  render json: {
28
26
  exact: matching_routes(query: query, exact_match: true),
@@ -34,6 +32,14 @@ class Rails::InfoController < Rails::ApplicationController # :nodoc:
34
32
  end
35
33
  end
36
34
 
35
+ def notes
36
+ @annotations = Rails::SourceAnnotationExtractor.new(
37
+ Rails::SourceAnnotationExtractor::Annotation.tags.join("|")
38
+ ).find(
39
+ Rails::SourceAnnotationExtractor::Annotation.directories
40
+ )
41
+ end
42
+
37
43
  private
38
44
  def matching_routes(query:, exact_match:)
39
45
  return [] if query.blank?
@@ -55,7 +61,7 @@ class Rails::InfoController < Rails::ApplicationController # :nodoc:
55
61
  match ||= (query === route_wrapper.verb)
56
62
 
57
63
  unless match
58
- controller_action = RFC2396_PARSER.escape(route_wrapper.reqs)
64
+ controller_action = URI::RFC2396_PARSER.escape(route_wrapper.reqs)
59
65
  match = exact_match ? (query === controller_action) : controller_action.include?(query)
60
66
  end
61
67
 
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/logger_silence"
6
+
7
+ module Rails
8
+ module Rack
9
+ # Allows you to silence requests made to a specific path.
10
+ # This is useful for preventing recurring requests like health checks from clogging the logging.
11
+ # This middleware is used to do just that against the path /up in production by default.
12
+ #
13
+ # Example:
14
+ #
15
+ # config.middleware.insert_before \
16
+ # Rails::Rack::Logger, Rails::Rack::SilenceRequest, path: "/up"
17
+ #
18
+ # This middleware can also be configured using `config.silence_healthcheck_path = "/up"` in Rails.
19
+ class SilenceRequest
20
+ def initialize(app, path:)
21
+ @app, @path = app, path
22
+ end
23
+
24
+ def call(env)
25
+ if env["PATH_INFO"] == @path
26
+ Rails.logger.silence { @app.call(env) }
27
+ else
28
+ @app.call(env)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
data/lib/rails/rack.rb CHANGED
@@ -3,5 +3,6 @@
3
3
  module Rails
4
4
  module Rack
5
5
  autoload :Logger, "rails/rack/logger"
6
+ autoload :SilenceRequest, "rails/rack/silence_request"
6
7
  end
7
8
  end
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "ripper"
3
+ begin
4
+ require "prism"
5
+ rescue LoadError
6
+ # If Prism isn't available (because of using an older Ruby version) then we'll
7
+ # define a fallback for the ParserExtractor using ripper.
8
+ require "ripper"
9
+ end
4
10
 
5
11
  module Rails
6
12
  # Implements the logic behind +Rails::Command::NotesCommand+. See <tt>rails notes --help</tt> for usage information.
@@ -16,24 +22,35 @@ module Rails
16
22
  # Wraps a regular expression that will be tested against each of the source
17
23
  # file's comments.
18
24
  class ParserExtractor < Struct.new(:pattern)
19
- class Parser < Ripper
20
- attr_reader :comments, :pattern
25
+ if defined?(Prism)
26
+ def annotations(file)
27
+ result = Prism.parse_file(file)
28
+ return [] unless result.success?
21
29
 
22
- def initialize(source, pattern:)
23
- super(source)
24
- @pattern = pattern
25
- @comments = []
30
+ result.comments.filter_map do |comment|
31
+ Annotation.new(comment.location.start_line, $1, $2) if comment.location.slice =~ pattern
32
+ end
26
33
  end
34
+ else
35
+ class Parser < Ripper
36
+ attr_reader :comments, :pattern
37
+
38
+ def initialize(source, pattern:)
39
+ super(source)
40
+ @pattern = pattern
41
+ @comments = []
42
+ end
27
43
 
28
- def on_comment(value)
29
- @comments << Annotation.new(lineno, $1, $2) if value =~ pattern
44
+ def on_comment(value)
45
+ @comments << Annotation.new(lineno, $1, $2) if value =~ pattern
46
+ end
30
47
  end
31
- end
32
48
 
33
- def annotations(file)
34
- contents = File.read(file, encoding: Encoding::BINARY)
35
- parser = Parser.new(contents, pattern: pattern).tap(&:parse)
36
- parser.error? ? [] : parser.comments
49
+ def annotations(file)
50
+ contents = File.read(file, encoding: Encoding::BINARY)
51
+ parser = Parser.new(contents, pattern: pattern).tap(&:parse)
52
+ parser.error? ? [] : parser.comments
53
+ end
37
54
  end
38
55
  end
39
56
 
@@ -1,32 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # While global constants are bad, many 3rd party tools depend on this one (e.g
4
- # rspec-rails & cucumber-rails). So a deprecation warning is needed if we want
5
- # to remove it.
6
- STATS_DIRECTORIES ||= [
7
- %w(Controllers app/controllers),
8
- %w(Helpers app/helpers),
9
- %w(Jobs app/jobs),
10
- %w(Models app/models),
11
- %w(Mailers app/mailers),
12
- %w(Mailboxes app/mailboxes),
13
- %w(Channels app/channels),
14
- %w(Views app/views),
15
- %w(JavaScripts app/assets/javascripts),
16
- %w(Stylesheets app/assets/stylesheets),
17
- %w(JavaScript app/javascript),
18
- %w(Libraries lib/),
19
- %w(APIs app/apis),
20
- %w(Controller\ tests test/controllers),
21
- %w(Helper\ tests test/helpers),
22
- %w(Job\ tests test/jobs),
23
- %w(Model\ tests test/models),
24
- %w(Mailer\ tests test/mailers),
25
- %w(Mailbox\ tests test/mailboxes),
26
- %w(Channel\ tests test/channels),
27
- %w(Integration\ tests test/integration),
28
- %w(System\ tests test/system),
29
- ]
3
+ require "rails/code_statistics"
4
+ STATS_DIRECTORIES = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
5
+ Rails::CodeStatistics::DIRECTORIES,
6
+ "`STATS_DIRECTORIES` is deprecated and will be removed in Rails 8.1! Use `Rails::CodeStatistics.register_directory('My Directory', 'path/to/dir)` instead.",
7
+ Rails.deprecator
8
+ )
30
9
 
31
10
  desc "Report code statistics (KLOCs, etc) from the application or engine"
32
11
  task :stats do
@@ -34,5 +13,11 @@ task :stats do
34
13
  stat_directories = STATS_DIRECTORIES.collect do |name, dir|
35
14
  [ name, "#{File.dirname(Rake.application.rakefile_location)}/#{dir}" ]
36
15
  end.select { |name, dir| File.directory?(dir) }
37
- CodeStatistics.new(*stat_directories).to_s
16
+
17
+ $stderr.puts Rails.deprecator.warn(<<~MSG, caller_locations(0..1))
18
+ `bin/rake stats` has been deprecated and will be removed in Rails 8.1.
19
+ Please use `bin/rails stats` as Rails command instead.\n
20
+ MSG
21
+
22
+ Rails::CodeStatistics.new(*stat_directories).to_s
38
23
  end
@@ -0,0 +1,65 @@
1
+ <style>
2
+ h2, p {
3
+ padding-left: 30px;
4
+ }
5
+ table {
6
+ margin: 0;
7
+ border-collapse: collapse;
8
+ word-wrap:break-word;
9
+ table-layout: fixed;
10
+ width:100%;
11
+ }
12
+ table thead tr {
13
+ border-bottom: 2px solid #ddd;
14
+ }
15
+ table th {
16
+ padding-left: 30px;
17
+ text-align: left;
18
+ }
19
+ table thead th.tag, table thead th.line-no {
20
+ width: 10%;
21
+ }
22
+ table tbody tr {
23
+ border-bottom: 1px solid #ddd;
24
+ }
25
+ table tbody tr:nth-child(odd) {
26
+ background: #f2f2f2;
27
+ }
28
+ table td {
29
+ padding: 4px 30px;
30
+ }
31
+ @media (prefers-color-scheme: dark) {
32
+ table tbody tr:nth-child(odd) {
33
+ background: #282828;
34
+ }
35
+ }
36
+ </style>
37
+
38
+ <h2>
39
+ Notes
40
+ </h2>
41
+
42
+ <table id="route_table" class="table">
43
+ <thead>
44
+ <th>File Name</th>
45
+ <th class="line-no">Line No.</th>
46
+ <th class="tag">Tag</th>
47
+ <th>Description</th>
48
+ </thead>
49
+ <tbody>
50
+ <% @annotations.each do |file, annotations| %>
51
+ <% annotations.each.with_index do |annotation, index| %>
52
+ <tr>
53
+ <% if index == 0 %>
54
+ <th rowspan="<%= annotations.size %>">
55
+ <%= file %>
56
+ </th>
57
+ <% end %>
58
+ <td class="line-no"><%= annotation.line %></td>
59
+ <td class="tag"><%= annotation.tag %></td>
60
+ <td><%= annotation.text %></td>
61
+ </tr>
62
+ <% end %>
63
+ <% end %>
64
+ </tbody>
65
+ </table>
@@ -9,17 +9,13 @@ require "rails/test_unit/test_parser"
9
9
 
10
10
  module Rails
11
11
  module TestUnit
12
- class InvalidTestError < ArgumentError
12
+ class InvalidTestError < StandardError
13
13
  def initialize(path, suggestion)
14
- super(<<~MESSAGE.rstrip)
14
+ super(<<~MESSAGE.squish)
15
15
  Could not load test file: #{path}.
16
16
  #{suggestion}
17
17
  MESSAGE
18
18
  end
19
-
20
- def backtrace(*args)
21
- []
22
- end
23
19
  end
24
20
 
25
21
  class Runner
@@ -72,7 +68,7 @@ module Rails
72
68
  if corrections.empty?
73
69
  raise exception
74
70
  end
75
- raise(InvalidTestError.new(path, DidYouMean::Formatter.message_for(corrections)), cause: nil)
71
+ raise InvalidTestError.new(path, DidYouMean::Formatter.message_for(corrections))
76
72
  else
77
73
  raise
78
74
  end
@@ -13,32 +13,29 @@ if defined?(Prism)
13
13
  # Parse a test file to extract the line ranges of all tests in both
14
14
  # method-style (def test_foo) and declarative-style (test "foo" do)
15
15
  module TestParser
16
- @begins_to_ends = {}
17
16
  # Helper to translate a method object into the path and line range where
18
17
  # the method was defined.
19
18
  def self.definition_for(method)
20
19
  filepath, start_line = method.source_location
21
- @begins_to_ends[filepath] ||= ranges(filepath)
22
- return unless end_line = @begins_to_ends[filepath][start_line]
23
- [filepath, start_line..end_line]
24
- end
20
+ queue = [Prism.parse_file(filepath).value]
25
21
 
26
- private
27
- def self.ranges(filepath)
28
- queue = [Prism.parse_file(filepath).value]
29
- begins_to_ends = {}
30
- while (node = queue.shift)
31
- case node.type
32
- when :def_node
33
- begins_to_ends[node.location.start_line] = node.location.end_line
34
- when :call_node
35
- begins_to_ends[node.location.start_line] = node.location.end_line
22
+ while (node = queue.shift)
23
+ case node.type
24
+ when :def_node
25
+ if node.location.start_line == start_line
26
+ return [filepath, start_line..node.location.end_line]
27
+ end
28
+ when :call_node
29
+ if node.location.start_line == start_line
30
+ return [filepath, start_line..node.location.end_line]
36
31
  end
37
-
38
- queue.concat(node.compact_child_nodes)
39
32
  end
40
- begins_to_ends
33
+
34
+ queue.concat(node.compact_child_nodes)
41
35
  end
36
+
37
+ nil
38
+ end
42
39
  end
43
40
  end
44
41
  end