route_mechanic 0.1.6 → 0.2.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
2
  SHA256:
3
- metadata.gz: a9ff0ae5fc0f5045075d58e1a353cb60c7dd440a2a964b2990b12f5298493496
4
- data.tar.gz: b8b517e7f557d648aee51cb1f6e61cb151058cbf2bd662df11783a67c0d05378
3
+ metadata.gz: 21e6f6fc80509ba7ce1b98ea9e5cc7f3868b83fde1d287d5d35867802bf73b1d
4
+ data.tar.gz: '094b2771b33125d34fe4eaa06bf77bc4ba0d6d250fe094b2b68c2794e2a0274c'
5
5
  SHA512:
6
- metadata.gz: ab4884a5c8f220562e025581a1aad63b66221b15e573a97fc3d0b1517149ff78cb4beb493c1f4ee58077f4ec97ebfc3d81c98673854032c3e23dac132d2368d3
7
- data.tar.gz: c054466bbe6a7750fde4eb1563b82d819a51d0f4e638b11309f2ee8962fa67503c3a5f976232d02d68b873f550e7f5aa175ab6b27327942ddb88322b34f7937d
6
+ metadata.gz: 51fc1082c4c0857485b6d518f40c1cf8539f8509c38c9311bf62b54b0b5875e62358fc990d10aa4c26b8e0b9b9309f71e330d8236c99f4e478b002019184968b
7
+ data.tar.gz: 13f6e7e2bd77520b1be677d39609c56a68fe5d87ce7eacb70faaca58a310cceeb669f09cf64d3eea071b36dcbf5f42c6886c08b06f4cc30adb148c8ecde47dcf
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  [![Gem Version](https://badge.fury.io/rb/route_mechanic.svg)](https://badge.fury.io/rb/route_mechanic)
3
3
  [![Build Status](https://github.com/ohbarye/route_mechanic/workflows/test/badge.svg?branch=master)](https://github.com/ohbarye/route_mechanic/actions?query=workflow%3Atest)
4
4
 
5
- No need to maintain Rails' routing tests manually. RouteMechanic automatically detects broken routes and missing action methods in controller once you've finished installation.
5
+ No need to maintain Rails' routing tests manually. RouteMechanic automatically detects broken routes and missing action methods in controllers once you've finished installation.
6
6
 
7
7
  ## Installation
8
8
 
@@ -24,11 +24,16 @@ $ bundle install
24
24
 
25
25
  RouteMechanic is available for both RSpec and MiniTest.
26
26
 
27
- All you have to do is to add just one test case that keeps your application's routes not broken.
27
+ All you have to do is to add just one test case that keeps your application's routes not broken. Then, RouteMechanic will get to report 2 types of broken routes.
28
+
29
+ 1. Unused actions
30
+ - Your application has the controller and the action method but `config/routes.rb` doesn't have corresponding settings.
31
+ 2. Unused routes
32
+ - Your application's `config/routes.rb` has a routing declaration but no controller has a corresponding action method.
28
33
 
29
34
  ### RSpec
30
35
 
31
- Just add a test file which has only one test case using `have_valid_routes` matcher.
36
+ Just add one test file that has only one test case using `have_valid_routes` matcher.
32
37
 
33
38
  ```ruby
34
39
  RSpec.describe 'Rails.application', type: :routing do
@@ -38,9 +43,23 @@ RSpec.describe 'Rails.application', type: :routing do
38
43
  end
39
44
  ```
40
45
 
46
+ If you'd like to test unused actions and unused routes separately or test only one of them, there're matchers to do so.
47
+
48
+ ```ruby
49
+ RSpec.describe 'Rails.application', type: :routing do
50
+ it "fails if application has unused actions" do
51
+ expect(Rails.application).to have_no_unused_actions
52
+ end
53
+
54
+ it "fails if application has unused routes" do
55
+ expect(Rails.application).to have_no_unused_routes
56
+ end
57
+ end
58
+ ```
59
+
41
60
  ### MiniTest
42
61
 
43
- Just add a test file like below.
62
+ Just add one test file like below.
44
63
 
45
64
  ```ruby
46
65
  class RoutingTest < Minitest::Test
@@ -52,6 +71,22 @@ class RoutingTest < Minitest::Test
52
71
  end
53
72
  ```
54
73
 
74
+ If you'd like to test unused actions and unused routes separately or test only one of them, there're assertions to do so.
75
+
76
+ ```ruby
77
+ class RoutingTest < Minitest::Test
78
+ include ::RouteMechanic::Testing::Methods
79
+
80
+ def test_that_application_has_no_unused_actions
81
+ assert_no_unused_actions
82
+ end
83
+
84
+ def test_that_application_has_no_unused_routes
85
+ assert_no_unused_routes
86
+ end
87
+ end
88
+ ```
89
+
55
90
  ### What if RouteMechanic detects broken routes?
56
91
 
57
92
  It tells you broken routes as follows.
@@ -72,31 +107,23 @@ It tells you broken routes as follows.
72
107
  1 examples, 1 failure, 0 passed
73
108
  ```
74
109
 
75
- RouteMechanic reports 2 types of broken routes.
76
-
77
- 1. Missing routes
78
- - Your application has the controller and the action method but `config/routes.rb` doesn't have corresponds settings.
79
- 2. Missing action methods
80
- - Your application's `config/routes.rb` has routing declaration but no controller has a correspond action method.
81
-
82
110
  ## Motivation
83
111
 
84
- I believe most Rails developers write request specs instead of routing specs, and you might wonder what's worth to automate routing spec. Having said that, I can come up with some use-cases of this gem.
112
+ I believe most Rails developers write request specs instead of routing specs, and you might wonder what's worth to automate routing specs. Having said that, I can come up with some use-cases of this gem.
85
113
 
86
114
  1. When your project is kinda aged and none knows which route is alive and which one is dead.
87
115
  - => You can detect dead code by using this gem.
88
116
  2. When your application doesn't have enough request specs (even controller specs).
89
117
  - => This gem could be a good start point to increase tests to ensure routing is valid.
90
- 3. When you try to make big refactor of `config/routes.rb`.
91
- - => It's burden to run all request specs during refactoring. This could save your time.
118
+ 3. When you try to make a big refactor of `config/routes.rb`.
119
+ - => It's a burden to run all request specs during refactoring. This could save your time.
92
120
  4. When you're compelled to write routing specs by any pressure. ;-)
93
121
  - => Set you free from tedious work!
94
122
 
95
-
96
123
  ## License
97
124
 
98
125
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
99
126
 
100
127
  ## Code of Conduct
101
128
 
102
- Everyone interacting in the RouteMechanic project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/route_mechanic/blob/master/CODE_OF_CONDUCT.md).
129
+ Everyone interacting in the RouteMechanic project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/route_mechanic/blob/master/CODE_OF_CONDUCT.md).
@@ -1,49 +1,20 @@
1
- require 'route_mechanic/testing/methods'
2
- require 'rspec/matchers/composable'
1
+ require 'route_mechanic/rspec/matchers/have_valid_routes'
2
+ require 'route_mechanic/rspec/matchers/have_no_unused_actions'
3
+ require 'route_mechanic/rspec/matchers/have_no_unused_routes'
3
4
 
4
5
  module RouteMechanic
5
6
  module RSpec
6
7
  module Matchers
7
- class HaveValidRoutes
8
- include ::RSpec::Matchers::Composable
9
- include RouteMechanic::Testing::Methods
10
-
11
- # @param [Rails::Application] expected
12
- def initialize(expected)
13
- @expected = expected
14
- end
15
-
16
- def matches?(_actual)
17
- # assert_recognizes does not consider ActionController::RoutingError an
18
- # assertion failure, so we have to capture that and Assertion here.
19
- match_unless_raises Minitest::Assertion, ActiveSupport::TestCase::Assertion, ActionController::RoutingError do
20
- assert_all_routes(@expected)
21
- end
22
- end
23
-
24
- def failure_message
25
- @rescued_exception.message
26
- end
27
-
28
- def description
29
- "have valid routes"
30
- end
31
-
32
- private
8
+ def have_valid_routes(application=Rails.application)
9
+ HaveValidRoutes.new(application)
10
+ end
33
11
 
34
- def match_unless_raises(*exceptions)
35
- exceptions.unshift Exception if exceptions.empty?
36
- begin
37
- yield
38
- true
39
- rescue *exceptions => @rescued_exception
40
- false
41
- end
42
- end
12
+ def have_no_unused_actions(application=Rails.application)
13
+ HaveNoUnusedActions.new(application)
43
14
  end
44
15
 
45
- def have_valid_routes(application=Rails.application)
46
- HaveValidRoutes.new(application)
16
+ def have_no_unused_routes(application=Rails.application)
17
+ HaveNoUnusedRoutes.new(application)
47
18
  end
48
19
  end
49
20
  end
@@ -0,0 +1,42 @@
1
+ require 'route_mechanic/testing/methods'
2
+ require 'rspec/matchers/composable'
3
+
4
+ module RouteMechanic
5
+ module RSpec
6
+ module Matchers
7
+ class BaseMatcher
8
+ include ::RSpec::Matchers::Composable
9
+ include RouteMechanic::Testing::Methods
10
+
11
+ # @param [Rails::Application] expected
12
+ def initialize(expected)
13
+ @expected = expected
14
+ end
15
+
16
+ def matches?(_actual)
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def failure_message
21
+ @rescued_exception.message
22
+ end
23
+
24
+ def description
25
+ raise NotImplementedError
26
+ end
27
+
28
+ private
29
+
30
+ def match_unless_raises(*exceptions)
31
+ exceptions.unshift Exception if exceptions.empty?
32
+ begin
33
+ yield
34
+ true
35
+ rescue *exceptions => @rescued_exception
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ require 'route_mechanic/rspec/matchers/base_matcher'
2
+
3
+ module RouteMechanic
4
+ module RSpec
5
+ module Matchers
6
+ class HaveNoUnusedActions < BaseMatcher
7
+ def matches?(_actual)
8
+ # assert_recognizes does not consider ActionController::RoutingError an
9
+ # assertion failure, so we have to capture that and Assertion here.
10
+ match_unless_raises Minitest::Assertion, ActiveSupport::TestCase::Assertion, ActionController::RoutingError do
11
+ assert_no_unused_actions(@expected)
12
+ end
13
+ end
14
+
15
+ def description
16
+ "have no unused actions"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'route_mechanic/rspec/matchers/base_matcher'
2
+
3
+ module RouteMechanic
4
+ module RSpec
5
+ module Matchers
6
+ class HaveNoUnusedRoutes < BaseMatcher
7
+ def matches?(_actual)
8
+ # assert_recognizes does not consider ActionController::RoutingError an
9
+ # assertion failure, so we have to capture that and Assertion here.
10
+ match_unless_raises Minitest::Assertion, ActiveSupport::TestCase::Assertion, ActionController::RoutingError do
11
+ assert_no_unused_routes(@expected)
12
+ end
13
+ end
14
+
15
+ def description
16
+ "have no unused routes"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'route_mechanic/rspec/matchers/base_matcher'
2
+
3
+ module RouteMechanic
4
+ module RSpec
5
+ module Matchers
6
+ class HaveValidRoutes < BaseMatcher
7
+ def matches?(_actual)
8
+ # assert_recognizes does not consider ActionController::RoutingError an
9
+ # assertion failure, so we have to capture that and Assertion here.
10
+ match_unless_raises Minitest::Assertion, ActiveSupport::TestCase::Assertion, ActionController::RoutingError do
11
+ assert_all_routes(@expected)
12
+ end
13
+ end
14
+
15
+ def description
16
+ "have valid routes"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -3,7 +3,7 @@ require "route_mechanic/testing/error_inspector"
3
3
  module RouteMechanic
4
4
  module Testing
5
5
  class ErrorAggregator
6
- attr_reader :controller_routes_errors, :config_routes_errors
6
+ attr_reader :unused_actions_errors, :unused_routes_errors
7
7
 
8
8
  # @param [Array<ActionDispatch::Journey::Route>] routes
9
9
  # @param [Array<Controller>] controllers
@@ -12,31 +12,36 @@ module RouteMechanic
12
12
  @controllers = controllers
13
13
  @config_routes = []
14
14
  @controller_routes = []
15
- @config_routes_errors = []
16
- @controller_routes_errors = []
15
+ @unused_routes_errors = []
16
+ @unused_actions_errors = []
17
17
  end
18
18
 
19
- def aggregate
20
- collect_controller_routes_errors
21
- collect_config_routes_errors
19
+ # @param [Boolean] unused_actions
20
+ # @param [Boolean] unused_routes
21
+ def aggregate(unused_actions: true, unused_routes: true)
22
+ collect_unused_actions_errors(unused_actions)
23
+ collect_unused_routes_errors(unused_routes)
22
24
  self
23
25
  end
24
26
 
27
+ # @return [Array<ActionDispatch::Journey::Route>]
25
28
  def all_routes
26
29
  @config_routes + @controller_routes
27
30
  end
28
31
 
32
+ # @return [Boolean]
29
33
  def no_error?
30
- [@config_routes_errors, @controller_routes_errors].all?(&:empty?)
34
+ [@unused_routes_errors, @unused_actions_errors].all?(&:empty?)
31
35
  end
32
36
 
37
+ # @return [String]
33
38
  def error_message
34
39
  ErrorInspector.new(self).message
35
40
  end
36
41
 
37
42
  private
38
43
 
39
- def collect_controller_routes_errors
44
+ def collect_unused_actions_errors(report_error)
40
45
  @controllers.each do |controller|
41
46
  controller_path = controller.controller_path
42
47
  controller.action_methods.each do |action_method|
@@ -45,7 +50,7 @@ module RouteMechanic
45
50
  end
46
51
 
47
52
  if journey_routes.empty?
48
- @controller_routes_errors << { controller: controller, action: action_method }
53
+ @unused_actions_errors << { controller: controller, action: action_method } if report_error
49
54
  else
50
55
  wrappers = journey_routes.map { |r| RouteWrapper.new(r) }
51
56
  @controller_routes.concat(wrappers)
@@ -54,7 +59,7 @@ module RouteMechanic
54
59
  end
55
60
  end
56
61
 
57
- def collect_config_routes_errors
62
+ def collect_unused_routes_errors(report_error)
58
63
  @routes.each do |journey_route|
59
64
  wrapper = RouteWrapper.new journey_route
60
65
  @config_routes << wrapper
@@ -63,7 +68,7 @@ module RouteMechanic
63
68
  wrapper.controller == w.controller && wrapper.action == w.action && wrapper.path == w.path
64
69
  end
65
70
 
66
- @config_routes_errors << wrapper unless matched_controller_exist
71
+ @unused_routes_errors << wrapper if !matched_controller_exist && report_error
67
72
  end
68
73
  end
69
74
  end
@@ -4,7 +4,7 @@ module RouteMechanic
4
4
  module Testing
5
5
  class ErrorInspector
6
6
  extend Forwardable
7
- def_delegators :@aggregator, :controller_routes_errors, :config_routes_errors
7
+ def_delegators :@aggregator, :unused_actions_errors, :unused_routes_errors
8
8
 
9
9
  # @param [RouteMechanic::Testing::ErrorAggregator] aggregator
10
10
  def initialize(aggregator)
@@ -15,27 +15,26 @@ module RouteMechanic
15
15
  def message
16
16
  buffer = []
17
17
 
18
- if controller_routes_errors.present?
18
+ if unused_actions_errors.present?
19
19
  buffer << " No route matches to the controllers and action methods below"
20
- buffer << controller_routes_errors.map {|r| " #{r[:controller]}##{r[:action]}" }
20
+ buffer << unused_actions_errors.map {|r| " #{r[:controller]}##{r[:action]}" }
21
21
  end
22
22
 
23
- if config_routes_errors.present?
23
+ if unused_routes_errors.present?
24
24
  verb_width, path_width = widths
25
25
  buffer << " No controller and action matches to the routes below"
26
- buffer << config_routes_errors.map { |w| " #{w.verb.ljust(verb_width)} #{w.path.ljust(path_width)} #{w.reqs}" }
27
- buffer << "\n"
26
+ buffer << unused_routes_errors.map { |w| " #{w.verb.ljust(verb_width)} #{w.path.ljust(path_width)} #{w.reqs}" }
28
27
  end
29
28
 
30
- ["[Route Mechanic]", buffer].join("\n")
29
+ ["[Route Mechanic]", buffer].join("\n") + "\n"
31
30
  end
32
31
 
33
32
  private
34
33
 
35
34
  def widths
36
35
  [
37
- config_routes_errors.map { |w| w.verb.length }.max || 0,
38
- config_routes_errors.map { |w| w.path.length }.max || 0
36
+ unused_routes_errors.map { |w| w.verb.length }.max || 0,
37
+ unused_routes_errors.map { |w| w.path.length }.max || 0
39
38
  ]
40
39
  end
41
40
  end
@@ -14,20 +14,41 @@ module RouteMechanic
14
14
  # @param [Rails::Application] application
15
15
  # @raise [Minitest::Assertion]
16
16
  def assert_all_routes(application=Rails.application)
17
+ assert_targets(application, unused_actions: true, unused_routes: true)
18
+ end
19
+
20
+ # @param [Rails::Application] application
21
+ # @raise [Minitest::Assertion]
22
+ def assert_no_unused_actions(application=Rails.application)
23
+ assert_targets(application, unused_actions: true, unused_routes: false)
24
+ end
25
+
26
+ # @param [Rails::Application] application
27
+ # @raise [Minitest::Assertion]
28
+ def assert_no_unused_routes(application=Rails.application)
29
+ assert_targets(application, unused_actions: false, unused_routes: true)
30
+ end
31
+
32
+ private
33
+
34
+ # @param [Rails::Application] application
35
+ # @param [Boolean] unused_actions
36
+ # @param [Boolean] unused_routes
37
+ # @raise [Minitest::Assertion]
38
+ def assert_targets(application, unused_actions:, unused_routes:)
17
39
  @application = application
18
40
 
19
41
  # Instead of including ActionController::TestCase::Behavior, set up
20
42
  # https://github.com/rails/rails/blob/5b6aa8c20a3abfd6274c83f196abf73cacb3400b/actionpack/lib/action_controller/test_case.rb#L519-L520
21
43
  @controller = nil unless defined? @controller
22
44
 
23
- aggregator = ErrorAggregator.new(target_routes, controllers).aggregate
45
+ aggregator = ErrorAggregator.new(target_routes, controllers).aggregate(
46
+ unused_actions: unused_actions, unused_routes: unused_routes)
24
47
  aggregator.all_routes.each { |wrapper| assert_routes(wrapper) }
25
48
 
26
49
  assert(aggregator.no_error?, ->{ aggregator.error_message })
27
50
  end
28
51
 
29
- private
30
-
31
52
  def routes
32
53
  # assert_routing expect @routes to exists as like this class inherits ActionController::TestCase.
33
54
  # If user already defines @routes, do not override
@@ -1,3 +1,3 @@
1
1
  module RouteMechanic
2
- VERSION = "0.1.6"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: route_mechanic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ohbarye
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-22 00:00:00.000000000 Z
11
+ date: 2020-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -75,6 +75,10 @@ files:
75
75
  - fixtures/fake_app/rails_app.rb
76
76
  - lib/route_mechanic.rb
77
77
  - lib/route_mechanic/rspec/matchers.rb
78
+ - lib/route_mechanic/rspec/matchers/base_matcher.rb
79
+ - lib/route_mechanic/rspec/matchers/have_no_unused_actions.rb
80
+ - lib/route_mechanic/rspec/matchers/have_no_unused_routes.rb
81
+ - lib/route_mechanic/rspec/matchers/have_valid_routes.rb
78
82
  - lib/route_mechanic/testing/error_aggregator.rb
79
83
  - lib/route_mechanic/testing/error_inspector.rb
80
84
  - lib/route_mechanic/testing/methods.rb