routes_coverage 0.4.3 → 0.5.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
- SHA1:
3
- metadata.gz: 66fbe57b380af966b736a53bfeeccf4c27600825
4
- data.tar.gz: aebe5c80b8aac0abdcc4f13f8cade0ea801d4b56
2
+ SHA256:
3
+ metadata.gz: 576b62caebf21a9e1448f549a213eaf5770ca8486c1ad94183c1bd8617bb9916
4
+ data.tar.gz: 9a150fe8e215e92ba00dee273700ebe69f08baca5606cc7980b09fcc854c0969
5
5
  SHA512:
6
- metadata.gz: 8a64c5c7fc2014e3f6dc6cb530c9aaae53140c6eadcdf8df96ad82d77f6acd7ec94f4a40d815177e503380aa306f5ae4bda1e7b2812850684bfe3c7c36f0cc05
7
- data.tar.gz: f5b7376fed952b708fca9747b2e2c0b13a065bd86e8185882b3845dbb27750fbded954007ea5541f9123dc07c04992febeb139343d0a2ed82c58745d3cdd726e
6
+ metadata.gz: 1695cd9694f9388c438814a990127af84930e39413694d15fec8a35bb5f12b1217c1c4a4e8d9fd3b1289a0f91543cc11d772142354481907508337c736f2217e
7
+ data.tar.gz: 4197958e82df3eceee17d222f441cbf7106d4de67d5fafb3bcdb4d16b0e59159c4678f2420c679483379aeb28a3304b246b77a8215026034aca936af7b4cb797
data/README.md CHANGED
@@ -41,6 +41,7 @@ RSpec.configure do |config|
41
41
  config.routes_coverage.exclude_namespaces << 'somenamespace' # excludes /somenamespace/*
42
42
 
43
43
  config.routes_coverage.groups["Some Route group title"] = %r{^/somespace/}
