carwow_rubocop 6.7.0 → 6.9.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: e3188042457455f9b00b2eba182c978d7687a7324c02bf2df68dd72a353e5a72
4
- data.tar.gz: 15b8d392add3a85e05b87a6acb705d3c1a8cd54f906d7021a5c5a854387a863a
3
+ metadata.gz: e2a4da2858ed109b02d01111b35e24184159d2d0c592855a4bc57bbd8d0d2f04
4
+ data.tar.gz: 8bf60cd532eae6c3a401eecb64f2460ba377c7573ba67657db2f792a1ac6b212
5
5
  SHA512:
6
- metadata.gz: e156ae2a9536b74791994e18aa3e708c9dc083b6018613e696fb564bfb8b0b7aab64a4611b97ab7d9b8149942a975f6846e6a789c4cfa8d8debe10a2e45b8f74
7
- data.tar.gz: 7853d1d241b1d1be66719736a202bfb83a68ebb7677e04b5b4775113c29225a233671cb002b98b5e13e007d50b92b9b89ee5be9eaf0acaf5c8c8e9156c50095d
6
+ metadata.gz: 57c7b0a76743efcafe2da17ad73254f484e83ae6d7ccd7b855f8b3f60648ff64fb83de0a63d091153b69b07e72f8df8121e2736ac88bb1d64319a5f64b6f9a23
7
+ data.tar.gz: 157af413e04a76aa595103680d403d846d3d9ef9018cf4b41c123a6fff97feb73e88fc55b3ebf057bd68b5cd2cc13254ecfcb6ace01d6d06e7b40f19b1166b44
data/Gemfile.lock CHANGED
@@ -1,11 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- carwow_rubocop (6.7.0)
4
+ carwow_rubocop (6.9.0)
5
5
  rubocop (>= 1.75)
6
6
  rubocop-factory_bot
7
7
  rubocop-performance
8
- rubocop-rspec (>= 3.2, < 3.6)
8
+ rubocop-rspec (~> 3.2)
9
9
 
10
10
  GEM
11
11
  remote: https://rubygems.org/
@@ -13,22 +13,22 @@ GEM
13
13
  ast (2.4.3)
14
14
  coderay (1.1.3)
15
15
  diff-lcs (1.5.1)
16
- json (2.10.2)
17
- language_server-protocol (3.17.0.4)
16
+ json (2.21.2)
17
+ language_server-protocol (3.17.0.6)
18
18
  lint_roller (1.1.0)
19
19
  method_source (1.1.0)
20
- parallel (1.26.3)
21
- parser (3.3.7.4)
20
+ parallel (2.1.0)
21
+ parser (3.3.12.0)
22
22
  ast (~> 2.4.1)
23
23
  racc
24
- prism (1.4.0)
24
+ prism (1.9.0)
25
25
  pry (0.15.2)
26
26
  coderay (~> 1.1)
27
27
  method_source (~> 1.0)
28
28
  racc (1.8.1)
29
29
  rainbow (3.1.1)
30
30
  rake (13.2.1)
31
- regexp_parser (2.10.0)
31
+ regexp_parser (2.12.0)
32
32
  rspec (3.13.0)
33
33
  rspec-core (~> 3.13.0)
34
34
  rspec-expectations (~> 3.13.0)
@@ -42,20 +42,20 @@ GEM
42
42
  diff-lcs (>= 1.2.0, < 2.0)
43
43
  rspec-support (~> 3.13.0)
44
44
  rspec-support (3.13.1)
45
- rubocop (1.75.2)
45
+ rubocop (1.89.0)
46
46
  json (~> 2.3)
47
47
  language_server-protocol (~> 3.17.0.2)
48
48
  lint_roller (~> 1.1.0)
49
- parallel (~> 1.10)
49
+ parallel (>= 1.10)
50
50
  parser (>= 3.3.0.2)
51
51
  rainbow (>= 2.2.2, < 4.0)
52
52
  regexp_parser (>= 2.9.3, < 3.0)
53
- rubocop-ast (>= 1.44.0, < 2.0)
53
+ rubocop-ast (>= 1.49.0, < 2.0)
54
54
  ruby-progressbar (~> 1.7)
55
55
  unicode-display_width (>= 2.4.0, < 4.0)
56
- rubocop-ast (1.44.0)
56
+ rubocop-ast (1.50.0)
57
57
  parser (>= 3.3.7.2)
58
- prism (~> 1.4)
58
+ prism (~> 1.7)
59
59
  rubocop-factory_bot (2.27.1)
60
60
  lint_roller (~> 1.1)
61
61
  rubocop (~> 1.72, >= 1.72.1)
@@ -63,13 +63,14 @@ GEM
63
63
  lint_roller (~> 1.1)
64
64
  rubocop (>= 1.72.1, < 2.0)
65
65
  rubocop-ast (>= 1.38.0, < 2.0)
66
- rubocop-rspec (3.5.0)
66
+ rubocop-rspec (3.10.2)
67
67
  lint_roller (~> 1.1)
