Skip to content

Commit cf80ede

Browse files
Merge pull request rails#47718 from jonathanhefner/command-did_you_mean-load-rake-tasks-only-once
Load Rake tasks only once for command suggestions
2 parents 0f6215a + 455b8e4 commit cf80ede

File tree

5 files changed

+99
-65
lines changed

5 files changed

+99
-65
lines changed

railties/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
* `bin/rails --help` will now list only framework and plugin commands. Rake
2+
tasks defined in `lib/tasks/*.rake` files will no longer be included. For a
3+
list of those tasks, use `rake -T`.
4+
5+
*Jonathan Hefner*
6+
17
* Allow calling `bin/rails restart` outside of app directory.
28

39
The following would previously fail with a "No Rakefile found" error.

railties/lib/rails/commands/rake/rake_command.rb

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,45 @@ class RakeCommand < Base # :nodoc:
99

1010
class << self
1111
def printing_commands
12-
formatted_rake_tasks
12+
rake_tasks.filter_map do |task|
13+
if task.comment && task.locations.any?(non_app_file_pattern)
14+
[task.name_with_args, task.comment]
15+
end
16+
end
1317
end
1418

1519
def perform(task, args, config)
16-
require_rake
17-
18-
Rake.with_application do |rake|
19-
rake.init("bin/rails", [task, *args])
20-
rake.load_rakefile
20+
with_rake(task, *args) do |rake|
2121
if unrecognized_task = rake.top_level_tasks.find { |task| !rake.lookup(task[/[^\[]+/]) }
22+
@rake_tasks = rake.tasks
2223
raise UnrecognizedCommandError.new(unrecognized_task)
2324
end
2425

25-
if Rails.respond_to?(:root)
26-
rake.options.suppress_backtrace_pattern = /\A(?!#{Regexp.quote(Rails.root.to_s)})/
27-
end
26+
rake.options.suppress_backtrace_pattern = non_app_file_pattern
2827
rake.standard_exception_handling { rake.top_level }
2928
end
3029
end
3130

3231
private
33-
def rake_tasks
34-
require_rake
35-
36-
return @rake_tasks if defined?(@rake_tasks)
37-
38-
require_application!
32+
def non_app_file_pattern
33+
/\A(?!#{Regexp.quote Rails::Command.root.to_s})/
34+
end
3935

36+
def with_rake(*args, &block)
37+
require "rake"
4038
Rake::TaskManager.record_task_metadata = true
41-
Rake.application.instance_variable_set(:@name, "rails")
42-
load_tasks
43-
@rake_tasks = Rake.application.tasks.select(&:comment)
44-
end
4539

46-
def formatted_rake_tasks
47-
rake_tasks.map { |t| [ t.name_with_args, t.comment ] }
40+
result = nil
41+
Rake.with_application do |rake|
42+
rake.init(bin, args) unless args.empty?
43+
rake.load_rakefile
44+
result = block.call(rake)
45+
end
46+
result
4847
end
4948

50-
def require_rake
51-
require "rake" # Defer booting Rake until we know it's needed.
49+
def rake_tasks
50+
@rake_tasks ||= with_rake(&:tasks)
5251
end
5352
end
5453
end

railties/test/command/application_test.rb

Lines changed: 0 additions & 42 deletions
This file was deleted.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# frozen_string_literal: true
2+
3+
require "isolation/abstract_unit"
4+
require "rails/command"
5+
6+
class Rails::Command::HelpIntegrationTest < ActiveSupport::TestCase
7+
setup :build_app
8+
teardown :teardown_app
9+
10+
test "prints helpful error on unrecognized command" do
11+
output = rails "vershen", allow_failure: true
12+
13+
assert_match %(Unrecognized command "vershen"), output
14+
assert_match "Did you mean? version", output
15+
end
16+
17+
test "loads Rake tasks only once on unrecognized command" do
18+
app_file "lib/tasks/my_task.rake", <<~RUBY
19+
puts "MY_TASK already defined? => \#{!!defined?(MY_TASK)}"
20+
MY_TASK = true
21+
RUBY
22+
23+
output = rails "vershen", allow_failure: true
24+
25+
assert_match "MY_TASK already defined? => false", output
26+
assert_no_match "MY_TASK already defined? => true", output
27+
end
28+
29+
test "prints help via `X:help` command when running `X` and `X:X` command is not defined" do
30+
help = rails "dev:help"
31+
output = rails "dev", allow_failure: true
32+
33+
assert_equal help, output
34+
end
35+
36+
test "excludes application Rake tasks from command listing" do
37+
app_file "Rakefile", <<~RUBY, "a"
38+
desc "my_task"
39+
task :my_task_1
40+
RUBY
41+
42+
app_file "lib/tasks/my_task.rake", <<~RUBY
43+
desc "my_task"
44+
task :my_task_2
45+
RUBY
46+
47+
assert_no_match "my_task", rails("--help")
48+
end
49+
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
require "abstract_unit"
4+
require "rails/command"
5+
6+
class Rails::Command::ApplicationTest < ActiveSupport::TestCase
7+
test "rails new without path prints help" do
8+
output = run_application_command "new"
9+
10+
# Doesn't include the default thor error message:
11+
assert_not output.start_with?("No value provided for required arguments")
12+
13+
# Includes contents of ~/railties/lib/rails/generators/rails/app/USAGE:
14+
assert output.include?("The `rails new` command creates a new Rails application with a default
15+
directory structure and configuration at the path you specify.")
16+
end
17+
18+
private
19+
def run_application_command(*args)
20+
capture(:stdout) { Rails::Command.invoke(:application, args) }
21+
end
22+
end

0 commit comments

Comments
 (0)