rubocop-nueca 1.1.6 → 1.2.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 +4 -4
- data/.github/workflows/publish.yml +5 -1
- data/.github/workflows/rubocop.yml +23 -0
- data/lib/rubocop/cop/rails/route_consistent_spacing.rb +9 -21
- data/lib/rubocop/cop/rails/route_grouping.rb +9 -21
- data/lib/rubocop/cop/rails/route_root_position.rb +13 -19
- data/lib/rubocop/cop/rails/route_separation.rb +9 -21
- data/lib/rubocop/cop/rails/route_sorting.rb +9 -21
- data/lib/rubocop/cop/shared/collection_context.rb +87 -5
- data/lib/rubocop/cop/shared/route_helper.rb +68 -0
- data/lib/rubocop/nueca/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3678eac28f6575b9cee5fb58f88a02e46de0c91bf36437a242fc34208cd4da3
|
4
|
+
data.tar.gz: a11bbc18d306047a0b8c1b329430b15d92bff287190adcd99e9994eceb4d33d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5ab9039fb41adbeb81026aa2d7bfa8f4f542c6566f01c0c853fce78ee6b987919750c983524eef7cc467902f7b6b1e7921a542ea48c1ff624d8eece8d96e5f1
|
7
|
+
data.tar.gz: '078491a8477ad8a25a19c0a6a6e38541f599cc5d44efc35ec24974e86eec267fd1026c5517063367e7a64163f595014f1d912d6cc467f1fd828c7b044b2eb550'
|
@@ -1,12 +1,16 @@
|
|
1
1
|
name: Publish Gem
|
2
2
|
|
3
3
|
on:
|
4
|
-
|
4
|
+
workflow_run:
|
5
|
+
workflows: ["RuboCop"]
|
6
|
+
types:
|
7
|
+
- completed
|
5
8
|
branches: [ master ]
|
6
9
|
|
7
10
|
jobs:
|
8
11
|
publish:
|
9
12
|
runs-on: ubuntu-latest
|
13
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
10
14
|
|
11
15
|
steps:
|
12
16
|
- uses: actions/checkout@v4
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: RuboCop
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
rubocop:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v4
|
15
|
+
|
16
|
+
- name: Set up Ruby
|
17
|
+
uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: '3.4'
|
20
|
+
bundler-cache: true
|
21
|
+
|
22
|
+
- name: Run RuboCop
|
23
|
+
run: bundle exec rubocop
|
@@ -1,39 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../shared/
|
3
|
+
require_relative '../shared/route_helper'
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Rails
|
8
8
|
class RouteConsistentSpacing < RuboCop::Cop::Base
|
9
|
+
include RouteHelper
|
10
|
+
|
9
11
|
MSG = 'Do not leave blank lines between routes of the same type at the same namespace level.'
|
10
12
|
|
11
13
|
def on_block(node)
|
12
|
-
|
13
|
-
|
14
|
-
routes = collect_routes(node)
|
15
|
-
return if routes.size < 2
|
14
|
+
process_route_block(node)
|
15
|
+
end
|
16
16
|
|
17
|
-
|
17
|
+
def investigate(processed_source)
|
18
|
+
process_route_file(processed_source)
|
18
19
|
end
|
19
20
|
|
20
21
|
private
|
21
22
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
send_node = node.send_node
|
26
|
-
receiver = send_node.receiver
|
27
|
-
return false unless receiver
|
28
|
-
|
29
|
-
receiver.source == 'Rails.application.routes' && send_node.method_name == :draw
|
30
|
-
end
|
31
|
-
|
32
|
-
def collect_routes(routes_block)
|
33
|
-
collector = RouteCollector.new
|
34
|
-
body = routes_block.body
|
35
|
-
collector.collect(body) if body
|
36
|
-
collector.routes.sort_by { |route| route[:line] }
|
23
|
+
def check_routes(routes)
|
24
|
+
check_consistent_spacing(routes)
|
37
25
|
end
|
38
26
|
|
39
27
|
def check_consistent_spacing(routes)
|
@@ -1,39 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../shared/
|
3
|
+
require_relative '../shared/route_helper'
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Rails
|
8
8
|
class RouteGrouping < RuboCop::Cop::Base
|
9
|
+
include RouteHelper
|
10
|
+
|
9
11
|
MSG = 'Group routes by type. Keep simple routes, resources, and namespaces grouped together.'
|
10
12
|
|
11
13
|
def on_block(node)
|
12
|
-
|
13
|
-
|
14
|
-
routes = collect_routes(node)
|
15
|
-
return if routes.size < 2
|
14
|
+
process_route_block(node)
|
15
|
+
end
|
16
16
|
|
17
|
-
|
17
|
+
def investigate(processed_source)
|
18
|
+
process_route_file(processed_source)
|
18
19
|
end
|
19
20
|
|
20
21
|
private
|
21
22
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
send_node = node.send_node
|
26
|
-
receiver = send_node.receiver
|
27
|
-
return false unless receiver
|
28
|
-
|
29
|
-
receiver.source == 'Rails.application.routes' && send_node.method_name == :draw
|
30
|
-
end
|
31
|
-
|
32
|
-
def collect_routes(routes_block)
|
33
|
-
collector = RouteCollector.new
|
34
|
-
body = routes_block.body
|
35
|
-
collector.collect(body) if body
|
36
|
-
collector.routes.sort_by { |route| route[:line] }
|
23
|
+
def check_routes(routes)
|
24
|
+
check_for_scattered_routes(routes)
|
37
25
|
end
|
38
26
|
|
39
27
|
def check_for_scattered_routes(routes)
|
@@ -1,39 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../shared/
|
3
|
+
require_relative '../shared/route_helper'
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Rails
|
8
8
|
class RouteRootPosition < RuboCop::Cop::Base
|
9
|
+
include RouteHelper
|
10
|
+
|
9
11
|
MSG = 'The root route should be positioned at the top of routes within the same namespace level.'
|
10
12
|
|
11
13
|
def on_block(node)
|
12
|
-
|
13
|
-
|
14
|
-
routes = collect_routes(node)
|
15
|
-
return if routes.empty?
|
14
|
+
process_route_block(node)
|
15
|
+
end
|
16
16
|
|
17
|
-
|
17
|
+
def investigate(processed_source)
|
18
|
+
process_route_file(processed_source)
|
18
19
|
end
|
19
20
|
|
20
21
|
private
|
21
22
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
send_node = node.send_node
|
26
|
-
receiver = send_node.receiver
|
27
|
-
return false unless receiver
|
28
|
-
|
29
|
-
receiver.source == 'Rails.application.routes' && send_node.method_name == :draw
|
23
|
+
def minimum_routes_for_check
|
24
|
+
0
|
30
25
|
end
|
31
26
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
collector.routes.sort_by { |route| route[:line] }
|
27
|
+
def check_routes(routes)
|
28
|
+
return if routes.empty?
|
29
|
+
|
30
|
+
check_root_position(routes)
|
37
31
|
end
|
38
32
|
|
39
33
|
def check_root_position(routes)
|
@@ -1,39 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../shared/
|
3
|
+
require_relative '../shared/route_helper'
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Rails
|
8
8
|
class RouteSeparation < RuboCop::Cop::Base
|
9
|
+
include RouteHelper
|
10
|
+
|
9
11
|
MSG = 'Separate different route types with a blank line.'
|
10
12
|
|
11
13
|
def on_block(node)
|
12
|
-
|
13
|
-
|
14
|
-
routes = collect_routes(node)
|
15
|
-
return if routes.size < 2
|
14
|
+
process_route_block(node)
|
15
|
+
end
|
16
16
|
|
17
|
-
|
17
|
+
def investigate(processed_source)
|
18
|
+
process_route_file(processed_source)
|
18
19
|
end
|
19
20
|
|
20
21
|
private
|
21
22
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
send_node = node.send_node
|
26
|
-
receiver = send_node.receiver
|
27
|
-
return false unless receiver
|
28
|
-
|
29
|
-
receiver.source == 'Rails.application.routes' && send_node.method_name == :draw
|
30
|
-
end
|
31
|
-
|
32
|
-
def collect_routes(routes_block)
|
33
|
-
collector = RouteCollector.new
|
34
|
-
body = routes_block.body
|
35
|
-
collector.collect(body) if body
|
36
|
-
collector.routes.sort_by { |route| route[:line] }
|
23
|
+
def check_routes(routes)
|
24
|
+
check_route_separation(routes)
|
37
25
|
end
|
38
26
|
|
39
27
|
def check_route_separation(routes)
|
@@ -1,40 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../shared/
|
3
|
+
require_relative '../shared/route_helper'
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module Rails
|
8
8
|
class RouteSorting < RuboCop::Cop::Base
|
9
|
+
include RouteHelper
|
10
|
+
|
9
11
|
MSG = 'Sort routes of the same type alphabetically within the same namespace level. ' \
|
10
12
|
'Expected order: %<expected>s.'
|
11
13
|
|
12
14
|
def on_block(node)
|
13
|
-
|
14
|
-
|
15
|
-
routes = collect_routes(node)
|
16
|
-
return if routes.size < 2
|
15
|
+
process_route_block(node)
|
16
|
+
end
|
17
17
|
|
18
|
-
|
18
|
+
def investigate(processed_source)
|
19
|
+
process_route_file(processed_source)
|
19
20
|
end
|
20
21
|
|
21
22
|
private
|
22
23
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
send_node = node.send_node
|
27
|
-
receiver = send_node.receiver
|
28
|
-
return false unless receiver
|
29
|
-
|
30
|
-
receiver.source == 'Rails.application.routes' && send_node.method_name == :draw
|
31
|
-
end
|
32
|
-
|
33
|
-
def collect_routes(routes_block)
|
34
|
-
collector = RouteCollector.new
|
35
|
-
body = routes_block.body
|
36
|
-
collector.collect(body) if body
|
37
|
-
collector.routes.sort_by { |route| route[:line] }
|
24
|
+
def check_routes(routes)
|
25
|
+
check_route_sorting(routes)
|
38
26
|
end
|
39
27
|
|
40
28
|
def check_route_sorting(routes)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
class CollectionContext
|
6
|
+
class CollectionContext # rubocop:disable Metrics/ClassLength
|
7
7
|
ROUTE_CATEGORIES = {
|
8
8
|
simple: [:get, :post, :put, :patch, :delete, :head, :options, :match, :root],
|
9
9
|
resource: [:resource, :resources],
|
@@ -12,6 +12,7 @@ module RuboCop
|
|
12
12
|
}.freeze
|
13
13
|
|
14
14
|
ALL_ROUTE_METHODS = ROUTE_CATEGORIES.values.flatten.freeze
|
15
|
+
SCOPE_OPTIONS = [:module, :path].freeze
|
15
16
|
|
16
17
|
def initialize(collector, namespace_level, namespace_path = [])
|
17
18
|
@collector = collector
|
@@ -43,19 +44,40 @@ module RuboCop
|
|
43
44
|
send_node = node.send_node
|
44
45
|
return unless send_node.send_type? && route_method?(send_node)
|
45
46
|
|
46
|
-
|
47
|
+
add_route_block_if_valid(node)
|
48
|
+
process_nested_context(node)
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_route_block_if_valid(node)
|
52
|
+
send_node = node.send_node
|
53
|
+
return unless route_method?(send_node)
|
54
|
+
|
55
|
+
route_info = build_route_info_from_block(node)
|
56
|
+
@collector.add_route(route_info) if route_info
|
57
|
+
end
|
47
58
|
|
59
|
+
def process_nested_context(node)
|
48
60
|
body = node.body
|
49
61
|
return unless body
|
50
62
|
|
63
|
+
new_namespace_path = build_namespace_path(node.send_node)
|
64
|
+
nested_context = CollectionContext.new(@collector, @namespace_level + 1, new_namespace_path)
|
65
|
+
nested_context.process_node(body)
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_namespace_path(send_node)
|
51
69
|
new_namespace_path = @namespace_path.dup
|
52
|
-
|
70
|
+
|
71
|
+
case send_node.method_name
|
72
|
+
when :namespace
|
53
73
|
namespace_name = extract_namespace_name(send_node)
|
54
74
|
new_namespace_path << namespace_name if namespace_name
|
75
|
+
when :scope
|
76
|
+
scope_name = extract_scope_name(send_node)
|
77
|
+
new_namespace_path << scope_name if scope_name
|
55
78
|
end
|
56
79
|
|
57
|
-
|
58
|
-
nested_context.process_node(body)
|
80
|
+
new_namespace_path
|
59
81
|
end
|
60
82
|
|
61
83
|
def extract_namespace_name(node)
|
@@ -65,6 +87,46 @@ module RuboCop
|
|
65
87
|
'unknown'
|
66
88
|
end
|
67
89
|
|
90
|
+
def extract_scope_name(node)
|
91
|
+
scope_name = extract_scope_option_value(node)
|
92
|
+
return scope_name if scope_name
|
93
|
+
|
94
|
+
first_arg = node.arguments.first
|
95
|
+
return first_arg.value.to_s if first_arg&.sym_type? || first_arg&.str_type?
|
96
|
+
|
97
|
+
'scope'
|
98
|
+
end
|
99
|
+
|
100
|
+
def extract_scope_option_value(node)
|
101
|
+
node.arguments.each do |arg|
|
102
|
+
next unless arg.hash_type?
|
103
|
+
|
104
|
+
scope_value = find_scope_option_in_hash(arg)
|
105
|
+
return scope_value if scope_value
|
106
|
+
end
|
107
|
+
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
def find_scope_option_in_hash(hash_arg)
|
112
|
+
hash_arg.pairs.each do |pair|
|
113
|
+
value = extract_scope_value_from_pair(pair)
|
114
|
+
return value if value
|
115
|
+
end
|
116
|
+
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
def extract_scope_value_from_pair(pair)
|
121
|
+
key = pair.key
|
122
|
+
return nil unless key&.sym_type? && SCOPE_OPTIONS.include?(key.value)
|
123
|
+
|
124
|
+
value = pair.value
|
125
|
+
return value.value.to_s if value&.sym_type? || value&.str_type?
|
126
|
+
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
68
130
|
def route_method?(node)
|
69
131
|
return false unless node.send_type?
|
70
132
|
|
@@ -96,6 +158,26 @@ module RuboCop
|
|
96
158
|
}
|
97
159
|
end
|
98
160
|
|
161
|
+
def build_route_info_from_block(block_node)
|
162
|
+
send_node = block_node.send_node
|
163
|
+
method_name = send_node.method_name
|
164
|
+
route_name = extract_route_name(send_node)
|
165
|
+
return nil unless route_name
|
166
|
+
|
167
|
+
send_range = send_node.source_range
|
168
|
+
block_range = block_node.source_range
|
169
|
+
{
|
170
|
+
node: send_node,
|
171
|
+
method: method_name,
|
172
|
+
name: route_name,
|
173
|
+
line: send_range.line,
|
174
|
+
end_line: block_range.last_line,
|
175
|
+
namespace_level: @namespace_level,
|
176
|
+
namespace_path: @namespace_path.dup,
|
177
|
+
type: categorize_route_method(method_name)
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
99
181
|
def extract_route_name(node)
|
100
182
|
method_name = node.method_name
|
101
183
|
first_arg = node.arguments.first
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'route_collector'
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Cop
|
7
|
+
module Rails
|
8
|
+
module RouteHelper
|
9
|
+
def route_file?
|
10
|
+
processed_source.file_path&.end_with?('routes.rb')
|
11
|
+
end
|
12
|
+
|
13
|
+
def route_block?(node)
|
14
|
+
return false unless node.block_type?
|
15
|
+
|
16
|
+
send_node = node.send_node
|
17
|
+
return false unless send_node.send_type?
|
18
|
+
return true if send_node.receiver&.source == 'Rails.application.routes' && send_node.method_name == :draw
|
19
|
+
return false unless route_file?
|
20
|
+
|
21
|
+
[:scope, :namespace, :concern].include?(send_node.method_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def collect_routes(routes_block)
|
25
|
+
collector = RouteCollector.new
|
26
|
+
body = routes_block.body
|
27
|
+
collector.collect(body) if body
|
28
|
+
collector.routes.sort_by { |route| route[:line] }
|
29
|
+
end
|
30
|
+
|
31
|
+
def collect_routes_from_file(ast_node)
|
32
|
+
collector = RouteCollector.new
|
33
|
+
collector.collect(ast_node) if ast_node
|
34
|
+
collector.routes.sort_by { |route| route[:line] }
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_route_block(node)
|
38
|
+
return unless route_block?(node)
|
39
|
+
|
40
|
+
routes = collect_routes(node)
|
41
|
+
return if routes.size < minimum_routes_for_check
|
42
|
+
|
43
|
+
check_routes(routes)
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_route_file(processed_source)
|
47
|
+
return unless route_file?
|
48
|
+
return if processed_source.ast.nil?
|
49
|
+
|
50
|
+
routes = collect_routes_from_file(processed_source.ast)
|
51
|
+
return if routes.size < minimum_routes_for_check
|
52
|
+
|
53
|
+
check_routes(routes)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def minimum_routes_for_check
|
59
|
+
2
|
60
|
+
end
|
61
|
+
|
62
|
+
def check_routes(routes)
|
63
|
+
raise NotImplementedError, 'Subclasses must implement check_routes method'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-nueca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tien
|
@@ -134,6 +134,7 @@ extensions: []
|
|
134
134
|
extra_rdoc_files: []
|
135
135
|
files:
|
136
136
|
- ".github/workflows/publish.yml"
|
137
|
+
- ".github/workflows/rubocop.yml"
|
137
138
|
- CODE_OF_CONDUCT.md
|
138
139
|
- LICENSE.txt
|
139
140
|
- README.md
|
@@ -160,6 +161,7 @@ files:
|
|
160
161
|
- lib/rubocop/cop/rails/time_zone_today.rb
|
161
162
|
- lib/rubocop/cop/shared/collection_context.rb
|
162
163
|
- lib/rubocop/cop/shared/route_collector.rb
|
164
|
+
- lib/rubocop/cop/shared/route_helper.rb
|
163
165
|
- lib/rubocop/nueca/plugin.rb
|
164
166
|
- lib/rubocop/nueca/version.rb
|
165
167
|
homepage: https://github.com/tieeeeen1994/rubocop-nueca
|