routes_coverage 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d0ca681a352b4b2c633a83b99cc7c5b4334e2e40
4
- data.tar.gz: d3d3126f530afe3fff1978c801a5194247b24580
3
+ metadata.gz: dcc558504676f10b8f11d5c803288084c92b7c84
4
+ data.tar.gz: 063d639635c7c5b3fbd17396f129882559541cfb
5
5
  SHA512:
6
- metadata.gz: 9d1a5f0dff0432078c4f359f5feb98e0687a9bb778c9ec6b4fa8de40421eefd92c10b5b61f259556a2ee7be6b9aeefb0fd7286fee5f59a0e1ef56b20e1be61ac
7
- data.tar.gz: 5fc59b5bfa01ec2a87c99fb861e67795cd33ebdc167bc03b3923ab50e2216bb6ca3f1b1733a18da5b8f56d3b9c36a5ebabd2469c3f96f56678f0b16d5278bd76
6
+ metadata.gz: d6dfb367c8a7f0487959ad3ef831cf057ef3a1ea3849583a89b0c52df259ffe58b27ad59a6ec03420d17bbd192f71fed14af2fdfe385de4298960f07db347060
7
+ data.tar.gz: a5308eb14d95cb0afb8a95221fad4c5ac7f6ef3897d159680ede52d1b6db6e51b77268e52992fa3c0e27986746c1adb823d0bfe687b20fc4bd85e9a6ca769047
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  /log/
11
+ /coverage/
data/Appraisals CHANGED
@@ -18,6 +18,11 @@ appraise "rails-40+rspec" do
18
18
  gem "rspec-rails"
19
19
  end
20
20
 
21
+ appraise "rails-40+simplecov" do
22
+ gem "rails", "~>4.0.0"
23
+ gem "simplecov"
24
+ end
25
+
21
26
  appraise "rails-51" do
22
27
  gem "rails", "~>5.1.0"
23
28
  end
data/Gemfile CHANGED
@@ -1,7 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in routes_coverage.gemspec
4
- gemspec
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
5
6
 
6
- #TODO: appraisal
7
+ # rails should be included before us
7
8
  gem 'rails', '4.0.13'
9
+
10
+ gemspec
data/README.md CHANGED
@@ -39,6 +39,14 @@ To get more detailed information somewhere in your test helper add
39
39
 
40
40
  ```ruby
41
41
  RoutesCoverage.settings.format = :full_text
42
+ ```
43
+
44
+ or into RSpec config:
45
+
46
+ ```ruby
47
+ RSpec.configure do |config|
48
+ config.routes_coverage.format = :full_text
49
+ end
42
50
  ```
43
51
 
44
52
  Routes coverage is 11.1% (1 of 9 routes hit at 1.0 hits average)
@@ -59,6 +67,41 @@ RoutesCoverage.settings.format = :full_text
59
67
  PUT /reqs/:id(.:format) dummy#update
60
68
  DELETE /reqs/:id(.:format) dummy#destroy
61
69
 
70
+ ### Usage with SimpleCov
71
+
72
+ Use `RoutesCoverage.settings.format = :simplecov_html` along with simplecov to generate a html report like this:
73
+
74
+ ![Html output example](/assets/html_output_screenshot.png?raw=true "Html Output example")
75
+
76
+ at the moment it shares styles with simplecov's one,
77
+ code coverage report does not need to be generated each time as long as you have `/coverage` directory with all the resources.
78
+
79
+ ### Configuration
80
+
81
+ Set options in `RoutesCoverage.settings` or rspec's `config.routes_coverage`:
82
+
83
+ ```ruby
84
+ RSpec.configure do |config|
85
+ config.routes_coverage.format = :full_text
86
+ config.routes_coverage.exclude_patterns << %r{PATCH /reqs} # excludes all requests matching regex
87
+ config.routes_coverage.exclude_namespaces << 'somenamespace' # excludes /somenamespace/*
88
+ config.routes_coverage.minimum_coverage = 80 # %, your coverage goal
89
+ config.routes_coverage.round_precision = 0 # just round to whole percents
90
+ end
91
+ ```
92
+
93
+ or
94
+
95
+ ```ruby
96
+ RoutesCoverage.configure do |config|
97
+ config.format = :full_text
98
+ # ...
99
+ end
100
+ ```
101
+
102
+
103
+ Excluded routes do not show in pending, but are shown if they're hit.
104
+
62
105
 