44
+ config.routes_coverage.groups["Subdomain"] = { constraints: { subdomain: 'some_subdomain' }, path: '/' }
44
45
  config.routes_coverage.groups["Admin"] = Regexp.union([
45
46
  %r{^/admin/},
46
47
  %r{^/secret_place/},
@@ -1,14 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RoutesCoverage
2
4
  module Adapters
3
5
  class AtExit
4
- def self.use coverer=nil
5
- #NB: at_exit order is important, for example minitest uses it to run, need to install our handler before it
6
+ def self.use(_coverer = nil)
7
+ # NB: at_exit order is important, for example minitest uses it to run, need to install our handler before it
6
8
 
7
9
  RoutesCoverage.reset!
8
10
  at_exit do
9
11
  next if RoutesCoverage.pid != Process.pid
12
+
10
13
  RoutesCoverage.perform_report
11
- exit
14
+ exit # rubocop:disable Rails/Exit
12
15
  end
13
16
  end
14
17
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rspec/core'
2
4
 
3
5
  RSpec.configure do |config|
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RoutesCoverage
2
4
  module Adapters
3
5
  class SimpleCov
4
6
  def self.use
5
7
  RoutesCoverage.reset!
6
8
  prev_block = ::SimpleCov.at_exit
7
- ::SimpleCov.at_exit{
9
+ ::SimpleCov.at_exit do
8
10
  RoutesCoverage.perform_report
9
11
  prev_block.call
10
- }
12
+ end
11
13
  end
12
14
  end
13
15
  end
@@ -1,15 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RoutesCoverage
2
4
  module Formatters
3
5
  class Base
4
- def initialize result, groups, settings
6
+ def initialize(result, groups, settings)
5
7
  @result = result
6
8
  @groups = groups
7
9
  @settings = settings
8
10
  end
9
11
 
10
- attr_reader :result
11
- attr_reader :groups
12
- attr_reader :settings
12
+ attr_reader :result, :groups, :settings
13
13
  end
14
14
  end
15
15
  end
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RoutesCoverage
2
4
  module Formatters
3
5
  class FullText < SummaryText
4
-
5
6
  class RouteFormatter
6
7
  attr_reader :buffer
7
8
 
8
- def initialize result=nil, _settings=nil, output_hits=false
9
+ def initialize(result = nil, _settings = nil, output_hits: false)
9
10
  @buffer = []
10
11
  @result = result
11
12
  @output_hits = output_hits
@@ -28,49 +29,47 @@ module RoutesCoverage
28
29
  @buffer << draw_header(routes)
29
30
  end
30
31
 
31
- def no_routes _routes_from_rails5=nil
32
+ def no_routes(_routes_from_rails5 = nil)
32
33
  @buffer << "\tNone"
33
34
  end
34
35
 
35
36
  private
36
- HEADER = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action']
37
+
38
+ HEADER = ['Prefix', 'Verb', 'URI Pattern', 'Controller#Action'].freeze
37
39
  def draw_section(routes)
38
40
  header_lengths = HEADER.map(&:length)
39
41
  name_width, verb_width, path_width, reqs_width = widths(routes).zip(header_lengths).map(&:max)
40
42
 
41
- hits = nil
42
43
  routes.map do |r|
43
- # puts "route is #{r.inspect}"
44
- if @output_hits
45
- # hits = " ?"
46
- if r[:original].respond_to?(:__getobj__)
47
- original_route = r[:original].__getobj__ # SimpleDelegator
48
- else
49
- original_route = r[:original]
50
- end
51
- hits = " #{@result.route_hit_counts[original_route]}"
52
- end
53
- "#{r[:name].rjust(name_width) if @output_prefix} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs].ljust(reqs_width)}#{hits}"
44
+ # unwrap SimpleDelegator
45
+ original_route = r[:original].respond_to?(:__getobj__) ? r[:original].__getobj__ : r[:original]
46
+
47
+ "#{r[:name].rjust(name_width) if @output_prefix} "\
48
+ "#{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs].ljust(reqs_width)}"\
49
+ "#{" #{@result.route_hit_counts[original_route]}" if @output_hits}"
54
50
  end
55
51
  end
56
52
 
57
53
  def draw_header(routes)
58
54
  name_width, verb_width, path_width, reqs_width = widths(routes)
59
55
 
60
- "#{"Prefix".rjust(name_width) if @output_prefix} #{"Verb".ljust(verb_width)} #{"URI Pattern".ljust(path_width)} #{"Controller#Action".ljust(reqs_width)}#{' Hits' if @output_hits}"
56
+ [
57
+ ('Prefix'.rjust(name_width) if @output_prefix),
58
+ 'Verb'.ljust(verb_width),
59
+ 'URI Pattern'.ljust(path_width),
60
+ 'Controller#Action'.ljust(reqs_width),
61
+ ('Hits' if @output_hits)
62
+ ].compact.join(' ')
61
63
  end
62
64
 
63
65
  def widths(routes)
64
- [routes.map { |r| r[:name].length }.max || 0,
65
- routes.map { |r| r[:verb].length }.max || 0,
66
- routes.map { |r| r[:path].length }.max || 0,
67
- routes.map { |r| r[:reqs].length }.max || 0,
68
- ]
66
+ %i[name verb path reqs].map do |key|
67
+ routes.map { |r| r[key].length }.max.to_i
68
+ end
69
69
  end
70
70
  end
71
71
 
72
-
73
- def routes_section formatter, title, routes
72
+ def routes_section(formatter, title, routes)
74
73
  formatter.buffer << title
75
74
 
76
75
  if routes.none?
@@ -83,24 +82,27 @@ module RoutesCoverage
83
82
  formatter.result
84
83
  end
85
84
 
86
- def hit_routes
87
- routes = result.hit_routes
85
+ def collect_routes(routes)
86
+ # rails 3
87
+ return Result::Inspector.new.collect_all_routes(routes) unless Result::Inspector::NEW_RAILS
88
+
89
+ Result::Inspector.new(routes).collect_all_routes
90
+ end
91
+
92
+ def hit_routes_details
88
93
  # engine routes now are in the same list
89
- if Result::Inspector::NEW_RAILS
90
- hit_routes = Result::Inspector.new(result.hit_routes).collect_all_routes
91
- pending_routes = Result::Inspector.new(result.pending_routes).collect_all_routes
92
- else
93
- #rails 3
94
- hit_routes = Result::Inspector.new.collect_all_routes(result.hit_routes)
95
- pending_routes = Result::Inspector.new.collect_all_routes(result.pending_routes)
96
- end
94
+ hit_routes = collect_routes(result.hit_routes)
95
+ pending_routes = collect_routes(result.pending_routes)
96
+
97
+ <<~TXT
98
+ #{routes_section(RouteFormatter.new(result, settings, output_hits: true), 'Covered routes:', hit_routes)}
97
99
 
98
- return routes_section(RouteFormatter.new(result, settings, true), "Covered routes:", hit_routes) + "\n\n" +
99
- routes_section(RouteFormatter.new(result, settings), "Pending routes:", pending_routes)
100
+ #{routes_section(RouteFormatter.new(result, settings), 'Pending routes:', pending_routes)}
101
+ TXT
100
102
  end
101
103
 
102
104
  def format
103
- "#{super}\n\n#{hit_routes}"
105
+ "#{super}\n\n#{hit_routes_details}"
104
106
  end
105
107
  end
106
108
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "erb"
2
4
  require "cgi"
3
5
  require "fileutils"
@@ -23,24 +25,25 @@ module RoutesCoverage
23
25
  ERB.new(File.read(File.join(File.dirname(__FILE__), "html_views", "#{name}.erb")))
24
26
  end
25
27
 
26
-
27
28
  def root(root = nil)
28
- #TODO: config for this
29
+ # TODO: config for this
29
30
  return SimpleCov.root if defined? SimpleCov
30
31
  return @root if defined?(@root) && root.nil?
32
+
31
33
  @root = File.expand_path(root || Dir.getwd)
32
34
  end
33
35
 
34
36
  def coverage_dir(dir = nil)
35
- #TODO: config for this
37
+ # TODO: config for this
36
38
  return SimpleCov.coverage_dir if defined? SimpleCov
37
39
  return @coverage_dir if defined?(@coverage_dir) && dir.nil?
38
- @coverage_path = nil # invalidate cache
40
+
41
+ @output_path = nil # invalidate cache
39
42
  @coverage_dir = (dir || "coverage")
40
43
  end
41
44
 
42
45
  def output_path
43
- @coverage_path ||= File.expand_path(coverage_dir, root).tap do |path|
46
+ @output_path ||= File.expand_path(coverage_dir, root).tap do |path|
44
47
  FileUtils.mkdir_p path
45
48
  end
46
49
  end
@@ -51,8 +54,8 @@ module RoutesCoverage
51
54
  @project_name ||= File.basename(root.split("/").last).capitalize.tr("_", " ")
52
55
  end
53
56
 
54
- def asset_content name
55
- File.read(File.expand_path("../../../compiled_assets/#{name}", File.dirname(__FILE__)))
57
+ def asset_content(name)
58
+ File.read(File.expand_path("../../../compiled_assets/#{name}", __dir__))
56
59
  end
57
60
 
58
61
  def style_asset_link
@@ -83,33 +86,23 @@ module RoutesCoverage
83
86
  end
84
87
  end
85
88
 
86
- def hits_css_class hits
87
- hits > 0 ? 'cov' : 'uncov'
89
+ def hits_css_class(hits)
90
+ hits.positive? ? 'cov' : 'uncov'
88
91
  end
89
92
 
90
93
  def timeago(time)
91
94
  "<abbr class=\"timeago\" title=\"#{time.iso8601}\">#{time.iso8601}</abbr>"
92
95
  end
93
96
 
94
-
95
97
  def all_result_groups
96
- return @all_result_groups if @all_result_groups
97
- @all_result_groups = [
98
- {
99
- id: 'all_routes',
100
- name: 'All Routes',
101
- result: result,
102
- }
103
- ]
104
- @all_result_groups += groups.map do |group_name, group_result|
105
- {
106
- id: group_name.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-\_]/, ""),
107
- name: group_name,
108
- result: group_result,
109
- }
110
- end
111
-
112
- @all_result_groups
98
+ @all_result_groups ||= [{ id: 'all_routes', name: 'All Routes', result: result }] +
99
+ groups.map do |group_name, group_result|
100
+ {
101
+ id: group_name.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-_]/, ""),
102
+ name: group_name,
103
+ result: group_result
104
+ }
105
+ end
113
106
  end
