Module: RoutesCoverage

Defined in:
lib/routes_coverage.rb,
lib/routes_coverage/result.rb,
lib/routes_coverage/version.rb,
lib/routes_coverage/middleware.rb,
lib/routes_coverage/adapters/rspec.rb,
lib/routes_coverage/adapters/atexit.rb,
lib/routes_coverage/formatters/base.rb,
lib/routes_coverage/formatters/html.rb,
lib/routes_coverage/adapters/simplecov.rb,
lib/routes_coverage/formatters/full_text.rb,
lib/routes_coverage/formatters/summary_text.rb

Defined Under Namespace

Modules: ActionControllerTestCase, ActionControllerTestCaseKvargs, Adapters, Formatters Classes: Middleware, Railtie, Result, Settings

Constant Summary collapse

VERSION =
"0.6.0"

Class Method Summary collapse

Class Method Details

._collect_all_routesObject



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/routes_coverage.rb', line 127

def self._collect_all_routes
  all_routes = ::Rails.application.routes.routes.routes.dup

  if defined?(::Sprockets) && defined?(::Sprockets::Environment)
    all_routes.reject! { |r| r.app.is_a?(::Sprockets::Environment) }
  end

  if settings.exclude_put_fallbacks
    all_routes.reject! do |put_route|
      (
        put_route.verb == /^PUT$/ ||
        put_route.verb == "PUT" # rails 5
      ) &&
        put_route.name.nil? &&
        route_hit_count[put_route].zero? &&
        all_routes.any? do |patch_route|
          (
            patch_route.verb == /^PATCH$/ ||
            patch_route.verb == "PATCH" # rails5
          ) &&
            patch_route.defaults == put_route.defaults &&
            patch_route.ip == put_route.ip &&
            patch_route.path.spec.to_s == put_route.path.spec.to_s
        end
    end
  end
  all_routes
end

._collect_route_groups(all_routes) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/routes_coverage.rb', line 156

def self._collect_route_groups(all_routes)
  settings.groups.map do |group_name, matcher|
    group_routes = all_routes.select do |route|
      if matcher.respond_to?(:call)
        matcher.call(route)
      elsif matcher.is_a?(Hash)
        matcher.all? do |key, value|
          case key
          when :path
            route.path.spec.to_s =~ value
          when :action
            route.requirements[:action]&.match(value)
          when :controller
            route.requirements[:controller]&.match(value)
          when :constraints
            value.all? do |constraint_name, constraint_value|
              if constraint_value.present?
                route.constraints[constraint_name] && route.constraints[constraint_name].match?(constraint_value)
              else
                route.constraints[constraint_name].blank?
              end
            end
          end
        end
      else
        route.path.spec.to_s.match?(matcher)
      end
    end

    [group_name, Result.new(group_routes, route_hit_count.slice(*group_routes), settings)]
  end.to_h
end

._touch_request(req) ⇒ Object

NB: router changes env/request during recognition



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/routes_coverage.rb', line 190

def self._touch_request(req)
  ::Rails.application.routes.router.recognize(req) do |route, parameters5, parameters4|
    parameters = parameters5 || parameters4
    dispatcher = route.app
    if dispatcher.respond_to?(:dispatcher?)
      req.path_parameters = parameters
      dispatcher = nil unless dispatcher.matches?(req) # && dispatcher.dispatcher?
    else # rails < 4.2
      dispatcher = route.app
      req.env['action_dispatch.request.path_parameters'] =
        (env['action_dispatch.request.path_parameters'] || {}).merge(parameters)
      while dispatcher.is_a?(ActionDispatch::Routing::Mapper::Constraints)
        dispatcher = (dispatcher.app if dispatcher.matches?(env))
      end
    end
    next unless dispatcher

    RoutesCoverage._touch_route(route)
    # there may be multiple matching routes - we should match only first
    break
  end
end

._touch_route(route) ⇒ Object



213
214
215
216
# File 'lib/routes_coverage.rb', line 213

def self._touch_route(route)
  reset! unless route_hit_count
  route_hit_count[route] += 1
end

.configure {|settings| ... } ⇒ Object

Yields:



86
87
88
# File 'lib/routes_coverage.rb', line 86

def self.configure
  yield settings
end

.enabled?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/routes_coverage.rb', line 78

def self.enabled?
  ::Rails.env.test?
end

.perform_reportObject



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/routes_coverage.rb', line 104

def self.perform_report
  return unless settings.perform_report

  all_routes = _collect_all_routes
  all_result = Result.new(all_routes, route_hit_count, settings)
  groups = _collect_route_groups(all_routes)

  if groups.size > 1
    ungroupped_routes = all_routes.reject do |r|
      groups.values.any? do |group_routes|
        group_routes.all_routes.include? r
      end
    end

    if ungroupped_routes.any?
      groups["Ungroupped"] = Result.new(ungroupped_routes, route_hit_count.slice(*ungroupped_routes), settings)
    end
  end

  puts
  puts settings.formatter_class.new(all_result, groups, settings).format # rubocop:disable Rails/Output
end

.pidObject

used in at_exit adapter to skip subprocesses



91
92
93
# File 'lib/routes_coverage.rb', line 91

def self.pid
  @pid
end

.reset!Object



99
100
101
102
# File 'lib/routes_coverage.rb', line 99

def self.reset!
  @route_hit_count = Hash.new(0)
  @pid = Process.pid
end

.route_hit_countObject



95
96
97
# File 'lib/routes_coverage.rb', line 95

def self.route_hit_count
  @route_hit_count
end

.settingsObject



82
83
84
# File 'lib/routes_coverage.rb', line 82

def self.settings
  @settings ||= Settings.new
end