63
106
  ## Development
64
107
 
data/Rakefile CHANGED
@@ -9,6 +9,12 @@ Rake::TestTask.new(:spec) do |t|
9
9
  t.libs.push 'spec'
10
10
  end
11
11
 
12
+ Rake::TestTask.new(:dummytest) do |t| t.pattern = 'spec/fixtures/dummy_test.rb' end
13
+ Rake::TestTask.new(:dummytest_full) do |t| t.pattern = 'spec/fixtures/dummy_test_full.rb' end
14
+ Rake::TestTask.new(:dummytest_filter) do |t| t.pattern = 'spec/fixtures/dummy_test_nsfilters.rb' end
15
+ Rake::TestTask.new(:dummytest_groups) do |t| t.pattern = 'spec/fixtures/dummy_test_groups.rb' end
16
+
17
+
12
18
  task :default => :spec
13
19
 
14
20
  $:.push File.expand_path("../lib", __FILE__)
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -4,4 +4,4 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~>4.0.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- routes_coverage (0.0.2)
4
+ routes_coverage (0.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -5,4 +5,4 @@ source "https://rubygems.org"
5
5
  gem "rails", "~>4.0.0"
6
6
  gem "rspec-rails"
7
7
 
8
- gemspec :path => "../"
8
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- routes_coverage (0.0.2)
4
+ routes_coverage (0.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~>4.0.0"
6
+ gem "simplecov"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,97 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ routes_coverage (0.0.3)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ actionmailer (4.0.13)
10
+ actionpack (= 4.0.13)
11
+ mail (~> 2.5, >= 2.5.4)
12
+ actionpack (4.0.13)
13
+ activesupport (= 4.0.13)
14
+ builder (~> 3.1.0)
15
+ erubis (~> 2.7.0)
16
+ rack (~> 1.5.2)
17
+ rack-test (~> 0.6.2)
18
+ activemodel (4.0.13)
19
+ activesupport (= 4.0.13)
20
+ builder (~> 3.1.0)
21
+ activerecord (4.0.13)
22
+ activemodel (= 4.0.13)
23
+ activerecord-deprecated_finders (~> 1.0.2)
24
+ activesupport (= 4.0.13)
25
+ arel (~> 4.0.0)
26
+ activerecord-deprecated_finders (1.0.4)
27
+ activesupport (4.0.13)
28
+ i18n (~> 0.6, >= 0.6.9)
29
+ minitest (~> 4.2)
30
+ multi_json (~> 1.3)
31
+ thread_safe (~> 0.1)
32
+ tzinfo (~> 0.3.37)
33
+ appraisal (2.2.0)
34
+ bundler
35
+ rake
36
+ thor (>= 0.14.0)
37
+ arel (4.0.2)
38
+ builder (3.1.4)
39
+ concurrent-ruby (1.0.5)
40
+ docile (1.1.5)
41
+ erubis (2.7.0)
42
+ i18n (0.8.1)
43
+ json (1.8.6)
44
+ mail (2.6.5)
45
+ mime-types (>= 1.16, < 4)
46
+ mime-types (3.1)
47
+ mime-types-data (~> 3.2015)
48
+ mime-types-data (3.2016.0521)
49
+ minitest (4.7.5)
50
+ multi_json (1.12.1)
51
+ rack (1.5.5)
52
+ rack-test (0.6.3)
53
+ rack (>= 1.0)
54
+ rails (4.0.13)
55
+ actionmailer (= 4.0.13)
56
+ actionpack (= 4.0.13)
57
+ activerecord (= 4.0.13)
58
+ activesupport (= 4.0.13)
59
+ bundler (>= 1.3.0, < 2.0)
60
+ railties (= 4.0.13)
61
+ sprockets-rails (~> 2.0)
62
+ railties (4.0.13)
63
+ actionpack (= 4.0.13)
64
+ activesupport (= 4.0.13)
65
+ rake (>= 0.8.7)
66
+ thor (>= 0.18.1, < 2.0)
67
+ rake (10.5.0)
68
+ simplecov (0.14.1)
69
+ docile (~> 1.1.0)
70
+ json (>= 1.8, < 3)
71
+ simplecov-html (~> 0.10.0)
72
+ simplecov-html (0.10.0)
73
+ sprockets (3.7.1)
74
+ concurrent-ruby (~> 1.0)
75
+ rack (> 1, < 3)
76
+ sprockets-rails (2.3.3)
77
+ actionpack (>= 3.0)
78
+ activesupport (>= 3.0)
79
+ sprockets (>= 2.8, < 4.0)
80
+ thor (0.19.4)
81
+ thread_safe (0.3.6)
82
+ tzinfo (0.3.53)
83
+
84
+ PLATFORMS
85
+ ruby
86
+
87
+ DEPENDENCIES
88
+ appraisal
89
+ bundler (~> 1.14)
90
+ minitest
91
+ rails (~> 4.0.0)
92
+ rake (~> 10.0)
93
+ routes_coverage!
94
+ simplecov
95
+
96
+ BUNDLED WITH
97
+ 1.14.6
@@ -4,4 +4,4 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~>4.2.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- routes_coverage (0.0.2)
4
+ routes_coverage (0.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -4,4 +4,4 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~>5.0.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- routes_coverage (0.0.2)
4
+ routes_coverage (0.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -4,4 +4,4 @@ source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~>5.1.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- routes_coverage (0.0.2)
4
+ routes_coverage (0.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1,12 @@
1
+ module RoutesCoverage
2
+ module Adapters
3
+ class SimpleCov
4
+ def self.use
5
+ RoutesCoverage.reset!
6
+ ::SimpleCov.at_exit{
7
+ RoutesCoverage.perform_report
8
+ }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module RoutesCoverage
2
+ module Formatters
3
+ class Base
4
+ def initialize result, groups, settings
5
+ @result = result
6
+ @groups = groups
7
+ @settings = settings
8
+ end
9
+
10
+ attr_reader :result
11
+ attr_reader :groups
12
+ attr_reader :settings
13
+ end
14
+ end
15
+ end
@@ -1,29 +1,7 @@
1
- require 'active_support/core_ext/string' # needed for rails5 version of inspector
2
- require 'action_dispatch/routing/inspector'
3
-
4
1
  module RoutesCoverage
5
2
  module Formatters
6
3
  class FullText < SummaryText
7
4
 
8
- class Inspector < ActionDispatch::Routing::RoutesInspector
9
- def collect_routes(routes)
10
- routes.collect do |route|
11
- ActionDispatch::Routing::RouteWrapper.new(route)
12
- end.reject do |route|
13
- route.internal?
14
- end.collect do |route|
15
- collect_engine_routes(route)
16
-
17
- { name: route.name,
18
- verb: route.verb,
19
- path: route.path,
20
- reqs: route.reqs,
21
- # regexp: route.json_regexp, # removed, this is not present in rails5
22
- original: route, # added
23
- }
24
- end
25
- end
26
- end
27
5
 
28
6
  class RouteFormatter < ActionDispatch::Routing::ConsoleFormatter
29
7
 
@@ -50,7 +28,7 @@ module RoutesCoverage
50
28
  if @output_hits
51
29
  # hits = " ?"
52
30
  original_route = r[:original].__getobj__ # SimpleDelegator
53
- hits = " #{@result.route_hits[original_route]}"
31
+ hits = " #{@result.route_hit_counts[original_route]}"
54
32
  end
55
33
  "#{r[:name].rjust(name_width) if @output_prefix} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs].ljust(reqs_width)}#{hits}"
56
34
  end
@@ -83,15 +61,16 @@ module RoutesCoverage
83
61
  # routes.map do |r|
84
62
  # "#{r.verb.ljust(verb_width)} #{r.path.ljust(path_width)} #{r.reqs}"
85
63
  # end
86
- Inspector.new(routes).format(RouteFormatter.new(result, settings, true)),
64
+ #TODO: replace formatter
65
+ Result::Inspector.new(routes).format(RouteFormatter.new(result, settings, true)),
87
66
  nil,
88
67
  "Pending routes:",
89
- Inspector.new(result.pending_routes).format(RouteFormatter.new),
68
+ Result::Inspector.new(result.pending_routes).format(RouteFormatter.new),
90
69
  ].flatten.join("\n")
91
70
  end
92
71
 
93
72
  def format
94
- "\nRoutes coverage is #{result.coverage}% (#{hits_count})#{status}\n\n#{hit_routes}"
73
+ "#{super}\n\n#{hit_routes}"
95
74
  end
96
75
  end
97
76
  end
@@ -0,0 +1,53 @@
1
+ <!DOCTYPE html>
2
+ <html xmlns='http://www.w3.org/1999/xhtml'>
3
+ <head>
4
+ <title>Code coverage for <%= SimpleCov.project_name %></title>
5
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
6
+ <script src='<%= assets_path('application.js') %>' type='text/javascript'></script>
7
+ <link href='<%= assets_path('application.css') %>' media='screen, projection, print' rel='stylesheet' type='text/css'>
8
+ <link rel="shortcut icon" type="image/png" href="<%= assets_path("favicon_#{coverage_css_class(result.coverage)}.png") %>" />
9
+ <link rel="icon" type="image/png" href="<%= assets_path('favicon.png') %>" />
10
+
11
+ <script>
12
+ $(document).ready(function() {
13
+ $('.route_list').dataTable({
14
+ "aaSorting": [], //[[ 1, "asc" ]],
15
+ "bPaginate": false,
16
+ "bJQueryUI": true,
17
+ "aoColumns": [
18
+ null, //name
19
+ null, //verb
20
+ null, //path
21
+ null, //action
22
+ null, //covered yes/no
23
+ null, //hits count
24
+ ]
25
+ });
26
+ });
27
+ </script>
28
+ </head>
29
+
30
+ <body>
31
+ <div id="loading">
32
+ Loading...
33
+ </div>
34
+ <div id="wrapper" style="display:none;">
35
+ <div class="timestamp">Generated <%= timeago(Time.now) %></div>
36
+ <ul class="group_tabs"></ul>
37
+
38
+ <div id="content">
39
+ <%= route_group_result("All Routes", result) %>
40
+
41
+ <% groups.each do |name, group_result| %>
42
+ <%= route_group_result(name, group_result) %>
43
+ <% end %>
44
+ </div>
45
+
46
+ <div id="footer">
47
+ Generated by <a href="http://github.com/Vasfed/routes_coverage">routes_coverage</a> v<%= RoutesCoverage::VERSION %>
48
+ and simplecov-html v<%= SimpleCov::Formatter::HTMLFormatter::VERSION %><br/>
49
+ </div>
50
+
51
+ </div>
52
+ </body>
53
+ </html>
@@ -0,0 +1,48 @@
1
+ <div class="file_list_container" id="<%= title_id %>">
2
+ <h2>
3
+ <span class="group_name"><%= title %></span>
4
+ (<span class="covered_percent"><span class="<%= coverage_css_class(result.coverage.to_f) %>"><%= result.coverage %>%</span></span>
5
+ covered at
6
+ <span class="covered_strength">
7
+ <span class="<%#= strength_css_class(result.avg_hits) %>">
8
+ <%= result.avg_hits.round(2) %>
9
+ </span>
10
+ </span> hits/route)
11
+ </h2>
12
+ <a name="<%= title_id %>"></a>
13
+ <div>
14
+ <b><%= result.total_count %></b> routes in total.
15
+ <b><%= result.expected_routes_count %></b> relevant routes.
16
+ <span class="green"><b><%= result.hit_routes_count %></b> covered</span> and
17
+ <span class="red"><b><%= result.pending_routes.size %></b> missed </span>
18
+ </div>
19
+ <table class="route_list">
20
+ <thead>
21
+ <tr>
22
+ <th>Name</th>
23
+ <th>Verb</th>
24
+ <th>Path</th>
25
+ <th>Controller</th>
26
+ <th>Covered</th>
27
+ <th>Hits</th>
28
+ </tr>
29
+ </thead>
30
+ <tbody>
31
+ <% result.all_routes_with_hits.each do |route| %>
32
+ <tr>
33
+ <td class="strong"><%= route[:name] %></td>
34
+ <td class="strong"><%= route[:verb] %></td>
35
+ <td class="strong"><%= route[:path] %></td>
36
+ <td class="strong">
37
+ <% if route[:engine_name] %>
38
+ engine <%= route[:engine_name] %>
39
+ <% end %>
40
+ <%= route[:reqs] %>
41
+ </td>
42
+ <td class="strong <%= hits_css_class route[:hits] %>"><%= route[:hits] > 0 ? 'Yes' : 'No' %></td>
43
+ <td class="<%= hits_css_class route[:hits] %> strong"><%= route[:hits] %></td>
44
+ </tr>
45
+ <% end %>
46
+ </tbody>
47
+ </table>
48
+ </div>
@@ -0,0 +1,97 @@
1
+ require "erb"
2
+ require "cgi"
3
+ require "fileutils"
4
+ require "digest/sha1"
5
+ require "time"
6
+
7
+ module RoutesCoverage
8
+ module Formatters
9
+ class SimpleCovHtml < Base
10
+ def format
11
+ #TODO: copy assets, if simplecov does not generate a report along us
12
+
13
+ # Dir[File.join(File.dirname(__FILE__), "../public/*")].each do |path|
14
+ # FileUtils.cp_r(path, asset_output_path)
15
+ # end
16
+
17
+ routes_filename = "routes.html"
18
+
19
+ File.open(File.join(output_path, routes_filename), "wb") do |file|
20
+ file.puts template("layout").result(binding)
21
+ end
22
+
23
+ "Routes coverage is #{result.coverage}% Report generated to #{output_path}/#{routes_filename}"
24
+ end
25
+
26
+ private
27
+
28
+ def template(name)
29
+ ERB.new(File.read(File.join(File.dirname(__FILE__), "html_views", "#{name}.erb")))
30
+ end
31
+
32
+ def output_path
33
+ SimpleCov.coverage_path
34
+ end
35
+
36
+ def asset_output_path
37
+ return @asset_output_path if defined?(@asset_output_path) && @asset_output_path
38
+ @asset_output_path = File.join(output_path, "assets", SimpleCov::Formatter::HTMLFormatter::VERSION)
39
+ FileUtils.mkdir_p(@asset_output_path)
40
+ @asset_output_path
41
+ end
42
+
43
+ def assets_path(name)
44
+ File.join("./assets", SimpleCov::Formatter::HTMLFormatter::VERSION, name)
45
+ end
46
+
47
+ # Returns the html for the given source_file
48
+ def formatted_source_file(source_file)
49
+ template("source_file").result(binding)
50
+ end
51
+
52
+ # Returns a table containing the given source files
53
+ def route_group_result(title, result)
54
+ title_id = title.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-\_]/, "")
55
+ # Silence a warning by using the following variable to assign to itself:
56
+ # "warning: possibly useless use of a variable in void context"
57
+ # The variable is used by ERB via binding.
58
+ title_id = title_id
59
+ template("route_group").result(binding)
60
+ end
61
+
62
+ def coverage_css_class(covered_percent)
63
+ if covered_percent > 90
64
+ "green"
65
+ elsif covered_percent > 80
66
+ "yellow"
67
+ else
68
+ "red"
69
+ end
70
+ end
71
+
72
+ def strength_css_class(covered_strength)
73
+ if covered_strength > 1
74
+ "green"
75
+ elsif covered_strength == 1
76
+ "yellow"
77
+ else
78
+ "red"
79
+ end
80
+ end
81
+
82
+ def hits_css_class hits
83
+ hits > 0 ? 'green' : 'red'
84
+ end
85
+
86
+ # Return a (kind of) unique id for the source file given. Uses SHA1 on path for the id
87
+ def id(source_file)
88
+ Digest::SHA1.hexdigest(source_file.filename)
89
+ end
90
+
91
+ def timeago(time)
92
+ "<abbr class=\"timeago\" title=\"#{time.iso8601}\">#{time.iso8601}</abbr>"
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -1,29 +1,31 @@
1
1
  module RoutesCoverage
2
2
  module Formatters
3
- class SummaryText
4
- def initialize result, settings
5
- @result = result
6
- @settings = settings
7
- end
8
-
9
- attr_reader :result
10
- attr_reader :settings
11
-
12
- def hits_count
3
+ class SummaryText < Base
4
+ def hits_count result
13
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}"
14
6
  end
15
7
 
16
8
  def status
17
9
  return unless settings.minimum_coverage
18
- if result.coverage_pass?
19
- ""
20
- else
21
- "\nCoverage failed. Need at least #{(settings.minimum_coverage / 100.0 * result.total_count).ceil}"
10
+ unless result.coverage_pass?
11
+ "Coverage is too low"
22
12
  end
23
13
  end
24
14
 
25
15
  def format
26
- "\nRoutes coverage is #{result.coverage}% (#{hits_count})#{status}"
16
+ buffer = [
17
+ "Routes coverage is #{result.coverage}% (#{hits_count result})"
18
+ ]
19
+
20
+ if groups.any?
21
+ buffer += groups.map{|group_name, group_result|
22
+ " #{group_name}: #{group_result.coverage}% (#{hits_count group_result})"
23
+ }
24
+ end
25
+
26
+ buffer << status
27
+
28
+ buffer.compact.join("\n")
27
29
  end
28
30
  end
29
31
  end
@@ -5,13 +5,9 @@ module RoutesCoverage
5
5
  end
6
6
 
7
7
  def call env
8
- # method = env["REQUEST_METHOD"]
9
- # path = env["REQUEST_PATH"] || env["PATH_INFO"] || env["ORIGINAL_FULLPATH"]
10
- # puts "req #{method} #{env["REQUEST_PATH"]} || #{env["ORIGINAL_FULLPATH"]} || #{env["PATH_INFO"]}"
11
- # puts "env is #{env.inspect}"
12
8
  req = ::Rails.application.routes.request_class.new env
13
9
  ::Rails.application.routes.router.recognize(req) do |route|
14
- RoutesCoverage.current_result.touch_route(route)
10
+ RoutesCoverage._touch_route(route)
15
11
  end
16
12
  #TODO: detect 404s? and maybe other route errors?
17
13
  @app.call(env)
@@ -1,45 +1,33 @@
1
+ require 'active_support/core_ext/string' # needed for rails5 version of inspector
2
+ require 'action_dispatch/routing/inspector'
3
+
1
4
  module RoutesCoverage
2
5
  class Result
3
- def initialize
4
- @route_hits = Hash.new(0)
6
+ def initialize all_routes, hit_routes, settings
7
+ @all_routes = all_routes
8
+ @route_hit_counts = hit_routes
9
+ @settings = settings
5
10
  end
6
11
 
7
- attr_reader :route_hits
8
-
9
- def touch_route route
10
- @route_hits[route] += 1
11
- end
12
+ attr_reader :all_routes
12
13
 
13
- def all_routes
14
- ::Rails.application.routes.routes.routes
15
- end
14
+ attr_reader :route_hit_counts
16
15
 
17
16
  def expected_routes
18
17
  return @expected_routes if @expected_routes
19
18
 
20
- routes = all_routes.dup
21
-
22
- if defined?(::Sprockets) && defined?(::Sprockets::Environment)
23
- routes.reject!{|r| r.app.is_a?(::Sprockets::Environment) }
24
- end
19
+ filter_regex = Regexp.union(@settings.exclude_patterns)
20
+ namespaces_regex = Regexp.union(@settings.exclude_namespaces.map{|n| /^\/#{n}/})
25
21
 
26
- excluded_routes = []
27
- regex = Regexp.union(RoutesCoverage.settings.exclude_patterns)
28
- routes.reject!{|r|
29
- if "#{r.verb.to_s[8..-3]} #{r.path.spec}".strip =~ regex
30
- excluded_routes << r
31
- end
22
+ routes_groups = all_routes.group_by{|r|
23
+ !!(
24
+ ("#{r.verb.to_s[8..-3]} #{r.path.spec}".strip =~ filter_regex) ||
25
+ (r.path.spec.to_s =~ namespaces_regex)
26
+ )
32
27
  }
33
28
 
34
- namespaces_regex = Regexp.union(RoutesCoverage.settings.exclude_namespaces.map{|n| /^\/#{n}/})
35
- routes.reject!{|r|
36
- if r.path.spec.to_s =~ namespaces_regex
37
- excluded_routes << r
38
- end
39
- }
40
-
41
- @excluded_routes = excluded_routes
42
- @expected_routes = routes
29
+ @excluded_routes = routes_groups[true] || []
30
+ @expected_routes = routes_groups[false] || []
43
31
  end
44
32
 
45
33
  def pending_routes
@@ -53,12 +41,12 @@ module RoutesCoverage
53
41
 
54
42
  def hit_routes
55
43
  #TODO: sort?
56
- route_hits.keys
44
+ @route_hit_counts.keys
57
45
  end
58
46
 
59
47
 
60
48
  def hit_routes_count
61
- route_hits.size
49
+ @route_hit_counts.size
62
50
  end
63
51
 
64
52
  def expected_routes_count
@@ -75,15 +63,56 @@ module RoutesCoverage
75
63
 
76
64
  def coverage
77
65
  return 'n/a' unless expected_routes.any?
78
- (hit_routes_count * 100.0 / expected_routes_count).round(RoutesCoverage.settings.round_precision)
66
+ (hit_routes_count * 100.0 / expected_routes_count).round(@settings.round_precision)
79
67
  end
80
68
 
81
69
  def avg_hits
82
- (route_hits.values.sum.to_f / hit_routes_count).round(RoutesCoverage.settings.round_precision)
70
+ (@route_hit_counts.values.sum.to_f / hit_routes_count).round(@settings.round_precision)
83
71
  end
84
72
 
85
73
  def coverage_pass?
86
- !RoutesCoverage.settings.minimum_coverage || (coverage >= RoutesCoverage.settings.minimum_coverage)
74
+ !@settings.minimum_coverage || (coverage.to_f >= @settings.minimum_coverage)
87
75
  end
76
+
77
+ def all_routes_with_hits
78
+ res = Inspector.new(all_routes).collect_all_routes
79
+ res.each{|val|
80
+ val[:hits] = @route_hit_counts[val[:original]] || 0
81
+ }
82
+ res
83
+ end
84
+
85
+ class Inspector < ActionDispatch::Routing::RoutesInspector
86
+ def collect_all_routes
87
+ res = collect_routes(@routes)
88
+ #TODO: test with engines
89
+ @engines.each do |engine_name, engine_routes|
90
+ res += engine_routes.map{|er|
91
+ er.merge({ engine_name: engine_name })
92
+ }
93
+ end
94
+ res
95
+ end
96
+
97
+ def collect_routes(routes)
98
+ routes.collect do |route|
99
+ ActionDispatch::Routing::RouteWrapper.new(route)
100
+ end.reject do |route|
101
+ route.internal?
102
+ end.collect do |route|
103
+ collect_engine_routes(route)
104
+
105
+ { name: route.name,
106
+ verb: route.verb,
107
+ path: route.path,
108
+ reqs: route.reqs,
109
+ # regexp: route.json_regexp, # removed, this is not present in rails5
110
+ # added:
111
+ original: route,
112
+ }
113
+ end
114
+ end
115
+ end
116
+
88
117
  end
89
118
  end
@@ -1,3 +1,3 @@
1
1
  module RoutesCoverage
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -2,8 +2,10 @@ require "routes_coverage/version"
2
2
  require "routes_coverage/result"
3
3
  require "routes_coverage/middleware"
4
4
 
5
+ require "routes_coverage/formatters/base"
5
6
  require "routes_coverage/formatters/summary_text"
6
7
  require "routes_coverage/formatters/full_text"
8
+ require "routes_coverage/formatters/simplecov_html"
7
9
 
8
10
  module RoutesCoverage
9
11
  class Railtie < ::Rails::Railtie
@@ -24,12 +26,30 @@ module RoutesCoverage
24
26
 
25
27
  attr_accessor :format
26
28
 
29
+ attr_reader :groups
30
+
27
31
  def initialize
28
32
  @exclude_patterns = []
29
33
  @exclude_namespaces = []
30
- @minimum_coverage = 80
34
+ @minimum_coverage = 1
31
35
  @round_precision = 1
32
36
  @format = :summary_text
37
+ @groups = {}
38
+ end
39
+
40
+ def formatter_class
41
+ case format
42
+ when :full_text
43
+ Formatters::FullText
44
+ when :summary_text
45
+ Formatters::SummaryText
46
+ when :simplecov_html
47
+ Formatters::SimpleCovHtml
48
+ when Formatters::Base
49
+ format
50
+ else
51
+ raise "Unknown formatter #{settings.format.inspect}"
52
+ end
33
53
  end
34
54
  end
35
55
 
@@ -41,33 +61,60 @@ module RoutesCoverage
41
61
  @@settings ||= Settings.new
42
62
  end
43
63
 
44
- mattr_reader :current_result
64
+ def self.configure
65
+ yield self.settings
66
+ end
67
+
45
68
  mattr_reader :pid
46
69
 
47
70
  def self.reset!
48
- @@current_result = Result.new
71
+ @@route_hit_count = Hash.new(0)
49
72
  @@pid = Process.pid
50
73
  end
51
74
 
52
75
  def self.perform_report
53
- result = current_result
54
-
55
- formatter_class = case settings.format
56
- when :full_text
57
- Formatters::FullText
58
- when :summary_text
59
- Formatters::SummaryText
60
- else
61
- raise "Unknown formatter #{settings.format.inspect}"
76
+
77
+ all_routes = ::Rails.application.routes.routes.routes.dup
78
+
79
+ if defined?(::Sprockets) && defined?(::Sprockets::Environment)
80
+ all_routes.reject!{|r| r.app.is_a?(::Sprockets::Environment) }
62
81
  end
63
82
 
64
- formatter = formatter_class.new(result, settings)
65
- puts formatter.format
83
+ all_result = Result.new(
84
+ all_routes,
85
+ @@route_hit_count,
86
+ settings
87
+ )
88
+
89
+
90
+ groups = Hash[settings.groups.map{|group_name, regex|
91
+ [group_name,
92
+ Result.new(
93
+ all_routes.select{|r| r.path.spec.to_s =~ regex},
94
+ Hash[@@route_hit_count.select{|r,_hits| r.path.spec.to_s =~ regex}],
95
+ settings
96
+ )
97
+ ]
98
+ }]
99
+
100
+ #TODO: group 'ungroupped'
101
+
102
+ puts
103
+ puts settings.formatter_class.new(all_result, groups, settings).format
66
104
  end
67
105
 
106
+
107
+ def self._touch_route route
108
+ reset! unless @@route_hit_count
109
+ @@route_hit_count[route] += 1
110
+ end
68
111
  end
69
112
 
70
113
  if RoutesCoverage.enabled?
114
+ if defined? SimpleCov
115
+ #TODO: use SimpleCov.at_exit
116
+ end
117
+
71
118
  if defined? RSpec
72
119
  require "routes_coverage/adapters/rspec"
73
120
  else
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  spec.license = "MIT"
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
- f.match(%r{^(test|spec|features)/})
18
+ f.match(%r{^(test|spec|features|assets)/})
19
19
  end
20
20
  spec.require_paths = ["lib"]
21
21
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: routes_coverage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vasily Fedoseyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-12 00:00:00.000000000 Z
11
+ date: 2017-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -83,10 +83,13 @@ files:
83
83
  - Rakefile
84
84
  - bin/console
85
85
  - bin/setup
86
+ - gemfiles/.bundle/config
86
87
  - gemfiles/rails_40.gemfile
87
88
  - gemfiles/rails_40.gemfile.lock
88
89
  - gemfiles/rails_40_rspec.gemfile
89
90
  - gemfiles/rails_40_rspec.gemfile.lock
91
+ - gemfiles/rails_40_simplecov.gemfile
92
+ - gemfiles/rails_40_simplecov.gemfile.lock
90
93
  - gemfiles/rails_42.gemfile
91
94
  - gemfiles/rails_42.gemfile.lock
92
95
  - gemfiles/rails_5.gemfile
@@ -96,7 +99,12 @@ files:
96
99
  - lib/routes_coverage.rb
97
100
  - lib/routes_coverage/adapters/atexit.rb
98
101
  - lib/routes_coverage/adapters/rspec.rb
102
+ - lib/routes_coverage/adapters/simplecov.rb
103
+ - lib/routes_coverage/formatters/base.rb
99
104
  - lib/routes_coverage/formatters/full_text.rb
105
+ - lib/routes_coverage/formatters/html_views/layout.erb
106
+ - lib/routes_coverage/formatters/html_views/route_group.erb
107
+ - lib/routes_coverage/formatters/simplecov_html.rb
100
108
  - lib/routes_coverage/formatters/summary_text.rb
101
109
  - lib/routes_coverage/middleware.rb
102
110
  - lib/routes_coverage/result.rb