route_mechanic 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +100 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/fixtures/fake_app/app/controllers/application_controller.rb +2 -0
- data/fixtures/fake_app/app/controllers/users_controller.rb +11 -0
- data/fixtures/fake_app/rails_app.rb +14 -0
- data/lib/route_mechanic.rb +7 -0
- data/lib/route_mechanic/rspec/matchers.rb +54 -0
- data/lib/route_mechanic/testing/error_aggregator.rb +71 -0
- data/lib/route_mechanic/testing/error_inspector.rb +43 -0
- data/lib/route_mechanic/testing/methods.rb +75 -0
- data/lib/route_mechanic/testing/minitest_assertion_adopter.rb +20 -0
- data/lib/route_mechanic/testing/route_wrapper.rb +36 -0
- data/lib/route_mechanic/version.rb +3 -0
- data/route_mechanic.gemspec +28 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ce0b5623dd35b2cb758cef52986509c7d95e4c66c15436b426effdf345b83309
|
4
|
+
data.tar.gz: 465c4b75e175e15e5827e2e394fcd553eada07d09cab9028d0975037beee8513
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3781e74689e0b7f02df2a8828e1ef5f37ce5be2b29201a7b565e2b66aacaefb980b8212684072308de15bb68d512e2cadae0eb7cbd8b38e68b732ed48136b729
|
7
|
+
data.tar.gz: 4f0ceb15174710c11abd26bb080186d1f085c51054970581d9840ea6f986d45904f1c4aa1013767afedadbc8fa9c42ff67c9140c5f884d4941f262f1747df06c
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at over.rye@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [https://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: https://contributor-covenant.org
|
74
|
+
[version]: https://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 ohbarye
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# RouteMechanic
|
2
|
+
|
3
|
+
No need to maintain Rails' routing tests manually. RouteMechanic automatically detects broken routes and missing action methods in controller once you've finished installation.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
group :test do
|
11
|
+
gem 'route_mechanic'
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
```shell
|
18
|
+
$ bundle install
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
RouteMechanic is available for both RSpec and MiniTest.
|
24
|
+
|
25
|
+
All you have to do is to add just one test case that keeps your application's routes not broken.
|
26
|
+
|
27
|
+
### RSpec
|
28
|
+
|
29
|
+
Just add a test file which has only one test case using `have_valid_routes` matcher.
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
RSpec.describe 'Rails.application', type: :routing do
|
33
|
+
it "fails if application does not have valid routes" do
|
34
|
+
expect(Rails.application.routes).to have_valid_routes
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
### MiniTest
|
40
|
+
|
41
|
+
Just add a test file like below.
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class RoutingTest < Minitest::Test
|
45
|
+
include ::RouteMechanic::Testing::Methods
|
46
|
+
|
47
|
+
def test_that_fake_app_has_correct_routes
|
48
|
+
assert_all_routes
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
### What if RouteMechanic detects broken routes?
|
54
|
+
|
55
|
+
It tells you broken routes as follows.
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
0) RouteMechanic::RSpec::Matchers fails if application does not have valid routes
|
59
|
+
Failure/Error: expect(Rails.application.routes).to have_valid_routes
|
60
|
+
|
61
|
+
[Route Mechanic]
|
62
|
+
No route matches to the controllers and action methods below
|
63
|
+
UsersController#unknown
|
64
|
+
No controller and action matches to the routes below
|
65
|
+
GET /users/:user_id/friends(.:format) users#friends
|
66
|
+
GET /users(.:format) users#index
|
67
|
+
DELETE /users/:id(.:format) users#destroy
|
68
|
+
# ./spec/rspec/matchers_spec.rb:8:in `block (2 levels) in <top (required)>'
|
69
|
+
|
70
|
+
1 examples, 1 failure, 0 passed
|
71
|
+
```
|
72
|
+
|
73
|
+
RouteMechanic reports 2 types of broken routes.
|
74
|
+
|
75
|
+
1. Missing routes
|
76
|
+
- Your application has the controller and the action method but `config/routes.rb` doesn't have corresponds settings.
|
77
|
+
2. Missing action methods
|
78
|
+
- Your application's `config/routes.rb` has routing declaration but no controller has a correspond action method.
|
79
|
+
|
80
|
+
## Motivation
|
81
|
+
|
82
|
+
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.
|
83
|
+
|
84
|
+
1. When your project is kinda aged and none knows which route is alive and which one is dead.
|
85
|
+
- => You can detect dead code by using this gem.
|
86
|
+
2. When your application doesn't have enough request specs (even controller specs).
|
87
|
+
- => This gem could be a good start point to increase tests to ensure routing is valid.
|
88
|
+
3. When you try to make big refactor of `config/routes.rb`.
|
89
|
+
- => It's burden to run all request specs during refactoring. This could save your time.
|
90
|
+
4. When you're compelled to write routing specs by any pressure. ;-)
|
91
|
+
- => Set you free from tedious work!
|
92
|
+
|
93
|
+
|
94
|
+
## License
|
95
|
+
|
96
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
97
|
+
|
98
|
+
## Code of Conduct
|
99
|
+
|
100
|
+
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).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "route_mechanic"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails/application'
|
2
|
+
require "action_controller/railtie"
|
3
|
+
|
4
|
+
FakeApp = Class.new(Rails::Application)
|
5
|
+
FakeApp.config.eager_load = false
|
6
|
+
FakeApp.config.hosts << 'www.example.com' if FakeApp.config.respond_to?(:hosts)
|
7
|
+
FakeApp.config.root = File.dirname(__FILE__)
|
8
|
+
FakeApp.initialize!
|
9
|
+
|
10
|
+
FakeApp.routes.draw do
|
11
|
+
resources :users do
|
12
|
+
get 'friends', to: :friends
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'route_mechanic/testing/methods'
|
2
|
+
require 'rspec/matchers/composable'
|
3
|
+
|
4
|
+
module RouteMechanic
|
5
|
+
module RSpec
|
6
|
+
module Matchers
|
7
|
+
class HaveValidRoutes
|
8
|
+
include ::RSpec::Matchers::Composable
|
9
|
+
include RouteMechanic::Testing::Methods
|
10
|
+
|
11
|
+
# @param [ActionDispatch::Routing::RouteSet] 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
|
33
|
+
|
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
|
43
|
+
end
|
44
|
+
|
45
|
+
def have_valid_routes(routes=Rails.application.routes)
|
46
|
+
HaveValidRoutes.new(routes)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
RSpec.configure do |config|
|
53
|
+
config.include RouteMechanic::RSpec::Matchers, type: :routing
|
54
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "route_mechanic/testing/error_inspector"
|
2
|
+
|
3
|
+
module RouteMechanic
|
4
|
+
module Testing
|
5
|
+
class ErrorAggregator
|
6
|
+
attr_reader :controller_routes_errors, :config_routes_errors
|
7
|
+
|
8
|
+
# @param [Array<ActionDispatch::Journey::Route>] routes
|
9
|
+
# @param [Array<Controller>] controllers
|
10
|
+
def initialize(routes, controllers)
|
11
|
+
@routes = routes
|
12
|
+
@controllers = controllers
|
13
|
+
@config_routes = []
|
14
|
+
@controller_routes = []
|
15
|
+
@config_routes_errors = []
|
16
|
+
@controller_routes_errors = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def aggregate
|
20
|
+
collect_controller_routes_errors
|
21
|
+
collect_config_routes_errors
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def all_routes
|
26
|
+
@config_routes + @controller_routes
|
27
|
+
end
|
28
|
+
|
29
|
+
def no_error?
|
30
|
+
[@config_routes_errors, @controller_routes_errors].all?(&:empty?)
|
31
|
+
end
|
32
|
+
|
33
|
+
def error_message
|
34
|
+
ErrorInspector.new(self).message
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def collect_controller_routes_errors
|
40
|
+
@controllers.each do |controller|
|
41
|
+
controller_path = controller.controller_path
|
42
|
+
controller.action_methods.each do |action_method|
|
43
|
+
journey_route = @routes.detect do |route|
|
44
|
+
route.defaults[:controller].to_sym == controller_path.to_sym && route.defaults[:action].to_sym == action_method.to_sym
|
45
|
+
end
|
46
|
+
|
47
|
+
if journey_route
|
48
|
+
wrapper = RouteWrapper.new journey_route
|
49
|
+
@controller_routes << wrapper
|
50
|
+
else
|
51
|
+
@controller_routes_errors << { controller: controller, action: action_method }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def collect_config_routes_errors
|
58
|
+
@routes.each do |journey_route|
|
59
|
+
wrapper = RouteWrapper.new journey_route
|
60
|
+
@config_routes << wrapper
|
61
|
+
|
62
|
+
matched_controller_exist = @controller_routes.any? do |w|
|
63
|
+
wrapper.controller == w.controller && wrapper.action == w.action && wrapper.path == w.path
|
64
|
+
end
|
65
|
+
|
66
|
+
@config_routes_errors << wrapper unless matched_controller_exist
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module RouteMechanic
|
4
|
+
module Testing
|
5
|
+
class ErrorInspector
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@aggregator, :controller_routes_errors, :config_routes_errors
|
8
|
+
|
9
|
+
# @param [RouteMechanic::Testing::ErrorAggregator] aggregator
|
10
|
+
def initialize(aggregator)
|
11
|
+
@aggregator = aggregator
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
def message
|
16
|
+
buffer = []
|
17
|
+
|
18
|
+
if controller_routes_errors.present?
|
19
|
+
buffer << " No route matches to the controllers and action methods below"
|
20
|
+
buffer << controller_routes_errors.map {|r| " #{r[:controller]}##{r[:action]}" }
|
21
|
+
end
|
22
|
+
|
23
|
+
if config_routes_errors.present?
|
24
|
+
verb_width, path_width = widths
|
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"
|
28
|
+
end
|
29
|
+
|
30
|
+
["[Route Mechanic]", buffer].join("\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def widths
|
36
|
+
[
|
37
|
+
config_routes_errors.map { |w| w.verb.length }.max || 0,
|
38
|
+
config_routes_errors.map { |w| w.path.length }.max || 0
|
39
|
+
]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "route_mechanic/testing/route_wrapper"
|
2
|
+
require "route_mechanic/testing/error_aggregator"
|
3
|
+
require "route_mechanic/testing/minitest_assertion_adopter"
|
4
|
+
require "action_controller"
|
5
|
+
require "action_controller/test_case"
|
6
|
+
|
7
|
+
module RouteMechanic
|
8
|
+
module Testing
|
9
|
+
module Methods
|
10
|
+
include MinitestAssertionAdapter if defined?(RSpec)
|
11
|
+
include ActionDispatch::Assertions
|
12
|
+
|
13
|
+
# @param [ActionDispatch::Routing::RouteSet] routes
|
14
|
+
# @raise [Minitest::Assertion]
|
15
|
+
def assert_all_routes(routes=Rails.application.routes)
|
16
|
+
# assert_routing expect @routes to exists as like this class inherits ActionController::TestCase.
|
17
|
+
# If user already defines @routes, do not override
|
18
|
+
@routes ||= routes
|
19
|
+
|
20
|
+
# Instead of including ActionController::TestCase::Behavior, set up
|
21
|
+
# https://github.com/rails/rails/blob/5b6aa8c20a3abfd6274c83f196abf73cacb3400b/actionpack/lib/action_controller/test_case.rb#L519-L520
|
22
|
+
@controller = nil unless defined? @controller
|
23
|
+
|
24
|
+
aggregator = ErrorAggregator.new(target_routes, controllers).aggregate
|
25
|
+
aggregator.all_routes.each { |wrapper| assert_routes(wrapper) }
|
26
|
+
|
27
|
+
assert(aggregator.no_error?, ->{ aggregator.error_message })
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# @param [RouteMechanic::Testing::RouteWrapper] wrapper
|
33
|
+
# @raise [Minitest::Assertion]
|
34
|
+
def assert_routes(wrapper)
|
35
|
+
required_parts = wrapper.required_parts.reduce({}) do |memo, required_part|
|
36
|
+
memo.merge({ required_part => '1' }) # '1' is pseudo id
|
37
|
+
end
|
38
|
+
|
39
|
+
base_option = { controller: wrapper.controller, action: wrapper.action }
|
40
|
+
url = @routes.url_helpers.url_for(
|
41
|
+
base_option.merge({ only_path: true }).merge(required_parts))
|
42
|
+
expected_options = base_option.merge(required_parts)
|
43
|
+
|
44
|
+
assert_routing({ path: url, method: wrapper.verb }, expected_options)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Array<Controller>]
|
48
|
+
def controllers
|
49
|
+
eager_load_controllers
|
50
|
+
ApplicationController.descendants
|
51
|
+
end
|
52
|
+
|
53
|
+
# In RAILS_ENV=test, eager load is false and `ApplicationController.descendants` might be empty.
|
54
|
+
# So it needs to load all controllers. To shorten loading time, it loads only controllers.
|
55
|
+
# If complicated controllers path is used, use Rails.application.eager_load! instead.
|
56
|
+
def eager_load_controllers
|
57
|
+
load_path = "#{Rails.root.join('app/controllers')}"
|
58
|
+
relname_range = (load_path.to_s.length + 1)...-3
|
59
|
+
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
|
60
|
+
require_dependency file[relname_range]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Array<ActionDispatch::Journey::Route>]
|
65
|
+
def target_routes
|
66
|
+
@routes.routes.reject do |journey_route|
|
67
|
+
# Skip internals, endpoints that Rails adds by default
|
68
|
+
# Also Engines should be skipped since Engine's tests should be done in Engine
|
69
|
+
wrapper = RouteWrapper.new(journey_route)
|
70
|
+
wrapper.internal? || wrapper.required_defaults.empty? || wrapper.path.start_with?('/rails/')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "minitest/assertions"
|
2
|
+
|
3
|
+
module RouteMechanic
|
4
|
+
module Testing
|
5
|
+
module Methods
|
6
|
+
# @private
|
7
|
+
module MinitestCounters
|
8
|
+
attr_writer :assertions
|
9
|
+
def assertions
|
10
|
+
@assertions ||= 0
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module MinitestAssertionAdapter
|
15
|
+
include Minitest::Assertions
|
16
|
+
include MinitestCounters
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
module RouteMechanic
|
4
|
+
module Testing
|
5
|
+
# This class just wraps ActionDispatch::Journey::Route
|
6
|
+
class RouteWrapper < SimpleDelegator
|
7
|
+
def endpoint
|
8
|
+
app.dispatcher? ? "#{controller}##{action}" : app.rack_app.inspect
|
9
|
+
end
|
10
|
+
|
11
|
+
def path
|
12
|
+
super.spec.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def reqs
|
16
|
+
@reqs ||= begin
|
17
|
+
reqs = endpoint
|
18
|
+
reqs += " #{requirements.except(:controller, :action)}" unless requirements.except(:controller, :action).empty?
|
19
|
+
reqs
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def controller
|
24
|
+
parts.include?(:controller) ? ":controller" : requirements[:controller]
|
25
|
+
end
|
26
|
+
|
27
|
+
def action
|
28
|
+
parts.include?(:action) ? ":action" : requirements[:action]
|
29
|
+
end
|
30
|
+
|
31
|
+
def internal?
|
32
|
+
internal
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'lib/route_mechanic/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "route_mechanic"
|
5
|
+
spec.version = RouteMechanic::VERSION
|
6
|
+
spec.authors = ["ohbarye"]
|
7
|
+
spec.email = ["over.rye@gmail.com"]
|
8
|
+
|
9
|
+
spec.summary = %q{RouteMechanic detects broken routes with ease}
|
10
|
+
spec.description = %q{No need to maintain Rails' routing tests manually. RouteMechanic automatically detects broken routes and missing action methods in controller once you've finished installation.}
|
11
|
+
spec.homepage = "https://github.com/ohbarye/route_machanic"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/ohbarye/route_machanic"
|
17
|
+
|
18
|
+
# Specify which files should be added to the gem when it is released.
|
19
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
20
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
+
end
|
23
|
+
spec.bindir = "exe"
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
|
27
|
+
spec.add_runtime_dependency "actionpack", ">= 4.2", "< 6.1"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: route_mechanic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ohbarye
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-09-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '6.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '6.1'
|
33
|
+
description: No need to maintain Rails' routing tests manually. RouteMechanic automatically
|
34
|
+
detects broken routes and missing action methods in controller once you've finished
|
35
|
+
installation.
|
36
|
+
email:
|
37
|
+
- over.rye@gmail.com
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- ".gitignore"
|
43
|
+
- ".rspec"
|
44
|
+
- ".travis.yml"
|
45
|
+
- CODE_OF_CONDUCT.md
|
46
|
+
- Gemfile
|
47
|
+
- LICENSE.txt
|
48
|
+
- README.md
|
49
|
+
- Rakefile
|
50
|
+
- bin/console
|
51
|
+
- bin/setup
|
52
|
+
- fixtures/fake_app/app/controllers/application_controller.rb
|
53
|
+
- fixtures/fake_app/app/controllers/users_controller.rb
|
54
|
+
- fixtures/fake_app/rails_app.rb
|
55
|
+
- lib/route_mechanic.rb
|
56
|
+
- lib/route_mechanic/rspec/matchers.rb
|
57
|
+
- lib/route_mechanic/testing/error_aggregator.rb
|
58
|
+
- lib/route_mechanic/testing/error_inspector.rb
|
59
|
+
- lib/route_mechanic/testing/methods.rb
|
60
|
+
- lib/route_mechanic/testing/minitest_assertion_adopter.rb
|
61
|
+
- lib/route_mechanic/testing/route_wrapper.rb
|
62
|
+
- lib/route_mechanic/version.rb
|
63
|
+
- route_mechanic.gemspec
|
64
|
+
homepage: https://github.com/ohbarye/route_machanic
|
65
|
+
licenses:
|
66
|
+
- MIT
|
67
|
+
metadata:
|
68
|
+
homepage_uri: https://github.com/ohbarye/route_machanic
|
69
|
+
source_code_uri: https://github.com/ohbarye/route_machanic
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.3.0
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubygems_version: 3.1.2
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: RouteMechanic detects broken routes with ease
|
89
|
+
test_files: []
|