68
- rubocop (~> 1.72, >= 1.72.1)
68
+ regexp_parser (>= 2.0)
69
+ rubocop (~> 1.86, >= 1.86.2)
69
70
  ruby-progressbar (1.13.0)
70
- unicode-display_width (3.1.4)
71
- unicode-emoji (~> 4.0, >= 4.0.4)
72
- unicode-emoji (4.0.4)
71
+ unicode-display_width (3.2.0)
72
+ unicode-emoji (~> 4.1)
73
+ unicode-emoji (4.2.0)
73
74
 
74
75
  PLATFORMS
75
76
  ruby
@@ -14,12 +14,12 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = 'https://github.com/carwow/carwow_rubocop'
15
15
  spec.license = 'MIT'
16
16
 
17
- if spec.respond_to?(:metadata)
18
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
19
- else
20
- raise 'RubyGems 2.0 or newer is required to protect against ' \
21
- 'public gem pushes.'
22
- end
17
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata)
18
+
19
+ spec.metadata = {
20
+ 'allowed_push_host' => 'https://rubygems.org',
21
+ 'rubygems_mfa_required' => 'true'
22
+ }
23
23
 
24
24
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
25
25
  f.match(%r{^(test|spec|features)/})
@@ -35,9 +35,6 @@ Gem::Specification.new do |spec|
35
35
 
36
36
  spec.add_dependency 'rubocop', '>= 1.75'
37
37
  spec.add_dependency 'rubocop-performance'
38
- spec.add_dependency 'rubocop-rspec', '>= 3.2', '< 3.6'
38
+ spec.add_dependency 'rubocop-rspec', '~> 3.2'
39
39
  spec.add_dependency 'rubocop-factory_bot'
40
- spec.metadata = {
41
- 'rubygems_mfa_required' => 'true'
42
- }
43
40
  end
@@ -45,3 +45,11 @@ Carwow/NoChangeMatcherWithBrowserAction:
45
45
  Include:
46
46
  - "**/*_spec.rb"
47
47
  - "**/spec/**/*"
