routes_coverage 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/lib/routes_coverage/auditor.rb +156 -0
- data/lib/routes_coverage/result.rb +3 -1
- data/lib/routes_coverage/version.rb +1 -1
- data/lib/routes_coverage.rb +4 -4
- data/routes_coverage.gemspec +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f992227e3172a9ee4ea678d302b2d6655cdf16feed78020947375cc627a0254
|
4
|
+
data.tar.gz: 4f5dae5790bac97b3675d085d771a17175e8744f00fed659625592dc471c8a27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97e2aaf45f30572215e26fe51370257f00ee92129c38404050f569d5faf3d290690ddd1f5b5022c366edbb09b3b3fe5b16e9c6a50814ce86c2a4d6fc313d8be0
|
7
|
+
data.tar.gz: 432330b7f4a1641f03bb9bcfed7dc2a85868a2b6637cc2ba3015454471f1bc7a33889730feb7fdb8257b0456a536defbceeff320c88a899d4dd19500882fe2f4
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## Unreleased
|
4
|
+
|
5
|
+
-
|
6
|
+
|
7
|
+
## 0.7.0
|
8
|
+
|
9
|
+
- Support for Rails 7 (added tests, main code was already working)
|
10
|
+
- Fixed: errors on Rails 3 and ruby 2.3
|
11
|
+
- New feature: `require 'routes_coverage/auditor'; RoutesCoverage::Auditor.new.print_missing_actions` detects actions present in routes, but not present in controllers.
|
12
|
+
`print_unused_actions` - the other direction. Useful for routes cleanup.
|
13
|
+
|
14
|
+
- Known bug: collecting coverage data from engines is still not supported :(
|
15
|
+
|
16
|
+
## <= 0.6.0
|
17
|
+
|
18
|
+
In commit history, sorry
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RoutesCoverage
|
4
|
+
class Auditor
|
5
|
+
def logger
|
6
|
+
@logger ||= Logger.new($stdout).tap do |log|
|
7
|
+
log.formatter = ->(_severity, _datetime, _progname, msg) { "#{msg}\n" }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def controllers
|
12
|
+
@controllers ||= begin
|
13
|
+
logger.info "Eager-loading app to collect controllers"
|
14
|
+
Rails.application.eager_load!
|
15
|
+
|
16
|
+
logger.info "Collecting controllers"
|
17
|
+
if defined?(ActionController::API)
|
18
|
+
ActionController::Base.descendants + ActionController::API.descendants
|
19
|
+
else
|
20
|
+
# older rails
|
21
|
+
ActionController::Base.descendants
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def controllers_hash
|
27
|
+
@controllers_hash ||= controllers.index_by { |controller| controller.name.sub(/Controller$/, "").underscore }
|
28
|
+
end
|
29
|
+
|
30
|
+
def controller_class_by_name(controller_name)
|
31
|
+
controller = controllers_hash[controller_name]
|
32
|
+
return controller if controller
|
33
|
+
|
34
|
+
@missing_controllers ||= Set.new
|
35
|
+
return if @missing_controllers.include?(controller_name)
|
36
|
+
|
37
|
+
controllers_hash[controller_name] ||= "#{controller_name}_controller".classify.constantize
|
38
|
+
logger.warn "Controller #{controller_name} was not collected, but exists"
|
39
|
+
controllers_hash[controller_name]
|
40
|
+
rescue ArgumentError => e
|
41
|
+
@missing_controllers << controller_name
|
42
|
+
logger.warn "Controller #{controller_name} failed to load: #{e}"
|
43
|
+
nil
|
44
|
+
rescue NameError
|
45
|
+
@missing_controllers << controller_name
|
46
|
+
logger.warn "Controller #{controller_name} looks not existing"
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def existing_actions_usage_hash
|
51
|
+
@existing_actions_usage_hash ||= begin
|
52
|
+
logger.info "Collecting actions"
|
53
|
+
controller_actions = controllers.map do |controller|
|
54
|
+
# cannot use controller.controller_name - it has no namespace, same thing without demodulize:
|
55
|
+
controller_name = controller.name.sub(/Controller$/, "").underscore
|
56
|
+
controller.action_methods.map { |action| "#{controller_name}##{action}" }
|
57
|
+
end
|
58
|
+
controller_actions.flatten.map { |action| [action, 0] }.to_h
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def all_routes
|
63
|
+
# NB: there're no engines
|
64
|
+
@all_routes ||= RoutesCoverage._collect_all_routes
|
65
|
+
end
|
66
|
+
|
67
|
+
def perform
|
68
|
+
require 'routes_coverage'
|
69
|
+
routes = all_routes
|
70
|
+
|
71
|
+
@missing_actions = Hash.new(0)
|
72
|
+
@existing_actions_usage_hash = nil
|
73
|
+
routes.each do |route|
|
74
|
+
next unless route.respond_to?(:requirements) && route.requirements[:controller]
|
75
|
+
|
76
|
+
action = "#{route.requirements[:controller]}##{route.requirements[:action]}"
|
77
|
+
if existing_actions_usage_hash[action]
|
78
|
+
existing_actions_usage_hash[action] += 1
|
79
|
+
else
|
80
|
+
# there may be inheritance or implicit renders
|
81
|
+
controller_instance = controller_class_by_name(route.requirements[:controller])&.new
|
82
|
+
unless controller_instance&.available_action?(route.requirements[:action])
|
83
|
+
if controller_instance.respond_to?(route.requirements[:action])
|
84
|
+
logger.warn "No action, but responds: #{action}"
|
85
|
+
end
|
86
|
+
@missing_actions[action] += 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def missing_actions
|
93
|
+
perform unless @missing_actions
|
94
|
+
@missing_actions
|
95
|
+
end
|
96
|
+
|
97
|
+
def unused_actions
|
98
|
+
perform unless @existing_actions_usage_hash
|
99
|
+
|
100
|
+
root = "#{Rails.root}/" # rubocop:disable Rails/FilePath
|
101
|
+
@unused_actions ||= begin
|
102
|
+
# methods with special suffixes are obviously not actions, reduce noise:
|
103
|
+
unused_actions_from_hash = existing_actions_usage_hash.reject do |action, count|
|
104
|
+
count.positive? || action.end_with?('?') || action.end_with?('!') || action.end_with?('=')
|
105
|
+
end
|
106
|
+
|
107
|
+
unused_actions_from_hash.keys.map do |action|
|
108
|
+
controller_name, action_name = action.split('#', 2)
|
109
|
+
controller = controller_class_by_name(controller_name)&.new
|
110
|
+
method = controller.method(action_name.to_sym)
|
111
|
+
if method&.source_location && method.source_location.first.start_with?(root)
|
112
|
+
"#{method.source_location.first.sub(root, '')}:#{method.source_location.second} - #{action}"
|
113
|
+
else
|
114
|
+
action
|
115
|
+
end
|
116
|
+
end.uniq.sort
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def print_missing_actions
|
121
|
+
logger.info "\nMissing #{missing_actions.count} actions:"
|
122
|
+
|
123
|
+
# NB: for singular `resource` there may be unnecessary `index` in suggestions
|
124
|
+
restful_actions = %w[index new create show edit update destroy].freeze
|
125
|
+
|
126
|
+
declared_restful = all_routes.select { |route|
|
127
|
+
route.respond_to?(:requirements) && route.requirements[:controller] &&
|
128
|
+
restful_actions.include?(route.requirements[:action])
|
129
|
+
}.group_by{ |route| route.requirements[:controller]}
|
130
|
+
|
131
|
+
missing_actions.keys.map { |action| action.split('#', 2) }.group_by(&:first).each do |(controller, actions)|
|
132
|
+
missing = actions.map(&:last)
|
133
|
+
next if missing.empty?
|
134
|
+
|
135
|
+
undeclared_restful = restful_actions - declared_restful[controller].map{|r| r.requirements[:action] }
|
136
|
+
logger.info([
|
137
|
+
"#{controller}:",
|
138
|
+
(if (restful_actions & missing).any?
|
139
|
+
"#{(missing & restful_actions).join(', ')}"\
|
140
|
+
", except: %i[#{(restful_actions & (missing + undeclared_restful)).join(' ')}]"\
|
141
|
+
", only: %i[#{(restful_actions - (missing + undeclared_restful)).join(' ')}]"
|
142
|
+
end),
|
143
|
+
(if (missing - restful_actions).any?
|
144
|
+
", Missing custom: #{(missing - restful_actions).join(', ')}"
|
145
|
+
end)
|
146
|
+
|
147
|
+
].compact.join(' '))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def print_unused_actions
|
152
|
+
logger.info "Unused #{unused_actions.count} actions:"
|
153
|
+
unused_actions.each { |action| logger.info action }
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -95,8 +95,10 @@ module RoutesCoverage
|
|
95
95
|
namespaces_regex = Regexp.union(@settings.exclude_namespaces.map { |n| %r{^/#{n}} })
|
96
96
|
|
97
97
|
routes_groups = all_routes.group_by do |r|
|
98
|
+
# rails <=4 has regex in verb
|
99
|
+
verb = r.verb.is_a?(Regexp) && r.verb.inspect.gsub(/[^\w]/, '') || r.verb
|
98
100
|
(
|
99
|
-
("#{
|
101
|
+
("#{verb} #{r.path.spec}".strip =~ filter_regex) ||
|
100
102
|
(r.path.spec.to_s =~ namespaces_regex)
|
101
103
|
).present?
|
102
104
|
end
|
data/lib/routes_coverage.rb
CHANGED
@@ -170,7 +170,7 @@ module RoutesCoverage
|
|
170
170
|
when :constraints
|
171
171
|
value.all? do |constraint_name, constraint_value|
|
172
172
|
if constraint_value.present?
|
173
|
-
route.constraints[constraint_name] && route.constraints[constraint_name].match
|
173
|
+
route.constraints[constraint_name] && route.constraints[constraint_name].match(constraint_value)
|
174
174
|
else
|
175
175
|
route.constraints[constraint_name].blank?
|
176
176
|
end
|
@@ -178,7 +178,7 @@ module RoutesCoverage
|
|
178
178
|
end
|
179
179
|
end
|
180
180
|
else
|
181
|
-
route.path.spec.to_s.match
|
181
|
+
route.path.spec.to_s.match(matcher)
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
@@ -197,9 +197,9 @@ module RoutesCoverage
|
|
197
197
|
else # rails < 4.2
|
198
198
|
dispatcher = route.app
|
199
199
|
req.env['action_dispatch.request.path_parameters'] =
|
200
|
-
(env['action_dispatch.request.path_parameters'] || {}).merge(parameters)
|
200
|
+
(req.env['action_dispatch.request.path_parameters'] || {}).merge(parameters)
|
201
201
|
while dispatcher.is_a?(ActionDispatch::Routing::Mapper::Constraints)
|
202
|
-
dispatcher = (dispatcher.app if dispatcher.matches?(env))
|
202
|
+
dispatcher = (dispatcher.app if dispatcher.matches?(req.env))
|
203
203
|
end
|
204
204
|
end
|
205
205
|
next unless dispatcher
|
data/routes_coverage.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.require_paths = ["lib"]
|
27
27
|
|
28
28
|
spec.add_development_dependency 'appraisal'
|
29
|
-
spec.add_development_dependency "bundler"
|
29
|
+
spec.add_development_dependency "bundler" #, ">= 2.2.10"
|
30
30
|
spec.add_development_dependency "minitest"
|
31
31
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
32
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: routes_coverage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vasily Fedoseyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: appraisal
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,6 +74,7 @@ executables: []
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- CHANGELOG.md
|
77
78
|
- LICENSE.txt
|
78
79
|
- README.md
|
79
80
|
- compiled_assets/routes.css
|
@@ -82,6 +83,7 @@ files:
|
|
82
83
|
- lib/routes_coverage/adapters/atexit.rb
|
83
84
|
- lib/routes_coverage/adapters/rspec.rb
|
84
85
|
- lib/routes_coverage/adapters/simplecov.rb
|
86
|
+
- lib/routes_coverage/auditor.rb
|
85
87
|
- lib/routes_coverage/formatters/base.rb
|
86
88
|
- lib/routes_coverage/formatters/full_text.rb
|
87
89
|
- lib/routes_coverage/formatters/html.rb
|
@@ -111,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
113
|
- !ruby/object:Gem::Version
|
112
114
|
version: '0'
|
113
115
|
requirements: []
|
114
|
-
rubygems_version: 3.
|
116
|
+
rubygems_version: 3.3.3
|
115
117
|
signing_key:
|
116
118
|
specification_version: 4
|
117
119
|
summary: Provides coverage report for your rails routes
|