Skip to content

Commit 48b4718

Browse files
committed
Test and fix routes auditor
1 parent 0d23531 commit 48b4718

File tree

5 files changed

+86
-19
lines changed

5 files changed

+86
-19
lines changed

lib/routes_coverage/auditor.rb

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ def controllers
1414
Rails.application.eager_load!
1515

1616
logger.info "Collecting controllers"
17-
ActionController::Base.descendants + ActionController::API.descendants
17+
if defined?(ActionController::API)
18+
ActionController::Base.descendants + ActionController::API.descendants
19+
else
20+
# older rails
21+
ActionController::Base.descendants
22+
end
1823
end
1924
end
2025

@@ -35,6 +40,7 @@ def controller_class_by_name(controller_name)
3540
rescue NameError
3641
@missing_controllers << controller_name
3742
logger.warn "Controller #{controller_name} looks not existing"
43+
nil
3844
end
3945

4046
def existing_actions_usage_hash
@@ -45,14 +51,18 @@ def existing_actions_usage_hash
4551
controller_name = controller.name.sub(/Controller$/, "").underscore
4652
controller.action_methods.map { |action| "#{controller_name}##{action}" }
4753
end
48-
controller_actions.flatten.to_h { |action| [action, 0] }
54+
controller_actions.flatten.map { |action| [action, 0] }.to_h
4955
end
5056
end
5157

58+
def all_routes
59+
# NB: there're no engines
60+
@all_routes ||= RoutesCoverage._collect_all_routes
61+
end
62+
5263
def perform
5364
require 'routes_coverage'
54-
# NB: there're no engines
55-
routes = RoutesCoverage._collect_all_routes
65+
routes = all_routes
5666

5767
@missing_actions = Hash.new(0)
5868
@existing_actions_usage_hash = nil
@@ -104,19 +114,33 @@ def unused_actions
104114
end
105115

106116
def print_missing_actions
107-
logger.info "Missing #{missing_actions.count} actions:"
117+
logger.info "\nMissing #{missing_actions.count} actions:"
108118

109-
# NB: для `resource` могут лезть лишние index в преложениях
119+
# NB: for singular `resource` there may be unnecessary `index` in suggestions
110120
restful_actions = %w[index new create show edit update destroy].freeze
121+
122+
declared_restful = all_routes.select { |route|
123+
route.respond_to?(:requirements) && route.requirements[:controller] &&
124+
restful_actions.include?(route.requirements[:action])
125+
}.group_by{ |route| route.requirements[:controller]}
126+
111127
missing_actions.keys.map { |action| action.split('#', 2) }.group_by(&:first).each do |(controller, actions)|
112128
missing = actions.map(&:last)
113-
if (restful_actions & missing).any?
114-
logger.info "#{controller}, except: %i[#{(restful_actions & missing).join(' ')}], "\
115-
"only: %i[#{(restful_actions - missing).join(' ')}]"
116-
end
117-
118-
missing_custom = missing - restful_actions
119-
logger.info "#{controller} missing custom: #{missing_custom.join(', ')}" if missing_custom.any?
129+
next if missing.empty?
130+
131+
undeclared_restful = restful_actions - declared_restful[controller].map{|r| r.requirements[:action] }
132+
logger.info([
133+
"#{controller}:",
134+
(if (restful_actions & missing).any?
135+
"#{(missing & restful_actions).join(', ')}"\
136+
", except: %i[#{(restful_actions & (missing + undeclared_restful)).join(' ')}]"\
137+
", only: %i[#{(restful_actions - (missing + undeclared_restful)).join(' ')}]"
138+
end),
139+
(if (missing - restful_actions).any?
140+
", Missing custom: #{(missing - restful_actions).join(', ')}"
141+
end)
142+
143+
].compact.join(' '))
120144
end
121145
end
122146

spec/fixtures/dummy_app.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ def index
2626
def update
2727
render status: :ok, inline: 'lala'
2828
end
29+
30+
# patch to fix rails 4 tests
31+
def _routes
32+
Rails.application.routes
33+
end
2934
end
3035

3136
DummyApplication.initialize!

spec/fixtures/dummy_routes.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,3 @@
1919
get 'r*catch_almost_all_route', to: 'dummy#not_found_error'
2020
end
2121

22-
# patch to fix rails 4
23-
class DummyController < ActionController::Base
24-
def _routes
25-
Rails.application.routes
26-
end
27-
end
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
require_relative 'dummy_app'
4+
require 'routes_coverage/auditor'
5+
6+
DummyApplication.routes.draw do
7+
resources :reqs, only: %i[index update create], controller: :dummy do
8+
post :current, on: :collection
9+
get :some_custom
10+
end
11+
12+
namespace :somespace do
13+
resources :foo, only: [:index]
14+
end
15+
16+
namespace :otherspace do
17+
resources :bar, only: [:index]
18+
end
19+
20+
resources :subdomain_route, only: [:index], constraints: { subdomain: 'subdomain' }
21+
22+
get 'r*catch_almost_all_route', to: 'dummy#not_found_error'
23+
end
24+
25+
26+
27+
RoutesCoverage::Auditor.new.print_missing_actions

spec/main_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,23 @@ def run_dummy_rspec(testfile = 'dummy_rspec.rb')
126126
refute_match(route_regex, res)
127127
end
128128

129+
it "can detect missing actions from routes" do
130+
res, code = run_dummy_test 'dummy_test_missing_actions.rb'
131+
assert(code.success?)
132+
assert_includes(res, "Controller somespace/foo looks not existing")
133+
assert_includes(res, "Controller otherspace/bar looks not existing")
134+
assert_includes(res, "Controller subdomain_route looks not existing")
135+
assert_includes(res, "Missing 6 actions:")
136+
137+
_, missing_actions = res.split("Missing 6 actions:\n", 2)
138+
assert_equal(<<~TXT, missing_actions)
139+
dummy: create, except: %i[new create show edit destroy], only: %i[index update] , Missing custom: some_custom, not_found_error
140+
somespace/foo: index, except: %i[index new create show edit update destroy], only: %i[]
141+
otherspace/bar: index, except: %i[index new create show edit update destroy], only: %i[]
142+
subdomain_route: index, except: %i[index new create show edit update destroy], only: %i[]
143+
TXT
144+
end
145+
129146
if defined? RSpec
130147
it "works with rspec" do
131148
res, code = run_dummy_rspec 'dummy_rspec.rb'

0 commit comments

Comments
 (0)