Class: RouteGuard::Rules::ShadowedRoutes

Inherits:
Base
  • Object
show all
Defined in:
lib/route_guard/rules/shadowed_routes.rb

Instance Attribute Summary

Attributes inherited from Base

#options

Instance Method Summary collapse

Methods inherited from Base

#expand_path, #initialize, #path_shadows?, #split_segments, #verb_matches?

Constructor Details

This class inherits a constructor from RouteGuard::Rules::Base

Instance Method Details

#analyze(routes, report) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/route_guard/rules/shadowed_routes.rb', line 9

def analyze(routes, report)
  issues = []
  return issues if routes.empty?

  # Cache segment info and path expansions for all routes
  route_info = routes.map do |r|
    segs = split_segments(r.path)
    {
      route: r,
      segs: segs,
      first: segs[1],
      is_catch_all: catch_all_wildcard?(r),
      expansions: (expand_path(r.original_path).map { |p| split_segments(p) } rescue [[r.path]])
    }
  end

  route_info.each_with_index do |info_b, idx|
    next if info_b[:is_catch_all]
    route_b = info_b[:route]
    first_b = info_b[:first]

    route_info[0...idx].each do |info_a|
      next if info_a[:is_catch_all]
      first_a = info_a[:first]

      # Optimization: Skip comparisons if first literal segment doesn't match
      if first_a && first_b && !first_a.start_with?(":") && !first_a.start_with?("*")
        next if first_a != first_b
      end

      route_a = info_a[:route]
      next unless verb_matches?(route_a.verb, route_b.verb)

      if shadows_cached?(info_a, info_b)
        issues << Models::Issue.new(
          rule_name: :shadowed_routes,
          severity: :warning,
          message: "Shadowed Route: #{route_b} is shadowed by #{route_a} defined earlier.",
          route: route_b,
          related_routes: [route_a]
        )
        break
      end
    end
  end

  issues
end