carwow_rubocop 6.7.0 → 6.8.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 +4 -4
- data/Gemfile.lock +1 -1
- data/config/rubocop-carwow.yml +8 -0
- data/lib/rubocop/carwow/version.rb +1 -1
- data/lib/rubocop/cop/carwow/product_area_required.rb +147 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9ba532ee22497e6dad682fcd4d4c8f452faf929df1ce5284e3fb5b2a1d3e5ab8
|
|
4
|
+
data.tar.gz: b54f7cc75cf53f47012ffde9f5253c06c33cc959fe6f38c7aed526aff30f990c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f6d34e7a2768754b1b6171aa09dba55b2a3eff404bc93d6039794630f9440c04304615de5ade41d82e58dcefa8f6ff98396053b68a3dee5da6e104746f49084
|
|
7
|
+
data.tar.gz: 47a4d2cfd627a6fda855df105eb4e6ccc8b246e602badaf823f253c9ddf3952aa6deca294c90c85e9dd1dc3eb1284846291880663dbce6bead17ffb187fa518d
|
data/Gemfile.lock
CHANGED
data/config/rubocop-carwow.yml
CHANGED
|
@@ -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.
|
|
4
|
+
version: 6.8.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-
|
|
11
|
+
date: 2026-08-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -172,6 +172,7 @@ files:
|
|
|
172
172
|
- lib/rubocop/cop/carwow/no_change_matcher_with_browser_action.rb
|
|
173
173
|
- lib/rubocop/cop/carwow/no_stubbing_business_event.rb
|
|
174
174
|
- lib/rubocop/cop/carwow/no_vehicle_brand_model_sideload.rb
|
|
175
|
+
- lib/rubocop/cop/carwow/product_area_required.rb
|
|
175
176
|
homepage: https://github.com/carwow/carwow_rubocop
|
|
176
177
|
licenses:
|
|
177
178
|
- MIT
|
|
@@ -192,7 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
192
193
|
- !ruby/object:Gem::Version
|
|
193
194
|
version: '0'
|
|
194
195
|
requirements: []
|
|
195
|
-
rubygems_version: 3.
|
|
196
|
+
rubygems_version: 3.5.20
|
|
196
197
|
signing_key:
|
|
197
198
|
specification_version: 4
|
|
198
199
|
summary: carwow's rubocop configuration
|