Skip to content
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 "rails routes --expanded" mode #32130

Merged
merged 1 commit into from Feb 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion actionpack/lib/action_dispatch/routing.rb
Expand Up @@ -243,7 +243,8 @@ module ActionDispatch
#
# rails routes
#
# Target specific controllers by prefixing the command with <tt>-c</tt> option.
# Target specific controllers by prefixing the command with <tt>-c</tt> option. Use
# <tt>--expanded</tt> to turn on the expanded table formatting mode.
#
module Routing
extend ActiveSupport::Autoload
Expand Down
127 changes: 90 additions & 37 deletions actionpack/lib/action_dispatch/routing/inspector.rb
Expand Up @@ -126,61 +126,114 @@ def collect_engine_routes(route)
end

class ConsoleFormatter
def initialize
@buffer = []
end
class Sheet
def initialize
@buffer = []
end

def result
@buffer.join("\n")
end
def result
@buffer.join("\n")
end

def section_title(title)
@buffer << "\n#{title}:"
end
def section_title(title)
@buffer << "\n#{title}:"
end

def section(routes)
@buffer << draw_section(routes)
end
def section(routes)
@buffer << draw_section(routes)
end

def header(routes)
@buffer << draw_header(routes)
end
def header(routes)
@buffer << draw_header(routes)
end

def no_routes(routes)
@buffer <<
if routes.none?
<<~MESSAGE
def no_routes(routes)
@buffer <<
if routes.none?
<<~MESSAGE
You don't have any routes defined!

Please add some routes in config/routes.rb.
MESSAGE
else
"No routes were found for this controller"
MESSAGE
else
"No routes were found for this controller"
end
@buffer << "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
end
@buffer << "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
end

private
def draw_section(routes)
header_lengths = ["Prefix", "Verb", "URI Pattern"].map(&:length)
name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
private

def draw_section(routes)
header_lengths = ["Prefix", "Verb", "URI Pattern"].map(&:length)
name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)

routes.map do |r|
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
routes.map do |r|
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
end
end

def draw_header(routes)
name_width, verb_width, path_width = widths(routes)

"#{"Prefix".rjust(name_width)} #{"Verb".ljust(verb_width)} #{"URI Pattern".ljust(path_width)} Controller#Action"
end

def widths(routes)
[routes.map { |r| r[:name].length }.max || 0,
routes.map { |r| r[:verb].length }.max || 0,
routes.map { |r| r[:path].length }.max || 0]
end
end

class Expanded < ConsoleFormatter
def initialize
@buffer = []
end

def draw_header(routes)
name_width, verb_width, path_width = widths(routes)
def result
@buffer.join("")
end

def section_title(title)
@buffer << "\n#{"[ #{title} ]"}\n"
end

"#{"Prefix".rjust(name_width)} #{"Verb".ljust(verb_width)} #{"URI Pattern".ljust(path_width)} Controller#Action"
def section(routes)
@buffer << draw_expanded_section(routes)
end

def widths(routes)
[routes.map { |r| r[:name].length }.max || 0,
routes.map { |r| r[:verb].length }.max || 0,
routes.map { |r| r[:path].length }.max || 0]
def header(routes)
@buffer
end

def no_routes(routes)
@buffer <<
if routes.none?
<<~MESSAGE
You don't have any routes defined!

Please add some routes in config/routes.rb.\n
MESSAGE
else
"No routes were found for this controller\n"
end
@buffer << "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
end

private

def draw_expanded_section(routes)
routes.map.each_with_index do |r, i|
<<~MESSAGE
--[ Route #{i + 1} ]#{'-' * 60}
Prefix | #{r[:name]}
Verb | #{r[:verb]}
URI | #{r[:path]}
Controller#Action | #{r[:reqs]}
MESSAGE
end
end
end
end

class HtmlTableFormatter
Expand Down
68 changes: 66 additions & 2 deletions actionpack/test/dispatch/routing/inspector_test.rb
Expand Up @@ -19,10 +19,10 @@ def setup
@set = ActionDispatch::Routing::RouteSet.new
end

def draw(options = nil, &block)
def draw(options = nil, formater = ActionDispatch::Routing::ConsoleFormatter::Sheet.new, &block)
@set.draw(&block)
inspector = ActionDispatch::Routing::RoutesInspector.new(@set.routes)
inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, options).split("\n")
inspector.format(formater, options).split("\n")
end

def test_displaying_routes_for_engines
Expand Down Expand Up @@ -321,6 +321,70 @@ def test_routes_can_be_filtered
" DELETE /posts/:id(.:format) posts#destroy"], output
end

def test_routes_when_expanded
engine = Class.new(Rails::Engine) do
def self.inspect
"Blog::Engine"
end
end
engine.routes.draw do
get "/cart", to: "cart#show"
end

output = draw(nil, ActionDispatch::Routing::ConsoleFormatter::Expanded.new) do
get "/custom/assets", to: "custom_assets#show"
get "/custom/furnitures", to: "custom_furnitures#show"
mount engine => "/blog", :as => "blog"
end

