rails_routes_analyzer 1.0.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.autotest +3 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +141 -0
- data/LICENSE.txt +1 -1
- data/README.md +45 -13
- data/Rakefile +1 -1
- data/lib/rails_routes_analyzer.rb +7 -6
- data/lib/rails_routes_analyzer/action_analysis.rb +276 -0
- data/lib/rails_routes_analyzer/gem_manager.rb +52 -0
- data/lib/rails_routes_analyzer/parameter_handler.rb +41 -0
- data/lib/rails_routes_analyzer/rails_routes_analyzer.rb +69 -30
- data/lib/rails_routes_analyzer/railtie.rb +11 -0
- data/lib/rails_routes_analyzer/route_analysis.rb +104 -67
- data/lib/rails_routes_analyzer/route_call.rb +65 -0
- data/lib/rails_routes_analyzer/route_file_annotator.rb +117 -37
- data/lib/rails_routes_analyzer/route_interceptor.rb +12 -13
- data/lib/rails_routes_analyzer/route_issue.rb +4 -109
- data/lib/rails_routes_analyzer/route_issue/base.rb +68 -0
- data/lib/rails_routes_analyzer/route_issue/no_action.rb +37 -0
- data/lib/rails_routes_analyzer/route_issue/no_controller.rb +30 -0
- data/lib/rails_routes_analyzer/route_issue/resources.rb +133 -0
- data/lib/rails_routes_analyzer/route_line.rb +85 -0
- data/lib/rails_routes_analyzer/version.rb +1 -1
- data/lib/tasks/rails_routes_analyzer.rake +28 -50
- data/rails_routes_analyzer.gemspec +13 -4
- metadata +129 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6438d7f464a7b4bfe625d5d7b0c046bb7dec19b6
|
4
|
+
data.tar.gz: e85d81221ecd342ec9b4a8ca995b9e9c4955586e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0a0f433be53c34ca4b61fc3167b0913807b1a967096c76dc903c52cb796b91ddb11c5088f27e6f201c81bc27115e972e5b58016304f15e5a36839fbbdf16e35
|
7
|
+
data.tar.gz: 8a6d61768c8ee3c80e513e7d05f8b98fed8ee70b3ba3b93ddba5268424bd32fb16f6126ac632db321cbfe664272d2dfb75c82aae619b64dd90de7920fdf10008
|
data/.autotest
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2016-12-04 10:48:23 +0200 using RuboCop version 0.46.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 17
|
10
|
+
Metrics/AbcSize:
|
11
|
+
Max: 35
|
12
|
+
|
13
|
+
# Offense count: 3
|
14
|
+
# Configuration parameters: CountComments.
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Max: 30
|
17
|
+
|
18
|
+
# Offense count: 6
|
19
|
+
# Configuration parameters: CountComments.
|
20
|
+
Metrics/ClassLength:
|
21
|
+
Max: 182
|
22
|
+
|
23
|
+
# Offense count: 4
|
24
|
+
Metrics/CyclomaticComplexity:
|
25
|
+
Max: 9
|
26
|
+
|
27
|
+
# Offense count: 189
|
28
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
29
|
+
# URISchemes: http, https
|
30
|
+
Metrics/LineLength:
|
31
|
+
Max: 236
|
32
|
+
|
33
|
+
# Offense count: 25
|
34
|
+
# Configuration parameters: CountComments.
|
35
|
+
Metrics/MethodLength:
|
36
|
+
Max: 44
|
37
|
+
|
38
|
+
# Offense count: 1
|
39
|
+
# Configuration parameters: CountKeywordArgs.
|
40
|
+
Metrics/ParameterLists:
|
41
|
+
Max: 7
|
42
|
+
|
43
|
+
# Offense count: 3
|
44
|
+
Metrics/PerceivedComplexity:
|
45
|
+
Max: 11
|
46
|
+
|
47
|
+
# Offense count: 1
|
48
|
+
# Cop supports --auto-correct.
|
49
|
+
Style/AlignArray:
|
50
|
+
Exclude:
|
51
|
+
- 'test/rails_routes_analyzer/route_analysis_test.rb'
|
52
|
+
|
53
|
+
# Offense count: 12
|
54
|
+
Style/Documentation:
|
55
|
+
Exclude:
|
56
|
+
- 'spec/**/*'
|
57
|
+
- 'test/**/*'
|
58
|
+
- 'lib/rails_routes_analyzer/action_analysis.rb'
|
59
|
+
- 'lib/rails_routes_analyzer/gem_manager.rb'
|
60
|
+
- 'lib/rails_routes_analyzer/parameter_handler.rb'
|
61
|
+
- 'lib/rails_routes_analyzer/rails_routes_analyzer.rb'
|
62
|
+
- 'lib/rails_routes_analyzer/railtie.rb'
|
63
|
+
- 'lib/rails_routes_analyzer/route_analysis.rb'
|
64
|
+
- 'lib/rails_routes_analyzer/route_file_annotator.rb'
|
65
|
+
- 'lib/rails_routes_analyzer/route_issue/base.rb'
|
66
|
+
- 'lib/rails_routes_analyzer/route_issue/no_action.rb'
|
67
|
+
- 'lib/rails_routes_analyzer/route_issue/no_controller.rb'
|
68
|
+
- 'lib/rails_routes_analyzer/route_issue/resources.rb'
|
69
|
+
|
70
|
+
# Offense count: 15
|
71
|
+
# Cop supports --auto-correct.
|
72
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
73
|
+
# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines
|
74
|
+
Style/EmptyLinesAroundClassBody:
|
75
|
+
Enabled: false
|
76
|
+
|
77
|
+
# Offense count: 29
|
78
|
+
# Cop supports --auto-correct.
|
79
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
80
|
+
# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines
|
81
|
+
Style/EmptyLinesAroundModuleBody:
|
82
|
+
Enabled: false
|
83
|
+
|
84
|
+
# Offense count: 9
|
85
|
+
# Cop supports --auto-correct.
|
86
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
87
|
+
# SupportedStyles: compact, expanded
|
88
|
+
Style/EmptyMethod:
|
89
|
+
Enabled: false
|
90
|
+
|
91
|
+
# Offense count: 2
|
92
|
+
# Cop supports --auto-correct.
|
93
|
+
# Configuration parameters: SupportedStyles, IndentationWidth.
|
94
|
+
# SupportedStyles: special_inside_parentheses, consistent, align_brackets
|
95
|
+
Style/IndentArray:
|
96
|
+
EnforcedStyle: consistent
|
97
|
+
|
98
|
+
# Offense count: 1
|
99
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
100
|
+
# SupportedStyles: module_function, extend_self
|
101
|
+
Style/ModuleFunction:
|
102
|
+
EnforcedStyle: extend_self
|
103
|
+
|
104
|
+
# Offense count: 7
|
105
|
+
# Cop supports --auto-correct.
|
106
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
|
107
|
+
# SupportedStyles: slashes, percent_r, mixed
|
108
|
+
Style/RegexpLiteral:
|
109
|
+
EnforcedStyle: mixed
|
110
|
+
|
111
|
+
# Offense count: 337
|
112
|
+
# Cop supports --auto-correct.
|
113
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline.
|
114
|
+
# SupportedStyles: single_quotes, double_quotes
|
115
|
+
Style/StringLiterals:
|
116
|
+
Enabled: false
|
117
|
+
|
118
|
+
# Offense count: 10
|
119
|
+
# Cop supports --auto-correct.
|
120
|
+
# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
|
121
|
+
# SupportedStyles: comma, consistent_comma, no_comma
|
122
|
+
Style/TrailingCommaInArguments:
|
123
|
+
Enabled: false
|
124
|
+
|
125
|
+
# Offense count: 13
|
126
|
+
# Cop supports --auto-correct.
|
127
|
+
# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
|
128
|
+
# SupportedStyles: comma, consistent_comma, no_comma
|
129
|
+
Style/TrailingCommaInLiteral:
|
130
|
+
EnforcedStyleForMultiline: consistent_comma
|
131
|
+
|
132
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
133
|
+
# SupportedStyles: snake_case, normalcase, non_integer
|
134
|
+
Style/VariableNumber:
|
135
|
+
Enabled: false
|
136
|
+
|
137
|
+
|
138
|
+
AllCops:
|
139
|
+
Exclude:
|
140
|
+
- 'test/dummy/routes*.rb'
|
141
|
+
- 'test/dummy/tmp/*.rb'
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
[![Build Status](https://travis-ci.org/bear-metal/rails_routes_analyzer.svg)](https://travis-ci.org/bear-metal/rails_routes_analyzer)
|
2
2
|
|
3
|
-
Adds rake tasks to detect extraneous routes and unreachable controller actions Ruby on Rails applications. It's
|
3
|
+
Adds rake tasks to detect extraneous routes and unreachable controller actions in Ruby on Rails applications. It's able to provide suggestions on how to change routes.rb to avoid defining dead routes.
|
4
|
+
|
5
|
+
It can also add comments to routes files (even when there are multiple) to suggest fixes and for certain common patterns optionally apply those fixes automatically.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
@@ -27,32 +29,62 @@ For multi-route calls like `resource' and `resources' it can also let you know i
|
|
27
29
|
For complex cases where for example a routes are created in a loop for multiple controllers a suggestion will be provided for each iteration but only if that specific iteration created a dead route. Every such suggestion will identify the exact controller to which it applies.
|
28
30
|
|
29
31
|
``` sh
|
30
|
-
rake routes:
|
32
|
+
rake routes:dead:annotate [ROUTES_FILE=path/to/routes.rb]
|
33
|
+
rake routes:dead:fix [ROUTES_FILE=path/to/routes.rb]
|
31
34
|
|
32
35
|
# Best used like this:
|
33
|
-
rake routes:
|
36
|
+
rake routes:dead:annotate > config/routest.rb.new
|
34
37
|
mv config/routes.rb.new config/routes.rb
|
35
38
|
# And then update the file as requested in any SUGGESTION comments
|
36
39
|
```
|
37
40
|
|
38
|
-
|
39
|
-
|
41
|
+
_routes:dead:annotate_ generates a commented version of a routes file. _routes:dead:fix_ generates a partly automatically fixed and partly commented version of a routes file. Without specifying a file in ROUTES_FILE parameter one is automatically picked provided that it is the only one that has problems, if there are more they will all be listed and a single name will have to be provided in the ROUTES_FILE parameter.
|
40
42
|
|
41
43
|
#### Additional options:
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
45
|
+
``` sh
|
46
|
+
ONLY_ONLY=1 # suggestions for resource routes will only generate "only:" regardless of how many elements are listed.
|
47
|
+
ONLY_EXCEPT=1 # suggestions for resource routes will only generate "except:" regardless of how many elements are listed.
|
48
|
+
ROUTES_VERBOSE=1 # more verbosity, currently this means listing which non-existing actions a given call provides routes for.
|
49
|
+
```
|
50
|
+
|
51
|
+
``` sh
|
52
|
+
rake routes:dead:annotate:inplace [ROUTES_FILE=path/to/routes.rb]
|
53
|
+
rake routes:dead:fix:inplace [ROUTES_FILE=path/to/routes.rb]
|
54
|
+
|
55
|
+
rake routes:dead:annotate:inplace[force] [ROUTES_FILE=path/to/routes.rb]
|
56
|
+
rake routes:dead:fix:inplace[force] [ROUTES_FILE=path/to/routes.rb]
|
57
|
+
```
|
58
|
+
|
59
|
+
Same as above but these commands change existing routes file content instead of printing it to standard output. By default they'll refuse to change a file if it's outside Rails root or has uncommited changes. To get around this protection set the ROUTES_FORCE=1 parameter.
|
60
|
+
|
46
61
|
|
47
62
|
```sh
|
48
|
-
rake
|
63
|
+
rake actions:missing_route
|
64
|
+
rake actions:missing_route[gems,modules,duplicates,full,metadata] # parameters can be combined in all ways
|
49
65
|
```
|
50
66
|
|
51
|
-
|
67
|
+
Lists all action methods for all controllers which have no route pointing to them. By default ignores methods coming from gems, included modules or inherited from a parent controller. Uses the ActionController#Base.action\_methods method which usually returns a list of all public methods of the controller class excluding any special Rails provided methods.
|
68
|
+
|
69
|
+
Generally it's not a problem to have ActionController#Base.action\_methods list non-actions given Rails no longer uses default routes that would benefit from proper limits on what is and what isn't an action. However there is also no obvious reason to be unable to correct it and possibly be able to use the more accurate metadata somewhere else (such as this tool).
|
70
|
+
|
71
|
+
The easiest way to remove non-actions from ActionController#Base.action\_methods is to make them protected or private. If that's not possible the other alternative is to override the action\_methods method itself and remove the relevant methods from the returned action list (this is more complicated and much more effort to keep updated.)
|
52
72
|
|
53
|
-
|
73
|
+
```sh
|
74
|
+
rake actions:list_all
|
75
|
+
rake actions:list_all[gems,modules,duplicates,full,metadata] # parameters can be combined in all ways
|
76
|
+
```
|
54
77
|
|
55
|
-
|
78
|
+
#### Additional options:
|
79
|
+
_(applies to both actions:missing\_route and actions:list\_all)_
|
80
|
+
|
81
|
+
``` sh
|
82
|
+
ROUTES_DUPLICATES=1 # report actions inherited from parent controllers (can generate a lot of noise)
|
83
|
+
ROUTES_GEMS=1 # includes actions that appear to be implemented by gems
|
84
|
+
ROUTES_MODULES=1 # includes public controller methods inherited from modules that are listed in action_methods
|
85
|
+
ROUTES_FULL_PATH=1 # disables file path shortening
|
86
|
+
ROUTES_METADATA=1 # lists collected data per action such as which gem it's from, if it's inherited from a superclass
|
87
|
+
```
|
56
88
|
|
57
89
|
## Contributing
|
58
90
|
|
@@ -62,4 +94,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/bear-m
|
|
62
94
|
|
63
95
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
64
96
|
|
65
|
-
Copyright (c) 2016 [Bear Metal](http://bearmetal.eu)
|
97
|
+
Copyright (c) 2016 Tarmo Tänav, [Bear Metal OÜ](http://bearmetal.eu),
|
data/Rakefile
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require "rails_routes_analyzer/route_issue"
|
5
|
-
require "rails_routes_analyzer/route_analysis"
|
6
|
-
require "rails_routes_analyzer/route_file_annotator"
|
1
|
+
require_relative "rails_routes_analyzer/version"
|
2
|
+
require_relative "rails_routes_analyzer/railtie"
|
3
|
+
require_relative "rails_routes_analyzer/rails_routes_analyzer"
|
7
4
|
|
5
|
+
require_relative "rails_routes_analyzer/parameter_handler"
|
6
|
+
require_relative "rails_routes_analyzer/action_analysis"
|
7
|
+
require_relative "rails_routes_analyzer/route_analysis"
|
8
|
+
require_relative "rails_routes_analyzer/route_file_annotator"
|
@@ -0,0 +1,276 @@
|
|
1
|
+
require_relative 'gem_manager'
|
2
|
+
|
3
|
+
require 'active_support/descendants_tracker'
|
4
|
+
require 'active_support/core_ext/class/subclasses'
|
5
|
+
require 'active_support/core_ext/string/strip'
|
6
|
+
|
7
|
+
module RailsRoutesAnalyzer
|
8
|
+
MAX_ACTION_LENGTH = 30 # Don't align action names longer than this
|
9
|
+
|
10
|
+
class ActionMethod < Hash
|
11
|
+
# is_inherited - action is inherited from a parent controller
|
12
|
+
# from_gem - action is implemented in a gem
|
13
|
+
# from_module - action implementation is in a module
|
14
|
+
%i(
|
15
|
+
controller_name
|
16
|
+
action_name
|
17
|
+
route_missing
|
18
|
+
source_location
|
19
|
+
is_inherited
|
20
|
+
from_gem
|
21
|
+
from_module
|
22
|
+
owner
|
23
|
+
).each do |name|
|
24
|
+
define_method(name) { self[name] }
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(opts = {})
|
28
|
+
update(opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def controller_class
|
32
|
+
@controller_class ||= controller_name.constantize
|
33
|
+
end
|
34
|
+
|
35
|
+
def needs_reporting?(report_duplicates:, report_gems:, report_modules:, report_routed:, **)
|
36
|
+
(route_missing? || report_routed) \
|
37
|
+
&& (!inherited? || report_duplicates) \
|
38
|
+
&& (!from_gem? || report_gems) \
|
39
|
+
&& (!from_module? || report_modules)
|
40
|
+
end
|
41
|
+
|
42
|
+
def pretty(max_action_length: MAX_ACTION_LENGTH, metadata: false, **)
|
43
|
+
format("%-#{max_action_length}s @ %s", action_name, source_location).tap do |result|
|
44
|
+
if metadata
|
45
|
+
result << " "
|
46
|
+
result << pretty_metadata
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def pretty_metadata
|
52
|
+
[
|
53
|
+
route_missing? ? "no-route" : nil,
|
54
|
+
inherited? ? "inherited:#{owner.name}" : nil,
|
55
|
+
from_gem? ? "gem:#{from_gem}" : nil,
|
56
|
+
from_module? ? "module:#{owner.name}" : nil,
|
57
|
+
].compact.join(' ')
|
58
|
+
end
|
59
|
+
|
60
|
+
alias inherited? is_inherited
|
61
|
+
alias route_missing? route_missing
|
62
|
+
alias from_gem? from_gem
|
63
|
+
alias from_module? from_module
|
64
|
+
end
|
65
|
+
|
66
|
+
class ActionAnalysis
|
67
|
+
|
68
|
+
attr_reader :all_action_methods, :unused_controllers, :options
|
69
|
+
|
70
|
+
# Options:
|
71
|
+
# report_duplicates - report actions which the parent controller also has
|
72
|
+
# report_gems - report actions which are implemented by a gem
|
73
|
+
# report_modules - report actions inherited from modules
|
74
|
+
# report_routed - report all actions including those with a route
|
75
|
+
# full_path - skips shortening file paths
|
76
|
+
# metadata - include discovered metadata about actions
|
77
|
+
def initialize(route_analysis: RailsRoutesAnalyzer::RouteAnalysis.new,
|
78
|
+
report_routed: false,
|
79
|
+
report_duplicates: false,
|
80
|
+
report_gems: false,
|
81
|
+
report_modules: false,
|
82
|
+
full_path: false,
|
83
|
+
metadata: false)
|
84
|
+
|
85
|
+
@options = {
|
86
|
+
report_routed: report_routed,
|
87
|
+
report_duplicates: report_duplicates,
|
88
|
+
report_gems: report_gems,
|
89
|
+
report_modules: report_modules,
|
90
|
+
full_path: full_path,
|
91
|
+
metadata: metadata,
|
92
|
+
}
|
93
|
+
|
94
|
+
@all_action_methods = analyze_action_methods(route_analysis: route_analysis)
|
95
|
+
|
96
|
+
@by_controller = @all_action_methods.group_by(&:controller_class)
|
97
|
+
@unused_controllers = find_unused_controllers
|
98
|
+
end
|
99
|
+
|
100
|
+
def print_report
|
101
|
+
print_missing_routes_report_preamble unless options[:report_routed]
|
102
|
+
print_actions_report
|
103
|
+
end
|
104
|
+
|
105
|
+
def print_actions_report
|
106
|
+
@controller_reporting_cache = {}
|
107
|
+
|
108
|
+
report_actions_recursive
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def unused_actions_present?
|
114
|
+
@all_action_methods.any? do |action|
|
115
|
+
action.needs_reporting?(**options)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def actions_for(controller)
|
120
|
+
@by_controller[controller] || []
|
121
|
+
end
|
122
|
+
|
123
|
+
def actions_to_report(controller)
|
124
|
+
actions_for(controller).select do |action|
|
125
|
+
action.needs_reporting?(**options)
|
126
|
+
end.sort_by(&:action_name)
|
127
|
+
end
|
128
|
+
|
129
|
+
def report_actions_recursive(controllers = ActionController::Base.subclasses, level = 0)
|
130
|
+
controllers.each do |controller|
|
131
|
+
next unless controller_needs_reporting?(controller)
|
132
|
+
|
133
|
+
puts "#{' ' * level}#{controller.name}"
|
134
|
+
|
135
|
+
if (actions = actions_to_report(controller)).any?
|
136
|
+
action_level = level + 1
|
137
|
+
|
138
|
+
if controller.subclasses.any? { |subclass| controller_needs_reporting?(subclass) }
|
139
|
+
puts "#{' ' * action_level}Actions:"
|
140
|
+
action_level += 1
|
141
|
+
end
|
142
|
+
|
143
|
+
max_action_length = [MAX_ACTION_LENGTH, actions.map { |a| a.action_name.size }.max].min
|
144
|
+
|
145
|
+
actions.each do |action|
|
146
|
+
puts "#{' ' * action_level}#{action.pretty(max_action_length: max_action_length, **options)}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
report_actions_recursive(controller.subclasses.sort_by(&:name), level + 1)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def controller_likely_from_gem?(controller)
|
155
|
+
actions_for(controller).all? { |action| action.from_gem? || action.from_module? || action.inherited? }
|
156
|
+
end
|
157
|
+
|
158
|
+
def controller_needs_reporting?(controller)
|
159
|
+
if @controller_reporting_cache.key?(controller)
|
160
|
+
return @controller_reporting_cache[controller]
|
161
|
+
end
|
162
|
+
|
163
|
+
@controller_reporting_cache[controller] = \
|
164
|
+
actions_to_report(controller).any? \
|
165
|
+
|| controller.subclasses.any? { |subclass| controller_needs_reporting?(subclass) }
|
166
|
+
end
|
167
|
+
|
168
|
+
def find_unused_controllers
|
169
|
+
ActionController::Base.descendants.select do |controller|
|
170
|
+
controller_has_no_routes?(controller) \
|
171
|
+
&& !default_controller?(controller) \
|
172
|
+
&& (options[:report_gems] || !controller_likely_from_gem?(controller))
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def default_controller?(controller)
|
177
|
+
defined?(::Rails::ApplicationController) && controller <= ::Rails::ApplicationController
|
178
|
+
end
|
179
|
+
|
180
|
+
def no_direct_routes_to?(controller)
|
181
|
+
actions_for(controller).all?(&:route_missing?)
|
182
|
+
end
|
183
|
+
|
184
|
+
def controller_has_no_routes?(controller)
|
185
|
+
no_direct_routes_to?(controller) \
|
186
|
+
&& controller.subclasses.all? { |c| controller_has_no_routes?(c) }
|
187
|
+
end
|
188
|
+
|
189
|
+
def analyze_action_methods(route_analysis:)
|
190
|
+
implemented_routes = Set.new(route_analysis.implemented_routes)
|
191
|
+
|
192
|
+
[].tap do |result|
|
193
|
+
ActionController::Base.descendants.each do |controller|
|
194
|
+
next if default_controller?(controller)
|
195
|
+
|
196
|
+
action_methods = controller.action_methods.to_a.map(&:to_sym)
|
197
|
+
|
198
|
+
if (parent_controller = controller.superclass) == ActionController::Base
|
199
|
+
parent_controller = nil
|
200
|
+
parent_actions = []
|
201
|
+
else
|
202
|
+
parent_actions = parent_controller.action_methods.to_a.map(&:to_sym)
|
203
|
+
end
|
204
|
+
|
205
|
+
action_methods.each do |action_name|
|
206
|
+
controller_name = controller.name
|
207
|
+
source_location = get_source_location(controller, action_name)
|
208
|
+
|
209
|
+
owner = controller.instance_method(action_name).owner
|
210
|
+
|
211
|
+
# A very strong likelyhood is that if the method is not defined by a module
|
212
|
+
# it comes directly from any ActionController::Base subclass.
|
213
|
+
#
|
214
|
+
# This knowledge helps us ignore methods which might come for example from
|
215
|
+
# someone (possibly unwisely) including a helper directly into a controller.
|
216
|
+
from_module = owner.class == Module
|
217
|
+
|
218
|
+
is_inherited = parent_actions.include?(action_name) \
|
219
|
+
&& get_source_location(parent_controller, action_name) == source_location
|
220
|
+
|
221
|
+
route_missing = !implemented_routes.include?([controller_name, action_name])
|
222
|
+
|
223
|
+
sanitized_location = RailsRoutesAnalyzer.sanitize_source_location(source_location, options.slice(:full_path))
|
224
|
+
|
225
|
+
result << ActionMethod.new(
|
226
|
+
controller_name: controller_name,
|
227
|
+
action_name: action_name,
|
228
|
+
is_inherited: is_inherited,
|
229
|
+
route_missing: route_missing,
|
230
|
+
source_location: sanitized_location,
|
231
|
+
from_gem: GemManager.identify_gem(source_location),
|
232
|
+
owner: owner,
|
233
|
+
from_module: from_module,
|
234
|
+
)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_source_location(controller_class_or_name, action_name)
|
241
|
+
controller = controller_class_or_name
|
242
|
+
controller = controller.constantize if controller.is_a?(String)
|
243
|
+
|
244
|
+
controller.instance_method(action_name).source_location.join(':')
|
245
|
+
end
|
246
|
+
|
247
|
+
PREAMBLE_WARNING = <<-EOS.strip_heredoc.freeze
|
248
|
+
NOTE Some gems, such as Devise, are expected to provide actions that have no matching
|
249
|
+
routes in case a particular feature is not enabled, this is normal and expected.
|
250
|
+
|
251
|
+
If any non-action methods are reported please consider making these
|
252
|
+
private/protected or customize #action_methods to exclude them.
|
253
|
+
|
254
|
+
Actions without a route:
|
255
|
+
|
256
|
+
EOS
|
257
|
+
|
258
|
+
def print_missing_routes_report_preamble
|
259
|
+
unless unused_actions_present?
|
260
|
+
puts "There are no actions without a route"
|
261
|
+
return
|
262
|
+
end
|
263
|
+
|
264
|
+
if unused_controllers.present?
|
265
|
+
puts "Controllers with no routes pointing to them:"
|
266
|
+
unused_controllers.sort_by(&:name).each do |controller|
|
267
|
+
puts " #{controller.name}"
|
268
|
+
end
|
269
|
+
puts ""
|
270
|
+
end
|
271
|
+
|
272
|
+
puts PREAMBLE_WARNING
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
276
|
+
end
|