class RailsRoutesAnalyzer::RouteAnalysis
Attributes
app[RW]
only_except[RW]
only_only[RW]
route_calls[RW]
route_lines[RW]
route_log[RW]
verbose[RW]
Public Class Methods
new(app: Rails.application, verbose: false, only_only: false, only_except: false)
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 14 def initialize(app: Rails.application, verbose: false, only_only: false, only_except: false) self.app = app self.verbose = verbose self.only_only = only_only self.only_except = only_except analyze! end
Public Instance Methods
all_unique_issues_file_names()
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 134 def all_unique_issues_file_names issues.map(&:full_filename).uniq.sort end
analyse_route_call(**kwargs)
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 68 def analyse_route_call(**kwargs) controller_class_name = "#{kwargs[:controller_name]}_controller".camelize opts = kwargs.merge(controller_class_name: controller_class_name) route_call = RouteCall.new(opts) route_calls << route_call controller = nil begin controller = Object.const_get(controller_class_name) rescue LoadError, RuntimeError, NameError => e route_call.add_issue RouteIssue::NoController.new(error: e.message) return end if controller.nil? route_call.add_issue RouteIssue::NoController.new(error: "#{controller_class_name} is nil") return end analyze_action_availability(controller, route_call, **opts) end
analyze!()
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 39 def analyze! clear_data prepare_for_analysis RouteInterceptor.route_data.each do |(file_location, route_creation_method, controller_name), action_names| analyse_route_call( file_location: file_location, route_creation_method: route_creation_method, controller_name: controller_name, action_names: action_names.uniq.sort, ) end route_log.concat RouteInterceptor.route_log generate_route_lines end
analyze_action_availability(controller, route_call, **opts)
click to toggle source
Checks which if any actions referred to by the route don't exist.
# File lib/rails_routes_analyzer/route_analysis.rb, line 93 def analyze_action_availability(controller, route_call, **opts) present, missing = opts[:action_names].partition { |name| controller.action_methods.include?(name.to_s) } route_call[:present_actions] = present if present.any? if SINGLE_METHODS.include?(opts[:route_creation_method]) # NOTE a single call like 'get' can add multiple actions if called in a loop if missing.present? route_call.add_issue RouteIssue::NoAction.new(missing_actions: missing) end return end return if missing.empty? # Everything is perfect, all routes match an action if present.sort == RESOURCE_ACTIONS.sort # Should happen only if RESOURCE_ACTIONS doesn't match which actions rails supports raise "shouldn't get all methods being present and yet some missing at the same time: #{present.inspect} #{missing.inspect}" end suggested_param = resource_route_suggested_param(present) route_call.add_issue RouteIssue::Resources.new(suggested_param: suggested_param) end
clear_data()
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 23 def clear_data self.route_lines = [] self.route_calls = [] self.route_log = [] end
generate_route_lines()
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 56 def generate_route_lines calls_per_line = route_calls.group_by do |record| [record.full_filename, record.line_number] end calls_per_line.each do |(full_filename, line_number), records| route_lines << RouteLine.new(full_filename: full_filename, line_number: line_number, records: records) end end
implemented_routes()
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 142 def implemented_routes Set.new.tap do |implemented_routes| route_calls.each do |route_call| (route_call.present_actions || []).each do |action| implemented_routes << [route_call.controller_class_name, action] end end end end
issues()
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 126 def issues route_calls.select(&:issue?) end
non_issues()
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 130 def non_issues route_calls.reject(&:issue?) end
prepare_for_analysis()
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 29 def prepare_for_analysis app.eager_load! # all controller classes need to be loaded ::ActionDispatch::Routing::Mapper::Mapping.prepend RouteInterceptor RouteInterceptor.route_log.clear app.reload_routes! end
print_report()
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 156 def print_report if issues.empty? puts "No route issues found" return end issues.each do |issue| puts issue.human_readable_error(verbose: verbose) end end
resource_route_suggested_param(present)
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 118 def resource_route_suggested_param(present) if (present.size < 4 || only_only) && !only_except "only: [#{present.sort.map { |x| ":#{x}" }.join(', ')}]" else "except: [#{(RESOURCE_ACTIONS - present).sort.map { |x| ":#{x}" }.join(', ')}]" end end
route_calls_for_file_name(full_filename)
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 138 def route_calls_for_file_name(full_filename) route_calls.select { |record| record.full_filename == full_filename.to_s } end
route_lines_for_file(full_filename)
click to toggle source
# File lib/rails_routes_analyzer/route_analysis.rb, line 152 def route_lines_for_file(full_filename) route_lines.select { |line| line.full_filename == full_filename.to_s } end