Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 37 additions & 13 deletions lib/routes_coverage/auditor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ def controllers
Rails.application.eager_load!

logger.info "Collecting controllers"
ActionController::Base.descendants + ActionController::API.descendants
if defined?(ActionController::API)
ActionController::Base.descendants + ActionController::API.descendants
else
# older rails
ActionController::Base.descendants
end
end
end

Expand All @@ -35,6 +40,7 @@ def controller_class_by_name(controller_name)
rescue NameError
@missing_controllers << controller_name
logger.warn "Controller #{controller_name} looks not existing"
nil
end

def existing_actions_usage_hash
Expand All @@ -45,14 +51,18 @@ def existing_actions_usage_hash
controller_name = controller.name.sub(/Controller$/, "").underscore
controller.action_methods.map { |action| "#{controller_name}##{action}" }
end
controller_actions.flatten.to_h { |action| [action, 0] }
controller_actions.flatten.map { |action| [action, 0] }.to_h
end
end

def all_routes
# NB: there're no engines
@all_routes ||= RoutesCoverage._collect_all_routes
end

def perform
require 'routes_coverage'
# NB: there're no engines
routes = RoutesCoverage._collect_all_routes
routes = all_routes

@missing_actions = Hash.new(0)
@existing_actions_usage_hash = nil
Expand Down Expand Up @@ -104,19 +114,33 @@ def unused_actions
end

def print_missing_actions
logger.info "Missing #{missing_actions.count} actions:"
logger.info "\nMissing #{missing_actions.count} actions:"

# NB: для `resource` могут лезть лишние index в преложениях
# NB: for singular `resource` there may be unnecessary `index` in suggestions
restful_actions = %w[index new create show edit update destroy].freeze

declared_restful = all_routes.select { |route|
route.respond_to?(:requirements) && route.requirements[:controller] &&
restful_actions.include?(route.requirements[:action])
}.group_by{ |route| route.requirements[:controller]}

missing_actions.keys.map { |action| action.split('#', 2) }.group_by(&:first).each do |(controller, actions)|
missing = actions.map(&:last)
if (restful_actions & missing).any?
logger.info "#{controller}, except: %i[#{(restful_actions & missing).join(' ')}], "\
"only: %i[#{(restful_actions - missing).join(' ')}]"
end

missing_custom = missing - restful_actions
logger.info "#{controller} missing custom: #{missing_custom.join(', ')}" if missing_custom.any?
next if missing.empty?

undeclared_restful = restful_actions - declared_restful[controller].map{|r| r.requirements[:action] }
logger.info([
"#{controller}:",
(if (restful_actions & missing).any?
"#{(missing & restful_actions).join(', ')}"\
", except: %i[#{(restful_actions & (missing + undeclared_restful)).join(' ')}]"\
", only: %i[#{(restful_actions - (missing + undeclared_restful)).join(' ')}]"
end),
(if (missing - restful_actions).any?
", Missing custom: #{(missing - restful_actions).join(', ')}"
end)

].compact.join(' '))
end
end

Expand Down
5 changes: 5 additions & 0 deletions spec/fixtures/dummy_app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def index
def update
render status: :ok, inline: 'lala'
end

# patch to fix rails 4 tests
def _routes
Rails.application.routes
end
end

DummyApplication.initialize!
6 changes: 0 additions & 6 deletions spec/fixtures/dummy_routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,3 @@
get 'r*catch_almost_all_route', to: 'dummy#not_found_error'
end

# patch to fix rails 4
class DummyController < ActionController::Base
def _routes
Rails.application.routes
end
end
27 changes: 27 additions & 0 deletions spec/fixtures/dummy_test_missing_actions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

require_relative 'dummy_app'
require 'routes_coverage/auditor'

DummyApplication.routes.draw do
resources :reqs, only: %i[index update create], controller: :dummy do
post :current, on: :collection
get :some_custom
end

namespace :somespace do
resources :foo, only: [:index]
end

namespace :otherspace do
resources :bar, only: [:index]
end

resources :subdomain_route, only: [:index], constraints: { subdomain: 'subdomain' }

get 'r*catch_almost_all_route', to: 'dummy#not_found_error'
end



RoutesCoverage::Auditor.new.print_missing_actions
17 changes: 17 additions & 0 deletions spec/main_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,23 @@ def run_dummy_rspec(testfile = 'dummy_rspec.rb')
refute_match(route_regex, res)
end

it "can detect missing actions from routes" do
res, code = run_dummy_test 'dummy_test_missing_actions.rb'
assert(code.success?)
assert_includes(res, "Controller somespace/foo looks not existing")
assert_includes(res, "Controller otherspace/bar looks not existing")
assert_includes(res, "Controller subdomain_route looks not existing")
assert_includes(res, "Missing 6 actions:")

_, missing_actions = res.split("Missing 6 actions:\n", 2)
assert_equal(<<~TXT, missing_actions)
dummy: create, except: %i[new create show edit destroy], only: %i[index update] , Missing custom: some_custom, not_found_error
somespace/foo: index, except: %i[index new create show edit update destroy], only: %i[]
otherspace/bar: index, except: %i[index new create show edit update destroy], only: %i[]
subdomain_route: index, except: %i[index new create show edit update destroy], only: %i[]
TXT
end

if defined? RSpec
it "works with rspec" do
res, code = run_dummy_rspec 'dummy_rspec.rb'
Expand Down