route_mechanic 0.1.6 → 0.2.0

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
  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