New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add "Did you mean?" for unrecognized CLI commands #47208
Add "Did you mean?" for unrecognized CLI commands #47208
Conversation
cea89fc
to
4e80029
Compare
This commit improves the error message that is displayed when a user specifies an unrecognized `bin/rails` command by offering "Did you mean?" suggestions. The suggestions cover all visible commands, and supersede any incomplete (Rake-task-only) suggestions that Rake might display. __Before (example)__ ```console $ bin/rails credentails:edit rails aborted! Don't know how to build task 'credentails:edit' (See the list of available tasks with `rails --tasks`) (See full trace by running task with --trace) $ bin/rails --tasks ... rails cache_digests:dependencies # Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html) rails cache_digests:nested_dependencies # Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html) rails db:create # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use ... rails db:drop # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db... ... $ bin/rails credentails -h # no output $ bin/rails credentials -h # no output $ bin/rails versoin rails aborted! Don't know how to build task 'versoin' (See the list of available tasks with `rails --tasks`) Did you mean? db:version (See full trace by running task with --trace) ``` __After (example)__ ```console $ bin/rails credentails:edit Unrecognized command "credentails:edit" (Rails::Command::UnrecognizedCommandError) Did you mean? credentials:edit $ bin/rails credentails -h Unrecognized command "credentails" (Rails::Command::UnrecognizedCommandError) Did you mean? credentials:diff $ bin/rails credentials -h Unrecognized command "credentials" (Rails::Command::UnrecognizedCommandError) Did you mean? credentials:diff $ bin/rails versoin Unrecognized command "versoin" (Rails::Command::UnrecognizedCommandError) Did you mean? version ```
4e80029
to
2530160
Compare
Follow-up to rails#47208. When using Rake-style CLI arguments, `Rake::Application#top_level_tasks` will return the full task invocation rather than the parsed task name. For example, when running `rake foo[bar] baz[qux]`, `top_level_tasks` will return `["foo[bar]", "baz[qux]"]` rather than `["foo", "baz"]`. Therefore, we must extract the task name before checking existence.
This causes a warning with a default generated engine plugin. It's also related to #46664 (edit: wrong PR). $ rails plugin new my_engine --main --full
$ cd my_engine
$ sed -Ei '' -e '/uri|homepage|host/ d' -e 's/TODO: //' *.gemspec # fix gemspec to be able to bundle
$ bin/rails test >/dev/null
/tmp/my_engine/Rakefile:3: warning: already initialized constant APP_RAKEFILE
/tmp/my_engine/Rakefile:3: warning: previous definition of APP_RAKEFILE was here Engine plugins have a curious setup which defines all the dummy app's tasks under the rails/railties/lib/rails/tasks/engine.rake Lines 4 to 5 in 779c14b
Since #47210 we check if there's a test:prepare task:rails/railties/lib/rails/commands/test/test_command.rb Lines 72 to 76 in 779c14b
Even though the exception is rescued, to initialize it we need all the existing commands: rails/railties/lib/rails/command.rb Line 43 in 779c14b
which asks RakeCommand its commands, which loads the Rakefile again: rails/railties/lib/rails/command/actions.rb Lines 31 to 34 in 779c14b
I can see multiple ways we can fix this:
I think the last one makes the most sense, but we need the metadata since we're filtering the tasks without comments, just doing that would remove the did you mean in that case. I think we could load the metadata in engine.rake, that way we don't lose that information for that specific case. |
Follow-up to rails#47208. Prior to rails#47208, `Rails::Command::ApplicationTest` appears to have been specifically intended to test `Rails::Command::ApplicationCommand`. Therefore, this commit extracts the tests that were added by rails#47208 out from `railties/test/command/application_test.rb` into `railties/test/command/help_integration_test.rb`, and moves `application_test.rb` from `railties/test/command` to `railties/test/commands` (with the rest of the command-specific tests).
Follow-up to rails#47208. `UnrecognizedCommandError` calls `printing_commands` to make "Did you mean?" suggestions. `printing_commands` calls `RakeCommand#rake_tasks`, which loads the Rake tasks if they have not been memoized. If the tasks have already been loaded by `RakeCommand#perform` (but not memoized by `RakeCommand#rake_tasks`), then the tasks will be loaded a second time. This can cause, for example, constant redefinition warnings if the task files define constants. Therefore, this commit memoizes the tasks from `RakeCommand#perform` before raising `UnrecognizedCommandError`.
@etiennebarrie Thank you for the detailed write up! 😍 I think avoiding the double load (option 3) is probably the best way. To that end, I've submitted #47718, which memoizes the already-loaded tasks before raising |
Follow-up to rails#47208. Prior to rails#47208, `Rails::Command::ApplicationTest` appears to have been specifically intended to test `Rails::Command::ApplicationCommand`. Therefore, this commit extracts the tests that were added by rails#47208 out from `railties/test/command/application_test.rb` into `railties/test/command/help_integration_test.rb`, and moves `application_test.rb` from `railties/test/command` to `railties/test/commands` (with the rest of the command-specific tests).
Follow-up to rails#47208. `UnrecognizedCommandError` calls `printing_commands` to make "Did you mean?" suggestions. `printing_commands` calls `RakeCommand::rake_tasks`, which loads the Rake tasks if they have not been memoized. If the tasks have already been loaded by `RakeCommand::perform` (but not memoized by `RakeCommand::rake_tasks`), then the tasks will be loaded a second time. This can cause, for example, constant redefinition warnings if the task files define constants. Therefore, this commit memoizes the tasks from `RakeCommand::perform` before raising `UnrecognizedCommandError`.
This commit improves the error message that is displayed when a user specifies an unrecognized
bin/rails
command by offering "Did you mean?" suggestions. The suggestions cover all visible commands, and supersede any incomplete (Rake-task-only) suggestions that Rake might display.Before (example)
After (example)