114
107
  end
115
108
  end
@@ -1,15 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RoutesCoverage
2
4
  module Formatters
3
5
  class SummaryText < Base
4
- def hits_count result
5
- "#{result.hit_routes_count} of #{result.expected_routes_count}#{"(#{result.total_count} total)" if result.expected_routes_count != result.total_count} routes hit#{ " at #{result.avg_hits} hits average" if result.hit_routes_count > 0}"
6
+ def hits_count(result)
7
+ "#{result.hit_routes_count} of #{result.expected_routes_count}"\
8
+ "#{"(#{result.total_count} total)" if result.expected_routes_count != result.total_count}"\
9
+ " routes hit#{" at #{result.avg_hits} hits average" if result.hit_routes_count.positive?}"
6
10
  end
7
11
 
8
12
  def status
9
13
  return unless settings.minimum_coverage
10
- unless result.coverage_pass?
11
- "Coverage is too low"
12
- end
14
+
15
+ "Coverage is too low" unless result.coverage_pass?
13
16
  end
14
17
 
15
18
  def format
@@ -18,9 +21,9 @@ module RoutesCoverage
18
21
  ]
19
22
 
20
23
  if groups.any?
21
- buffer += groups.map{|group_name, group_result|
24
+ buffer += groups.map do |group_name, group_result|
22
25
  " #{group_name}: #{group_result.coverage}% (#{hits_count group_result})"
23
- }
26
+ end
24
27
  end
25
28
 
26
29
  buffer << status
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RoutesCoverage
2
4
  class Middleware
3
- def initialize app
5
+ def initialize(app)
4
6
  @app = app
5
7
  end
6
8
 
7
- def call original_env
9
+ def call(original_env)
8
10
  # router changes env/request during recognition so need a copy:
9
11
  env = original_env.dup
10
12
  req = ::Rails.application.routes.request_class.new env
@@ -13,18 +15,13 @@ module RoutesCoverage
13
15
  dispatcher = route.app
14
16
  if dispatcher.respond_to?(:dispatcher?)
15
17
  req.path_parameters = parameters
16
- unless dispatcher.matches?(req) # && dispatcher.dispatcher?
17
- dispatcher = nil
18
- end
18
+ dispatcher = nil unless dispatcher.matches?(req) # && dispatcher.dispatcher?
19
19
  else # rails < 4.2
20
20
  dispatcher = route.app
21
- req.env['action_dispatch.request.path_parameters'] = (env['action_dispatch.request.path_parameters'] || {}).merge(parameters)
22
- while dispatcher.is_a?(ActionDispatch::Routing::Mapper::Constraints) do
23
- if dispatcher.matches?(env)
24
- dispatcher = dispatcher.app
25
- else
26
- dispatcher = nil
27
- end
21
+ req.env['action_dispatch.request.path_parameters'] =
22
+ (env['action_dispatch.request.path_parameters'] || {}).merge(parameters)
23
+ while dispatcher.is_a?(ActionDispatch::Routing::Mapper::Constraints)
24
+ dispatcher = (dispatcher.app if dispatcher.matches?(env))
28
25
  end
29
26
  end
30
27
  next unless dispatcher
@@ -33,7 +30,7 @@ module RoutesCoverage
33
30
  # there may be multiple matching routes - we should match only first
34
31
  break
35
32
  end
36
- #TODO: detect 404s? and maybe other route errors?
33
+ # TODO: detect 404s? and maybe other route errors?
37
34
  @app.call(original_env)
38
35
  end
39
36
  end
@@ -1,19 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/string' # needed for rails5 version of inspector
2
4
 
3
5
  module RoutesCoverage
4
6
  class Result
5
-
6
7
  begin
7
8
  require 'action_dispatch/routing/inspector'
8
9
  class Inspector < ActionDispatch::Routing::RoutesInspector
9
10
  NEW_RAILS = true
10
11
  def collect_all_routes
11
12
  res = collect_routes(@routes)
12
- #TODO: test with engines
13
+ # TODO: test with engines
13
14
  @engines.each do |engine_name, engine_routes|
14
- res += engine_routes.map{|er|
15
+ res += engine_routes.map do |er|
15
16
  er.merge({ engine_name: engine_name })
16
- }
17
+ end
17
18
  end
18
19
  res
19
20
  end
@@ -21,39 +22,35 @@ module RoutesCoverage
21
22
  def collect_routes(routes)
22
23
  routes.collect do |route|
23
24
  ActionDispatch::Routing::RouteWrapper.new(route)
24
- end.reject do |route|
25
- route.internal?
26
- end.collect do |route|
25
+ end.reject(&:internal?).collect do |route| # rubocop:disable Style/MultilineBlockChain
27
26
  collect_engine_routes(route)
28
27
 
29
- { name: route.name,
30
- verb: route.verb,
31
- path: route.path,
32
- reqs: route.reqs,
28
+ { name: route.name,
29
+ verb: route.verb,
30
+ path: route.path,
31
+ reqs: route.reqs,
33
32
  # regexp: route.json_regexp, # removed, this is not present in rails5
34
33
  # added:
35
- original: route,
36
- }
34
+ original: route }
37
35
  end
38
36
  end
39
37
  end
40
-
41
38
  rescue LoadError
42
- #rails 3
39
+ # rails 3
43
40
  require 'rails/application/route_inspector'
44
41
  class Inspector < Rails::Application::RouteInspector
45
42
  NEW_RAILS = false
46
- def collect_all_routes(routes)
43
+ def collect_all_routes(routes) # rubocop:disable Lint/DuplicateMethods
47
44
  res = collect_routes(routes)
48
45
  @engines.each do |engine_name, engine_routes|
49
- res += engine_routes.map{|er|
46
+ res += engine_routes.map do |er|
50
47
  er.merge({ engine_name: engine_name })
51
- }
48
+ end
52
49
  end
53
50
  res
54
51
  end
55
52
 
56
- def collect_routes(routes)
53
+ def collect_routes(routes) # rubocop:disable Lint/DuplicateMethods
57
54
  routes = routes.collect do |route|
58
55
  route_reqs = route.requirements
59
56
 
@@ -70,44 +67,40 @@ module RoutesCoverage
70
67
 
71
68
  collect_engine_routes(reqs, rack_app)
72
69
 
73
- { name: route.name.to_s,
74
- verb: route.verb.source.gsub(/[$^]/, ''),
75
- path: route.path.spec.to_s,
76
- reqs: reqs,
70
+ { name: route.name.to_s,
71
+ verb: route.verb.source.gsub(/[$^]/, ''),
72
+ path: route.path.spec.to_s,
73
+ reqs: reqs,
77
74
  # added:
78
- original: route,
79
- }
75
+ original: route }
80
76
  end
81
77
 
82
78
  # Skip the route if it's internal info route
83
79
  routes.reject { |r| r[:path] =~ %r{/rails/info/properties|^#{Rails.application.config.assets.prefix}} }
84
80
  end
85
81
  end
86
-
87
82
  end
88
83
 
89
- def initialize all_routes, hit_routes, settings
84
+ def initialize(all_routes, hit_routes, settings)
90
85
  @all_routes = all_routes
91
86
  @route_hit_counts = hit_routes
92
87
  @settings = settings
93
88
  end
94
89
 
95
- attr_reader :all_routes
96
-
97
- attr_reader :route_hit_counts
90
+ attr_reader :all_routes, :route_hit_counts
98
91
 
99
92
  def expected_routes
100
93
  return @expected_routes if @expected_routes
101
94
 
102
95
  filter_regex = Regexp.union(@settings.exclude_patterns)
103
- namespaces_regex = Regexp.union(@settings.exclude_namespaces.map{|n| /^\/#{n}/})
96
+ namespaces_regex = Regexp.union(@settings.exclude_namespaces.map { |n| %r{^/#{n}} })
104
97
 
105
- routes_groups = all_routes.group_by{|r|
106
- !!(
98
+ routes_groups = all_routes.group_by do |r|
99
+ (
107
100
  ("#{r.verb.to_s[8..-3]} #{r.path.spec}".strip =~ filter_regex) ||
108
101
  (r.path.spec.to_s =~ namespaces_regex)
109
- )
110
- }
102
+ ).present?
103
+ end
111
104
 
112
105
  @excluded_routes = routes_groups[true] || []
113
106
  @expected_routes = routes_groups[false] || []
@@ -123,11 +116,10 @@ module RoutesCoverage
123
116
  end
124
117
 
125
118
  def hit_routes
126
- #TODO: sort?
119
+ # TODO: sort?
127
120
  @route_hit_counts.keys
128
121
  end
129
122
 
130
-
131
123
  def hit_routes_count
132
124
  @route_hit_counts.size
133
125
  end
@@ -146,6 +138,7 @@ module RoutesCoverage
146
138
 
147
139
  def coverage
148
140
  return 0 unless expected_routes.any?
141
+
149
142
  (hit_routes_count * 100.0 / expected_routes_count).round(@settings.round_precision)
150
143
  end
151
144
 
@@ -158,16 +151,15 @@ module RoutesCoverage
158
151
  end
159
152
 
160
153
  def all_routes_with_hits
161
- if Inspector::NEW_RAILS
162
- res = Inspector.new(all_routes).collect_all_routes
163
- else
164
- res = Inspector.new.collect_all_routes(all_routes)
165
- end
166
- res.each{|val|
154
+ res = if Inspector::NEW_RAILS
155
+ Inspector.new(all_routes).collect_all_routes
156
+ else
157
+ Inspector.new.collect_all_routes(all_routes)
158
+ end
159
+ res.each do |val|
167
160
  val[:hits] = @route_hit_counts[val[:original]] || 0
168
- }
161
+ end
169
162
  res
170
163
  end
171
-
172
164
  end
173
165
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RoutesCoverage
2
- VERSION = "0.4.3"
4
+ VERSION = "0.5.0"
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "routes_coverage/version"
2
4
  require "routes_coverage/result"
3
5
  require "routes_coverage/middleware"
@@ -12,24 +14,13 @@ module RoutesCoverage
12
14
  railtie_name :routes_coverage
13
15
 
14
16
  initializer "request_coverage.inject_test_middleware" do
15
- if RoutesCoverage.enabled?
16
- ::Rails.application.middleware.use RoutesCoverage::Middleware
17
- end
17
+ ::Rails.application.middleware.use RoutesCoverage::Middleware if RoutesCoverage.enabled?
18
18
  end
19
19
  end
20
20
 
21
21
  class Settings
22
- attr_reader :exclude_patterns
23
- attr_reader :exclude_namespaces
24
- attr_accessor :exclude_put_fallbacks
25
-
26
- attr_accessor :perform_report
27
- attr_accessor :minimum_coverage
28
- attr_accessor :round_precision
29
-
30
- attr_accessor :format
31
-
32
- attr_reader :groups
22
+ attr_reader :exclude_patterns, :exclude_namespaces, :groups
23
+ attr_accessor :exclude_put_fallbacks, :perform_report, :minimum_coverage, :round_precision, :format
33
24
 
34
25
  def initialize
35
26
  @exclude_patterns = []
@@ -63,96 +54,115 @@ module RoutesCoverage
63
54
  end
64
55
 
65
56
  def self.settings
66
- @@settings ||= Settings.new
57
+ @settings ||= Settings.new
67
58
  end
68
59
 
69
60
  def self.configure
70
- yield self.settings
61
+ yield settings
71
62
  end
72
63
 
73
- mattr_reader :pid
64
+ # used in at_exit adapter to skip subprocesses
65
+ def self.pid
66
+ @pid
67
+ end
68
+
69
+ def self.route_hit_count
70
+ @route_hit_count
71
+ end
74
72
 
75
73
  def self.reset!
76
- @@route_hit_count = Hash.new(0)
77
- @@pid = Process.pid
74
+ @route_hit_count = Hash.new(0)
75
+ @pid = Process.pid
78
76
  end
79
77
 
80
78
  def self.perform_report
81
79
  return unless settings.perform_report
82
80
 
81
+ all_routes = _collect_all_routes
82
+ all_result = Result.new(all_routes, route_hit_count, settings)
83
+ groups = _collect_route_groups(all_routes)
84
+
85
+ if groups.size > 1
86
+ ungroupped_routes = all_routes.reject do |r|
87
+ groups.values.any? do |group_routes|
88
+ group_routes.all_routes.include? r
89
+ end
90
+ end
91
+
92
+ if ungroupped_routes.any?
93
+ groups["Ungroupped"] = Result.new(ungroupped_routes, route_hit_count.slice(ungroupped_routes), settings)
94
+ end
95
+ end
96
+
97
+ puts
98
+ puts settings.formatter_class.new(all_result, groups, settings).format # rubocop:disable Rails/Output
99
+ end
100
+
101
+ def self._collect_all_routes
83
102
  all_routes = ::Rails.application.routes.routes.routes.dup
84
103
 
85
104
  if defined?(::Sprockets) && defined?(::Sprockets::Environment)
86
- all_routes.reject!{|r| r.app.is_a?(::Sprockets::Environment) }
105
+ all_routes.reject! { |r| r.app.is_a?(::Sprockets::Environment) }
87
106
  end
88
107
 
89
108
  if settings.exclude_put_fallbacks
90
- all_routes.reject!{|put_route|
109
+ all_routes.reject! do |put_route|
91
110
  (
92
111
  put_route.verb == /^PUT$/ ||
93
112
  put_route.verb == "PUT" # rails 5
94
113
  ) &&
95
- put_route.name.nil? &&
96
- @@route_hit_count[put_route] == 0 &&
97
- all_routes.any?{|patch_route|
98
- (
99
- patch_route.verb == /^PATCH$/ ||
100
- patch_route.verb == "PATCH" # rails5
101
- ) &&
102
- patch_route.defaults == put_route.defaults &&
103
- patch_route.ip == put_route.ip &&
104
- patch_route.path.spec.to_s == put_route.path.spec.to_s
105
- }
106
- }
114
+ put_route.name.nil? &&
115
+ route_hit_count[put_route].zero? &&
116
+ all_routes.any? do |patch_route|
117
+ (
118
+ patch_route.verb == /^PATCH$/ ||
119
+ patch_route.verb == "PATCH" # rails5
120
+ ) &&
121
+ patch_route.defaults == put_route.defaults &&
122
+ patch_route.ip == put_route.ip &&
123
+ patch_route.path.spec.to_s == put_route.path.spec.to_s
124
+ end
125
+ end
107
126
  end
127
+ all_routes
128
+ end
108
129
 
109
- all_result = Result.new(
110
- all_routes,
111
- @@route_hit_count,
112
- settings
113
- )
114
-
115
-
116
- groups = Hash[settings.groups.map{|group_name, regex|
117
- [group_name,
118
- Result.new(
119
- all_routes.select{|r| r.path.spec.to_s =~ regex},
120
- Hash[@@route_hit_count.select{|r,_hits| r.path.spec.to_s =~ regex}],
121
- settings
122
- )
123
- ]
124
- }]
125
-
126
- if groups.size > 1
127
- ungroupped_routes = all_routes.reject{|r|
128
- groups.values.any?{|group_routes|
129
- group_routes.all_routes.include? r
130
- }
131
- }
132
-
133
- if ungroupped_routes.any?
134
- groups["Ungroupped"] = Result.new(
135
- ungroupped_routes,
136
- Hash[@@route_hit_count.select{|r,_hits| ungroupped_routes.include? r}],
137
- settings
138
- )
130
+ def self._collect_route_groups(all_routes)
131
+ settings.groups.map do |group_name, matcher|
132
+ group_routes = all_routes.select do |route|
133
+ if matcher.respond_to?(:call)
134
+ matcher.call(route)
135
+ elsif matcher.is_a?(Hash)
136
+ matcher.all? do |key, value|
137
+ case key
138
+ when :path
139
+ route.path.spec.to_s =~ value
140
+ when :constraints
141
+ value.all? do |constraint_name, constraint_value|
142
+ if constraint_value.present?
143
+ route.constraints[constraint_name] && route.constraints[constraint_name].match?(constraint_value)
144
+ else
145
+ route.constraints[constraint_name].blank?
146
+ end
147
+ end
148
+ end
149
+ end
150
+ else
151
+ route.path.spec.to_s.match?(matcher)
152
+ end
139
153
  end
140
- end
141
154
 
142
- puts
143
- puts settings.formatter_class.new(all_result, groups, settings).format
155
+ [group_name, Result.new(group_routes, route_hit_count.slice(group_routes), settings)]
156
+ end.to_h
144
157
  end
145
158
 
146
-
147
- def self._touch_route route
148
- reset! unless @@route_hit_count
149
- @@route_hit_count[route] += 1
159
+ def self._touch_route(route)
160
+ reset! unless route_hit_count
161
+ route_hit_count[route] += 1
150
162
  end
151
163
  end
152
164
 
153
- if defined? RSpec
154
- require "routes_coverage/adapters/rspec"
155
- end
165
+ require "routes_coverage/adapters/rspec" if defined? RSpec
156
166
 
157
167
  if RoutesCoverage.enabled?
158
168
  if defined?(SimpleCov) && SimpleCov.running
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'routes_coverage/version'
5
6
 
@@ -9,18 +10,23 @@ Gem::Specification.new do |spec|
9
10
  spec.authors = ["Vasily Fedoseyev"]
10
11
  spec.email = ["vasilyfedoseyev@gmail.com"]
11
12
 
12
- spec.summary = %q{Provides coverage report for your rails routes}
13
- spec.description = %q{Generates coverage report for routes hit by your request/integration/feature tests including capybara ones}
13
+ spec.summary = "Provides coverage report for your rails routes"
14
+ spec.description = "Generates coverage report for routes hit by your request/integration/feature tests "\
15
+ "including capybara ones"
14
16
  spec.homepage = "https://github.com/Vasfed/routes_coverage"
15
17
  spec.license = "MIT"
16
18
 
17
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
- f.match(%r{^(test|spec|features|assets)/})
19
+ spec.required_ruby_version = ">= 2.0"
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
+ f.match(%r{^(test|spec|features|assets|bin|gemfiles)/}) ||
23
+ f.start_with?('.') ||
24
+ %w[Appraisals Gemfile Rakefile].include?(f)
19
25
  end
20
26
  spec.require_paths = ["lib"]
21
27
 
28
+ spec.add_development_dependency 'appraisal'
22
29
  spec.add_development_dependency "bundler", "~> 1.14"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
30
  spec.add_development_dependency "minitest"
25
- spec.add_development_dependency 'appraisal'
31
+ spec.add_development_dependency "rake", "~> 10.0"
26
32
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: routes_coverage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vasily Fedoseyev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-09 00:00:00.000000000 Z
11
+ date: 2021-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: appraisal
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.14'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.14'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '1.14'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '1.14'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,19 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: appraisal
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '10.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '10.0'
69
69
  description: Generates coverage report for routes hit by your request/integration/feature
70
70
  tests including capybara ones
71
71
  email:
@@ -74,26 +74,10 @@ executables: []
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
- - ".gitattributes"
78
- - ".gitignore"
79
- - ".travis.yml"
80
- - Appraisals
81
- - Gemfile
82
77
  - LICENSE.txt
83
78
  - README.md
84
- - Rakefile
85
- - bin/console
86
- - bin/setup
87
79
  - compiled_assets/routes.css
88
80
  - compiled_assets/routes.js
89
- - gemfiles/.bundle/config
90
- - gemfiles/rails_3.gemfile
91
- - gemfiles/rails_40.gemfile
92
- - gemfiles/rails_40_rspec.gemfile
93
- - gemfiles/rails_40_simplecov.gemfile
94
- - gemfiles/rails_42.gemfile
95
- - gemfiles/rails_5.gemfile
96
- - gemfiles/rails_51.gemfile
97
81
  - lib/routes_coverage.rb
98
82
  - lib/routes_coverage/adapters/atexit.rb
99
83
  - lib/routes_coverage/adapters/rspec.rb
@@ -112,7 +96,7 @@ homepage: https://github.com/Vasfed/routes_coverage
112
96
  licenses:
113
97
  - MIT
114
98
  metadata: {}
115
- post_install_message:
99
+ post_install_message:
116
100
  rdoc_options: []
117
101
  require_paths:
118
102
  - lib
@@ -120,16 +104,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
120
104
  requirements:
121
105
  - - ">="
122
106
  - !ruby/object:Gem::Version
123
- version: '0'
107
+ version: '2.0'
124
108
  required_rubygems_version: !ruby/object:Gem::Requirement
125
109
  requirements:
126
110
  - - ">="
127
111
  - !ruby/object:Gem::Version
128
112
  version: '0'
129
113
  requirements: []
130
- rubyforge_project:
131
- rubygems_version: 2.6.12
132
- signing_key:
114
+ rubygems_version: 3.0.9
115
+ signing_key:
133
116
  specification_version: 4
134
117
  summary: Provides coverage report for your rails routes
135
118
  test_files: []
data/.gitattributes DELETED
@@ -1 +0,0 @@
1
- compiled_assets/* linguist-generated
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- /log/
11
- /coverage/
12
- /gemfiles/*.lock
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.3.0
5
- before_install: gem install bundler -v 1.14.6
data/Appraisals DELETED
@@ -1,33 +0,0 @@
1
- # to test against all, run: appraisal rake spec
2
-
3
-
4
- appraise "rails-40" do
5
- gem "rails", "~>4.0.0"
6
- end
7
-
8
- appraise "rails-42" do
9
- gem "rails", "~>4.2.0"
10
- end
11
-
12
- appraise "rails-5" do
13
- gem "rails", "~>5.0.0"
14
- end
15
-
16
- appraise "rails-40+rspec" do
17
- gem "rails", "~>4.0.0"
18
- gem "rspec-rails"
19
- end
20
-
21
- appraise "rails-40+simplecov" do
22
- gem "rails", "~>4.0.0"
23
- gem "simplecov"
24
- end
25
-
26
- appraise "rails-51" do
27
- gem "rails", "~>5.1.0"
28
- end
29
-
30
- appraise 'rails-3' do
31
- gem 'rails', '~>3.2.22'
32
- gem 'test-unit'
33
- end
data/Gemfile DELETED
@@ -1,15 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- #NB: gem's dependencies are in routes_coverage.gemspec
4
- #NB: all other non-listed gems should go into Appraisals,
5
- # this file is only for quick tests
6
-
7
- # rails should be included before us
8
- gem 'rails', '5.2.0.rc1'
9
- gem 'simplecov', require: false
10
-
11
- # for assets:
12
- gem 'sprockets'
13
- gem 'sass'
14
-
15
- gemspec
data/Rakefile DELETED
@@ -1,36 +0,0 @@
1
- require 'bundler'
2
- require 'bundler/setup'
3
- require "bundler/gem_tasks"
4
-
5
- require 'rake/testtask'
6
-
7
- Rake::TestTask.new(:spec) do |t|
8
- t.pattern = 'spec/**/*_spec.rb'
9
- t.libs.push 'spec'
10
- end
11
-
12
- Rake::TestTask.new(:dummytest) do |t| t.pattern = 'spec/fixtures/dummy_test.rb' end
13
- Rake::TestTask.new(:dummytest_html) do |t| t.pattern = 'spec/fixtures/dummy_html.rb' end
14
- Rake::TestTask.new(:dummytest_full) do |t| t.pattern = 'spec/fixtures/dummy_test_full.rb' end
15
- Rake::TestTask.new(:dummytest_filter) do |t| t.pattern = 'spec/fixtures/dummy_test_nsfilters.rb' end
16
- Rake::TestTask.new(:dummytest_groups) do |t| t.pattern = 'spec/fixtures/dummy_test_groups.rb' end
17
-
18
-
19
- task :default => :spec
20
-
21
- $:.push File.expand_path("../lib", __FILE__)
22
- require 'routes_coverage/version'
23
-
24
- namespace :assets do
25
- desc "Compiles all assets"
26
- task :compile do
27
- puts "Compiling assets"
28
- require "sprockets"
29
- assets = Sprockets::Environment.new
30
- assets.append_path "assets/javascripts"
31
- assets.append_path "assets/stylesheets"
32
- compiled_path = "compiled_assets"
33
- assets["application.js"].write_to("#{compiled_path}/routes.js")
34
- assets["application.css"].write_to("#{compiled_path}/routes.css")
35
- end
36
- end
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "rails"
5
- require "routes_coverage"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require "irb"
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
- appraisal
@@ -1,2 +0,0 @@
1
- ---
2
- BUNDLE_RETRY: "1"
@@ -1,11 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rails", "~>3.2.22"
6
- gem "simplecov", require: false
7
- gem "sprockets"
8
- gem "sass"
9
- gem "test-unit"
10
-
11
- gemspec path: "../"
@@ -1,10 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rails", "~>4.0.0"
6
- gem "simplecov", require: false
7
- gem "sprockets"
8
- gem "sass"
9
-
10
- gemspec path: "../"
@@ -1,11 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rails", "~>4.0.0"
6
- gem "simplecov", require: false
7
- gem "sprockets"
8
- gem "sass"
9
- gem "rspec-rails"
10
-
11
- gemspec path: "../"
@@ -1,10 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rails", "~>4.0.0"
6
- gem "simplecov"
7
- gem "sprockets"
8
- gem "sass"
9
-
10
- gemspec path: "../"
@@ -1,10 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rails", "~>4.2.0"
6
- gem "simplecov", require: false
7
- gem "sprockets"
8
- gem "sass"
9
-
10
- gemspec path: "../"
@@ -1,10 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rails", "~>5.0.0"
6
- gem "simplecov", require: false
7
- gem "sprockets"
8
- gem "sass"
9
-
10
- gemspec path: "../"
@@ -1,10 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rails", "~>5.1.0"
6
- gem "simplecov", require: false
7
- gem "sprockets"
8
- gem "sass"
9
-
10
- gemspec path: "../"