48
+
49
+ Carwow/ProductAreaRequired:
50
+ Enabled: true
51
+ Include:
52
+ - "app/controllers/**/*.rb"
53
+ - "app/jobs/**/*.rb"
54
+ Exclude:
55
+ - "**/concerns/**/*.rb"
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module Carwow
3
- VERSION = '6.7.0'.freeze
3
+ VERSION = '6.9.0'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Carwow
8
+ # Controllers and jobs are attributed to a team via the `product_area` DSL
9
+ # (CarwowCore::ProductArea::Taggable) so their Honeycomb spans and Bugsnag
10
+ # errors route to the owning team automatically. A class with no declared
11
+ # area falls back to manual triage.
12
+ #
13
+ # This only checks that `product_area` is called somewhere in the class's
14
+ # own body — it cannot verify, statically, that a superclass declared in
15
+ # another file already sets one. Shared base classes that are
16
+ # intentionally left untagged (e.g. ApplicationController, ApplicationJob,
17
+ # or a namespaced API base controller) should be added to this cop's
18
+ # `Exclude` in the consuming app's .rubocop.yml rather than tagged.
19
+ #
20
+ # A class nested inside another class (rather than merely inside a
21
+ # namespacing module) is assumed to be a helper — e.g. a custom error
22
+ # raised by the enclosing job — not an entry point of its own, so it is
23
+ # not checked. Only the enclosing class needs the tag.
24
+ #
25
+ # A class that subclasses a known Ruby exception (StandardError,
26
+ # RuntimeError, ...) or whose superclass name ends in `Error`/`Exception`
27
+ # is never checked, wherever it's defined: exceptions are raised inside
28
+ # an entry point, not routed to one of their own.
29
+ #
30
+ # A class that doesn't look like a controller/job entry point itself (no
31
+ # Controller/Job-ish superclass, no `include Sidekiq::Worker`/`Sidekiq::Job`,
32
+ # no `perform` method) is skipped if it sits alongside a sibling class in
33
+ # the same file/module that already declares `product_area` — a common
34
+ # pattern for small POROs colocated with the controller/job that uses
35
+ # them. Two genuinely independent entry points defined in the same file
36
+ # still both need their own tag.
37
+ #
38
+ # @example
39
+ # # bad
40
+ # class PaymentsController < ApplicationController
41
+ # end
42
+ #
43
+ # # good
44
+ # class PaymentsController < ApplicationController
45
+ # product_area 'smc-payments'
46
+ # end
47
+ #
48
+ # # good - nested error class does not need its own tag
49
+ # class SyncListing < ApplicationJob
50
+ # product_area 'salesforce'
51
+ #
52
+ # class UnknownReference < StandardError; end
53
+ # end
54
+ #
55
+ # # good - sibling error class, and sibling PORO helper, do not need their own tag
56
+ # module Payments
57
+ # class RefundFailed < StandardError; end
58
+ #
59
+ # class RefundsController < ApplicationController
60
+ # product_area 'smc-payments'
61
+ # end
62
+ #
63
+ # class RefundPresenter
64
+ # def initialize(refund); end
65
+ # end
66
+ # end
67
+ #
68
+ class ProductAreaRequired < ::RuboCop::Cop::Base
69
+ MSG = 'Declare a `product_area` for this class so its Honeycomb spans ' \
70
+ 'and Bugsnag errors route to the owning team.'
71
+
72
+ KNOWN_ERROR_SUPERCLASSES = %w[StandardError RuntimeError Exception ScriptError].freeze
73
+ ERROR_NAME_PATTERN = /Error\z|Exception\z/
74
+ ENTRY_POINT_SUPERCLASS_PATTERN = /Controller|Job/
75
+
76
+ def_node_matcher :product_area_declaration?, <<~PATTERN
77
+ (send nil? :product_area _)
78
+ PATTERN
79
+
80
+ def_node_matcher :sidekiq_worker_include?, <<~PATTERN
81
+ (send nil? :include (const (const nil? :Sidekiq) {:Worker :Job}))
82
+ PATTERN
83
+
84
+ def_node_search :defines_perform_method?, <<~PATTERN
85
+ (def :perform ...)
86
+ PATTERN
87
+
88
+ def on_class(node)
89
+ return if nested_in_class?(node)
90
+ return if subclasses_known_error?(node)
91
+ return if declares_product_area?(node)
92
+ return if !looks_like_entry_point?(node) && sibling_already_tagged?(node)
93
+
94
+ class_node, = *node
95
+ add_offense(class_node)
96
+ end
97
+
98
+ private
99
+
100
+ def nested_in_class?(node)
101
+ node.each_ancestor(:class).any?
102
+ end
103
+
104
+ def subclasses_known_error?(class_node)
105
+ _name, superclass, = *class_node
106
+ return false unless superclass
107
+
108
+ superclass_name = superclass.source
109
+
110
+ KNOWN_ERROR_SUPERCLASSES.include?(superclass_name) || superclass_name.match?(ERROR_NAME_PATTERN)
111
+ end
112
+
113
+ def declares_product_area?(class_node)
114
+ top_level_statements(class_node).any? { |stmt| product_area_declaration?(stmt) }
115
+ end
116
+
117
+ def looks_like_entry_point?(class_node)
118
+ _name, superclass, body = *class_node
119
+
120
+ return true if superclass&.source&.match?(ENTRY_POINT_SUPERCLASS_PATTERN)
121
+
122
+ statements = top_level_statements(class_node)
123
+ statements.any? { |stmt| sidekiq_worker_include?(stmt) } || (body && defines_perform_method?(body))
124
+ end
125
+
126
+ def sibling_already_tagged?(class_node)
127
+ siblings_scope = class_node.each_ancestor(:module, :class, :begin).first || class_node.parent
128
+ return false unless siblings_scope
129
+
130
+ sibling_classes(siblings_scope, class_node).any? { |sibling| declares_product_area?(sibling) }
131
+ end
132
+
133
+ def sibling_classes(scope, exclude)
134
+ statements = scope.begin_type? ? scope.children : [scope]
135
+ statements.select { |stmt| stmt&.class_type? && stmt != exclude }
136
+ end
137
+
138
+ def top_level_statements(class_node)
139
+ body = class_node.body
140
+ return [] unless body
141
+
142
+ body.begin_type? ? body.children : [body]
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carwow_rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.7.0
4
+ version: 6.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - carwow Developers
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-15 00:00:00.000000000 Z
11
+ date: 2026-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -98,22 +98,16 @@ dependencies:
98
98
  name: rubocop-rspec
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
103
  version: '3.2'
104
- - - "<"
105
- - !ruby/object:Gem::Version
106
- version: '3.6'
107
104
  type: :runtime
108
105
  prerelease: false
109
106
  version_requirements: !ruby/object:Gem::Requirement
110
107
  requirements:
111
- - - ">="
108
+ - - "~>"
112
109
  - !ruby/object:Gem::Version
113
110
  version: '3.2'
114
- - - "<"
115
- - !ruby/object:Gem::Version
116
- version: '3.6'
117
111
  - !ruby/object:Gem::Dependency
118
112
  name: rubocop-factory_bot
119
113
  requirement: !ruby/object:Gem::Requirement
@@ -172,10 +166,12 @@ files:
172
166
  - lib/rubocop/cop/carwow/no_change_matcher_with_browser_action.rb
173
167
  - lib/rubocop/cop/carwow/no_stubbing_business_event.rb
174
168
  - lib/rubocop/cop/carwow/no_vehicle_brand_model_sideload.rb
169
+ - lib/rubocop/cop/carwow/product_area_required.rb
175
170
  homepage: https://github.com/carwow/carwow_rubocop
176
171
  licenses:
177
172
  - MIT
178
173
  metadata:
174
+ allowed_push_host: https://rubygems.org
179
175
  rubygems_mfa_required: 'true'
180
176
  post_install_message:
181
177
  rdoc_options: []
@@ -192,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
188
  - !ruby/object:Gem::Version
193
189
  version: '0'
194
190
  requirements: []
195
- rubygems_version: 3.4.19
191
+ rubygems_version: 3.5.20
196
192
  signing_key:
197
193
  specification_version: 4
198
194
  summary: carwow's rubocop configuration