assert_equal ["--[ Route 1 ]------------------------------------------------------------",
"Prefix | custom_assets",
"Verb | GET",
"URI | /custom/assets(.:format)",
"Controller#Action | custom_assets#show",
"--[ Route 2 ]------------------------------------------------------------",
"Prefix | custom_furnitures",
"Verb | GET",
"URI | /custom/furnitures(.:format)",
"Controller#Action | custom_furnitures#show",
"--[ Route 3 ]------------------------------------------------------------",
"Prefix | blog",
"Verb | ",
"URI | /blog",
"Controller#Action | Blog::Engine",
"",
"[ Routes for Blog::Engine ]",
"--[ Route 1 ]------------------------------------------------------------",
"Prefix | cart",
"Verb | GET",
"URI | /cart(.:format)",
"Controller#Action | cart#show"], output
end


def test_no_routes_matched_filter_when_expanded
output = draw("rails/dummy", ActionDispatch::Routing::ConsoleFormatter::Expanded.new) do
get "photos/:id" => "photos#show", :id => /[A-Z]\d{5}/
end

assert_equal [
"No routes were found for this controller",
"For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
], output
end

def test_not_routes_when_expanded
output = draw("rails/dummy", ActionDispatch::Routing::ConsoleFormatter::Expanded.new) {}

assert_equal [
"You don't have any routes defined!",
"",
"Please add some routes in config/routes.rb.",
"",
"For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
], output
end

def test_routes_can_be_filtered_with_namespaced_controllers
output = draw("admin/posts") do
resources :articles
Expand Down
2 changes: 1 addition & 1 deletion guides/source/routing.md
Expand Up @@ -1183,7 +1183,7 @@ $ bin/rails routes -c Comments
$ bin/rails routes -c Articles::CommentsController
```

TIP: You'll find that the output from `rails routes` is much more readable if you widen your terminal window until the output lines don't wrap.
TIP: You'll find that the output from `rails routes` is much more readable if you widen your terminal window until the output lines don't wrap. You can also use --expanded option to turn on the expanded table formatting mode.

### Testing Routes

Expand Down
19 changes: 19 additions & 0 deletions railties/CHANGELOG.md
@@ -1,5 +1,24 @@
## Rails 6.0.0.alpha (Unreleased) ##

* Add "rails routes --expanded" option to output routes in expanded mode like
"psql --expanded". Result looks like:

```
$ rails routes --expanded
--[ Route 1 ]------------------------------------------------------------
Prefix | high_scores
Verb | GET
URI | /high_scores(.:format)
Controller#Action | high_scores#index
--[ Route 2 ]------------------------------------------------------------
Prefix | new_high_score
Verb | GET
URI | /high_scores/new(.:format)
Controller#Action | high_scores#new
```

*Benoit Tigeot*

* Rails 6 requires Ruby 2.4.1 or newer.

*Jeremy Daer*
Expand Down
8 changes: 7 additions & 1 deletion railties/lib/rails/commands/routes/routes_command.rb
Expand Up @@ -7,12 +7,14 @@ module Command
class RoutesCommand < Base # :nodoc:
class_option :controller, aliases: "-c", type: :string, desc: "Specifies the controller."
class_option :grep_pattern, aliases: "-g", type: :string, desc: "Specifies grep pattern."
class_option :expanded_format, aliases: "--expanded", type: :string, desc: "Turn on expanded format mode."

no_commands do
def help
say "Usage: Print out all defined routes in match order, with names."
say ""
say "Target specific controller with -c option, or grep routes using -g option"
say "Use expanded format with --expanded option"
say ""
end
end
Expand All @@ -24,7 +26,11 @@ def perform(*)
all_routes = Rails.application.routes.routes
inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)

say inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, routes_filter)
if options.has_key?("expanded_format")
say inspector.format(ActionDispatch::Routing::ConsoleFormatter::Expanded.new, routes_filter)
else
say inspector.format(ActionDispatch::Routing::ConsoleFormatter::Sheet.new, routes_filter)
end
end

private
Expand Down
40 changes: 0 additions & 40 deletions railties/test/application/rake_test.rb
Expand Up @@ -122,46 +122,6 @@ def test_code_statistics_sanity
rails("stats")
end

def test_rails_routes_calls_the_route_inspector
app_file "config/routes.rb", <<-RUBY
Rails.application.routes.draw do
get '/cart', to: 'cart#show'
end
RUBY

output = rails("routes")
assert_equal <<~MESSAGE, output
Prefix Verb URI Pattern Controller#Action
cart GET /cart(.:format) cart#show
rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show
rails_blob_preview GET /rails/active_storage/previews/:signed_blob_id/:variation_key/*filename(.:format) active_storage/previews#show
rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
MESSAGE
end

def test_singular_resource_output_in_rake_routes
app_file "config/routes.rb", <<-RUBY
Rails.application.routes.draw do
resource :post
end
RUBY

expected_output = [" Prefix Verb URI Pattern Controller#Action",
" new_post GET /post/new(.:format) posts#new",
"edit_post GET /post/edit(.:format) posts#edit",
" post GET /post(.:format) posts#show",
" PATCH /post(.:format) posts#update",
" PUT /post(.:format) posts#update",
" DELETE /post(.:format) posts#destroy",
" POST /post(.:format) posts#create\n"].join("\n")

output = rails("routes", "-c", "PostController")
assert_equal expected_output, output
end

def test_logger_is_flushed_when_exiting_production_rake_tasks
add_to_config <<-RUBY
rake_tasks do
Expand Down