lib/coverband/collectors/route_tracker.rb
# frozen_string_literal: true
require "set"
require "singleton"
module Coverband
module Collectors
###
# This class tracks route usage via ActiveSupport::Notifications
###
class RouteTracker < AbstractTracker
REPORT_ROUTE = "routes_tracker"
TITLE = "Routes"
def initialize(options = {})
if Rails&.respond_to?(:version) && Gem::Version.new(Rails.version) >= Gem::Version.new("6.0.0") && Gem::Version.new(Rails.version) < Gem::Version.new("7.1.0")
require_relative "../utils/rails6_ext"
end
super
end
###
# This method is called on every routing call, so we try to reduce method calls
# and ensure high performance
###
def track_key(payload)
route = if payload.key?(:location)
# For redirect.action_dispatch
return unless Coverband.configuration.track_redirect_routes
{
controller: nil,
action: nil,
url_path: payload[:request].path,
verb: payload[:request].method
}
else
# For start_processing.action_controller
{
controller: payload[:params]["controller"],
action: payload[:action],
url_path: nil,
verb: payload[:method]
}
end
if newly_seen_key?(route)
@logged_keys << route
@keys_to_record << route if track_key?(route)
end
end
def self.supported_version?
defined?(Rails) && defined?(Rails::VERSION) && Rails::VERSION::STRING.split(".").first.to_i >= 6
end
def unused_keys(used_keys = nil)
recently_used_routes = (used_keys || self.used_keys).keys
# NOTE: we match with or without path to handle paths with named params like `/user/:user_id` to used routes filling with all the variable named paths
all_keys.reject { |r| recently_used_routes.include?(r.to_s) || recently_used_routes.include?(r.merge(url_path: nil).to_s) }
end
def railtie!
ActiveSupport::Notifications.subscribe("start_processing.action_controller") do |name, start, finish, id, payload|
Coverband.configuration.route_tracker.track_key(payload)
end
# NOTE: This event was instrumented in Aug 10th 2022, but didn't make the 7.0.4 release and should be in the next release
# https://github.com/rails/rails/pull/43755
# Automatic tracking of redirects isn't available before Rails 7.1.0 (currently tested against the 7.1.0.alpha)
# We could consider back porting or patching a solution that works on previous Rails versions
ActiveSupport::Notifications.subscribe("redirect.action_dispatch") do |name, start, finish, id, payload|
Coverband.configuration.route_tracker.track_key(payload)
end
end
private
def concrete_target
if defined?(Rails.application)
if Rails.application.respond_to?(:reload_routes!) && Rails.application.routes.empty?
# NOTE: depending on eager loading etc, routes may not be loaded
# so load them if they aren't
Rails.application.reload_routes!
end
Rails.application.routes.routes.map do |route|
{
controller: route.defaults[:controller],
action: route.defaults[:action],
url_path: route.path.spec.to_s.gsub("(.:format)", ""),
verb: route.verb
}
end
else
[]
end
end
end
end
end