rubocop-nueca 1.1.5 → 1.2.2

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: 6d780fbfedb280b9cb1348a3559a27639ea9139b726de9de211d2fb8bc908dc8
4
- data.tar.gz: 90b26a394731353973252722260e66f080fab38d6f94ccea5da2f9ac43caca72
3
+ metadata.gz: a49b9e5e060e7ec154ef9feaf553325fb156d58c59aba67ea3cc2ac1e1e13b67
4
+ data.tar.gz: 3e110fe0c9a710158865e3f67a443dacde11a7aba68f92df3ff305016cc2fdbc
5
5
  SHA512:
6
- metadata.gz: ac985c1f32b83fdec80085a665a0219f75558df08be4ed73ef5828397822ab214cc7c605fcaf9ec464a51fc3ed7614cd717a9e1e4ea1b8310c4e2356c639dddc
7
- data.tar.gz: 7334aae4c9f2d30c41d55bfbc51135f8985b5be4b6699fc32caf79a06fd058b17a126bdea0528186b301103a9bd0de3997926ca3709c5af0b1adfe8b986b4791
6
+ metadata.gz: 376a36b1a4e897c49af1fc553504471e2c9014e8bfb1b147dc9f0d1af5f5202acb3e91feaf382310f17468e7515b1c59d2cb8d0198285418d5733cf2a643742d
7
+ data.tar.gz: 910aef0f0fd56d15ef56c1fe7b2e3bb2ebc18d65dfb35aa7c88ab698e6e6ed8fe8bd21635ff3ca94f85c10f9c730d81b5fc3b247a4412dfb180f9f869093d693
@@ -7,6 +7,7 @@ module RuboCop
7
7
  MSG = 'Association %<association>s references through %<through>s, ' \
8
8
  'but %<through>s is not defined in this model.'
9
9
  ASSOCIATION_METHODS = [
10
+ :belongs_to,
10
11
  :has_one,
11
12
  :has_many,
12
13
  :has_and_belongs_to_many
@@ -1,39 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../shared/route_collector'
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
- return unless rails_routes_draw_block?(node)
13
-
14
- routes = collect_routes(node)
15
- return if routes.size < 2
14
+ process_route_block(node)
15
+ end
16
16
 
17
- check_consistent_spacing(routes)
17
+ def investigate(processed_source)
18
+ process_route_file(processed_source)
18
19
  end
19
20
 
20
21
  private
21
22
 
22
- def rails_routes_draw_block?(node)
23
- return false unless node.block_type?
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/route_collector'
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
- return unless rails_routes_draw_block?(node)
13
-
14
- routes = collect_routes(node)
15
- return if routes.size < 2
14
+ process_route_block(node)
15
+ end
16
16
 
17
- check_for_scattered_routes(routes)
17
+ def investigate(processed_source)
18
+ process_route_file(processed_source)
18
19
  end
19
20
 
20
21
  private
21
22
 
22
- def rails_routes_draw_block?(node)
23
- return false unless node.block_type?
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/route_collector'
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
- return unless rails_routes_draw_block?(node)
13
-
14
- routes = collect_routes(node)
15
- return if routes.empty?
14
+ process_route_block(node)
15
+ end
16
16
 
17
- check_root_position(routes)
17
+ def investigate(processed_source)
18
+ process_route_file(processed_source)
18
19
  end
19
20
 
20
21
  private
21
22
 
22
- def rails_routes_draw_block?(node)
23
- return false unless node.block_type?
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 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] }
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/route_collector'
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
- return unless rails_routes_draw_block?(node)
13
-
14
- routes = collect_routes(node)
15
- return if routes.size < 2
14
+ process_route_block(node)
15
+ end
16
16
 
17
- check_route_separation(routes)
17
+ def investigate(processed_source)
18
+ process_route_file(processed_source)
18
19
  end
19
20
 
20
21
  private
21
22
 
22
- def rails_routes_draw_block?(node)
23
- return false unless node.block_type?
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/route_collector'
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
- return unless rails_routes_draw_block?(node)
14
-
15
- routes = collect_routes(node)
16
- return if routes.size < 2
15
+ process_route_block(node)
16
+ end
17
17
 
18
- check_route_sorting(routes)
18
+ def investigate(processed_source)
19
+ process_route_file(processed_source)
19
20
  end
20
21
 
21
22
  private
22
23
 
