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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 67a79ca7d35119c2df16b62e017573f47d933bd8
4
- data.tar.gz: 38eac12198817bb09b77a7915b744b627fe54ac9
3
+ metadata.gz: 6438d7f464a7b4bfe625d5d7b0c046bb7dec19b6
4
+ data.tar.gz: e85d81221ecd342ec9b4a8ca995b9e9c4955586e
5
5
  SHA512:
6
- metadata.gz: 44460432c3912943b80b26ae2abd1740c7b44b7e9b0d8885d91ad011ec8a02732e945df6b5dc2509d757c4bc5e1223e1703ff4dfff2bcc377db816d2d982f2b9
7
- data.tar.gz: fa4b03e5eea3da9cd729bd9f60349c37cea6e789a9cbffa85d2548c479bab840f829ab905b4b702530555e030d9ab43ef587ae054f66b4d12c0ba2584b43e323
6
+ metadata.gz: d0a0f433be53c34ca4b61fc3167b0913807b1a967096c76dc903c52cb796b91ddb11c5088f27e6f201c81bc27115e972e5b58016304f15e5a36839fbbdf16e35
7
+ data.tar.gz: 8a6d61768c8ee3c80e513e7d05f8b98fed8ee70b3ba3b93ddba5268424bd32fb16f6126ac632db321cbfe664272d2dfb75c82aae619b64dd90de7920fdf10008
data/.autotest CHANGED
@@ -3,4 +3,7 @@ require "autotest/suffix"
3
3
 
4
4
  Autotest.add_hook :initialize do |at|
5
5
  at.testlib = "minitest/autorun"
6
+
7
+ at.add_exception 'test/dummy/tmp'
8
+ at.add_exception 'test/dummy/routes_bad-for-git-test.rb'
6
9
  end
data/.gitignore CHANGED
@@ -9,4 +9,6 @@
9
9
  /spec/reports/
10
10
  /tmp/
11
11
  /test/dummy/test.log
12
+ /test/dummy/tmp
12
13
  /.byebug_history
14
+ /coverage
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
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016 Bear Metal OÜ
3
+ Copyright (c) 2016 Tarmo Tänav, Bear Metal OÜ
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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 also able to provide suggestions on how to change routes.rb to avoid defining dead routes and can generate an annotated version of a route file with suggestions for each line added as comments.
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:annotate_dead [ANNOTATE=path/to/routes.rb]
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:annotate_dead > config/routest.rb.new
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
- Will output an annotated version of config/routes.rb or any other routes file as provided in the ANNOTATE parameter.
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
- * ONLY\_ONLY=1 - suggestions for resource routes will only generate "only:" regardless of how many elements are listed.
44
- * ONLY\_EXCEPT=1 - suggestions for resource routes will only generate "except:" regardless of how many elements are listed.
45
- * VERBOSE=1 - more verbosity, currently this means listing which non-existing actions a given call provides routes for.
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 routes:missing
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
- #### Additional options:
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
- Lists all action methods for all controllers which have no route pointing to them. Uses the (maybe not so well known) ActionController#Base.action\_methods method which usually returns a list of all public methods of the controller class excluding any special Rails provided methods. To make the output of ActionController#Base.action\_methods it would be ideal to try to make all application-provided controller methods non-public if they are not meant to be callable as an action. Alternatively it's also possible (but less desirable) to override the action\_methods call in any controller class to explicitly remove mis-characterised methods.
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
- * STRICT=1 - causes controller base class provided public methods to be considered as actions for a subclass controller and thus reported as errors if they lack routes. Enabling this can generate a lot of noise for applications that have public non-actions in a controller base class.
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](http://bearmetal.eu),
data/Rakefile CHANGED
@@ -7,4 +7,4 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.test_files = FileList['test/**/*_test.rb']
8
8
  end
9
9
 
10
- task :default => :test
10
+ task default: :test
@@ -1,7 +1,8 @@
1
- require "rails_routes_analyzer/version"
2
- require "rails_routes_analyzer/rails_routes_analyzer"
3
- require "rails_routes_analyzer/route_interceptor"
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