23
- def rails_routes_draw_block?(node)
24
- return false unless node.block_type?
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,6 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
+ # rubocop:disable Metrics/ClassLength
6
7
  class CollectionContext
7
8
  ROUTE_CATEGORIES = {
8
9
  simple: [:get, :post, :put, :patch, :delete, :head, :options, :match, :root],
@@ -12,6 +13,7 @@ module RuboCop
12
13
  }.freeze
13
14
 
14
15
  ALL_ROUTE_METHODS = ROUTE_CATEGORIES.values.flatten.freeze
16
+ SCOPE_OPTIONS = [:module, :path].freeze
15
17
 
16
18
  def initialize(collector, namespace_level, namespace_path = [])
17
19
  @collector = collector
@@ -44,18 +46,31 @@ module RuboCop
44
46
  return unless send_node.send_type? && route_method?(send_node)
45
47
 
46
48
  add_route_if_valid(send_node)
49
+ process_nested_context(node)
50
+ end
47
51
 
52
+ def process_nested_context(node)
48
53
  body = node.body
49
54
  return unless body
50
55
 
56
+ new_namespace_path = build_namespace_path(node.send_node)
57
+ nested_context = CollectionContext.new(@collector, @namespace_level + 1, new_namespace_path)
58
+ nested_context.process_node(body)
59
+ end
60
+
61
+ def build_namespace_path(send_node)
51
62
  new_namespace_path = @namespace_path.dup
52
- if send_node.method_name == :namespace
63
+
64
+ case send_node.method_name
65
+ when :namespace
53
66
  namespace_name = extract_namespace_name(send_node)
54
67
  new_namespace_path << namespace_name if namespace_name
68
+ when :scope
69
+ scope_name = extract_scope_name(send_node)
70
+ new_namespace_path << scope_name if scope_name
55
71
  end
56
72
 
57
- nested_context = CollectionContext.new(@collector, @namespace_level + 1, new_namespace_path)
58
- nested_context.process_node(body)
73
+ new_namespace_path
59
74
  end
60
75
 
61
76
  def extract_namespace_name(node)
@@ -65,6 +80,47 @@ module RuboCop
65
80
  'unknown'
66
81
  end
67
82
 
83
+ def extract_scope_name(node)
84
+ scope_name = extract_scope_option_value(node)
85
+ return scope_name if scope_name
86
+
87
+ # If no module/path specified, use first argument if it's a symbol/string
88
+ first_arg = node.arguments.first
89
+ return first_arg.value.to_s if first_arg&.sym_type? || first_arg&.str_type?
90
+
91
+ 'scope'
92
+ end
93
+
94
+ def extract_scope_option_value(node)
95
+ node.arguments.each do |arg|
96
+ next unless arg.hash_type?
97
+
98
+ scope_value = find_scope_option_in_hash(arg)
99
+ return scope_value if scope_value
100
+ end
101
+
102
+ nil
103
+ end
104
+
105
+ def find_scope_option_in_hash(hash_arg)
106
+ hash_arg.pairs.each do |pair|
107
+ value = extract_scope_value_from_pair(pair)
108
+ return value if value
109
+ end
110
+
111
+ nil
112
+ end
113
+
114
+ def extract_scope_value_from_pair(pair)
115
+ key = pair.key
116
+ return nil unless key&.sym_type? && SCOPE_OPTIONS.include?(key.value)
117
+
118
+ value = pair.value
119
+ return value.value.to_s if value&.sym_type? || value&.str_type?
120
+
121
+ nil
122
+ end
123
+
68
124
  def route_method?(node)
69
125
  return false unless node.send_type?
70
126
 
@@ -134,6 +190,7 @@ module RuboCop
134
190
  :other
135
191
  end
136
192
  end
193
+ # rubocop:enable Metrics/ClassLength
137
194
  end
138
195
  end
139
196
  end
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Nueca
5
- VERSION = '1.1.5'
5
+ VERSION = '1.2.2'
6
6
  end
7
7
  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.1.5
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tien
@@ -160,6 +160,7 @@ files:
160
160
  - lib/rubocop/cop/rails/time_zone_today.rb
161
161
  - lib/rubocop/cop/shared/collection_context.rb
162
162
  - lib/rubocop/cop/shared/route_collector.rb
163
+ - lib/rubocop/cop/shared/route_helper.rb
163
164
  - lib/rubocop/nueca/plugin.rb
164
165
  - lib/rubocop/nueca/version.rb
165
166
  homepage: https://github.com/tieeeeen1994/rubocop-nueca