rubocop-petal 1.8.0 → 1.10.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: cf2f66c07fd9f7e2d26db32b9ab5a5fc704293066bf6e8519b6fdec8cec71128
4
- data.tar.gz: 3f4a7fef3bde079552b9bcffc43665a93422d3bf4dacda37a754540787dd4773
3
+ metadata.gz: 10f81655924fecdd2195281a71d19602efadfc7a9a60e7a9f005136c969aa45f
4
+ data.tar.gz: d5e0bab2b887cec6b46ed1ac96993cd18fe0c485f0723425288bb9c80c57f396
5
5
  SHA512:
6
- metadata.gz: 70fe73e240f643f554767029a2cffbb4e298b80706c823c2dda8eccb2470da549aba51847241e68f7871aedabd6a0702f37ed97ed771c2aa703aabe6c03c9b93
7
- data.tar.gz: 77d30ff3b03bf663b6546f3a243a90cde664a854467879e0ef3d47f2856f860eddec31ac39a8f9b723450e642f3f9f2593c1ad1cfae59baa5fea8e1b3e67e3e6
6
+ metadata.gz: 0f54b691eb3fa5512d745e425494ce785dec5f74deccc101c85e7214eb2b4d227619f987347e2ab29a7dc5d0d509b52bfdaa6e44cdf297d440b76a21a91dd4dd
7
+ data.tar.gz: 210f323604d3ceae5db556afd7aa7f689b4c830d2886d7994030586c0726f5e6f7b7e05314dc49f0d39ec887142b857dd0ce9cbe22712dc3fc8a855ae35ac084
data/config/default.yml CHANGED
@@ -15,6 +15,12 @@ Grape/PreferNamespace:
15
15
  Include:
16
16
  - app/api/**/*
17
17
 
18
+ Migration/AlterAfterCreateTable:
19
+ Description: 'Prefer defining new table changes inside the create_table block.'
20
+ Enabled: true
21
+ Include:
22
+ - db/migrate/**
23
+
18
24
  Migration/AlwaysBulkChangeTable:
19
25
  Description: 'Suggest to always use `bulk: true` when using `change_table`.'
20
26
  Enabled: true
@@ -59,6 +65,12 @@ RSpec/AggregateExamples:
59
65
  - validate_inclusion_of
60
66
  - validates_exclusion_of
61
67
 
68
+ RSpec/AggregateFailuresTag:
69
+ Description: 'Checks that the `:aggregate_failures` tag is only used on examples, not on example groups.'
70
+ Enabled: true
71
+ Include:
72
+ - spec/**/*
73
+
62
74
  RSpec/ChewyStrategy:
63
75
  Description: 'Prevent using Chewy strategy in tests.'
64
76
  Enabled: true
@@ -156,12 +168,14 @@ Sidekiq/KeywordArguments:
156
168
  Enabled: true
157
169
  Include:
158
170
  - app/workers/**/*
171
+ - app/jobs/**/*
159
172
 
160
173
  Sidekiq/NoNilReturn:
161
174
  Description: 'Prevent early nil return in workers'
162
175
  Enabled: false
163
176
  Include:
164
177
  - app/workers/**/*
178
+ - app/jobs/**/*
165
179
 
166
180
  Sidekiq/PerformInline:
167
181
  Description: 'Suggest to use `perform_inline` instead of `new.perform` for Sidekiq workers.'
@@ -171,3 +185,26 @@ Sidekiq/SymbolArgument:
171
185
  Description: "Prevent passing keywords arguments in worker's perform method"
172
186
  Enabled: true
173
187
  SafeAutoCorrect: false
188
+
189
+ Sidekiq/PreferJob:
190
+ Description: 'Prefer `Sidekiq::Job` over `Sidekiq::Worker`.'
191
+ Enabled: true
192
+ StyleGuide: https://github.com/sidekiq/sidekiq/wiki/Best-Practices#4-use-precise-terminology
193
+ Include:
194
+ - app/workers/**/*
195
+ - app/jobs/**/*
196
+
197
+ Sidekiq/JobNaming:
198
+ Description: 'Job class name should end with `Job` instead of `Worker`.'
199
+ Enabled: true
200
+ StyleGuide: https://github.com/sidekiq/sidekiq/wiki/Best-Practices#4-use-precise-terminology
201
+ Include:
202
+ - app/workers/**/*
203
+ - app/jobs/**/*
204
+
205
+ Sidekiq/JobLocation:
206
+ Description: 'Job class with `Job` suffix should be placed in `app/jobs` directory instead of `app/workers`.'
207
+ Enabled: true
208
+ StyleGuide: https://github.com/sidekiq/sidekiq/wiki/Best-Practices#4-use-precise-terminology
209
+ Include:
210
+ - app/workers/**/*
@@ -18,7 +18,16 @@ module RuboCop
18
18
  'Specify endpoint name with an argument: `get :some_path`.'
19
19
  HTTP_ACTIONS = Set.new(%i[get head put post patch delete])
20
20
  GRAPE_NAMESPACE_ALIAS = Set.new(%i[namespace resource resources])
21
- METHOD_JUSTIFY_NAMESPACE = Set.new(%i[route_param namespaces resource resources version])
21
+ METHOD_JUSTIFY_NAMESPACE = Set.new(%i[route_param
22
+ namespaces
23
+ resource
24
+ resources
25
+ version
26
+ before
27
+ after
28
+ finally
29
+ after_validation
30
+ before_validation])
22
31
 
23
32
  def_node_matcher :namespace?, <<~PATTERN
24
33
  (send nil? GRAPE_NAMESPACE_ALIAS ({sym | str} _))
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Migration
6
+ # Prefer defining table changes inside the create_table block instead of
7
+ # altering the table immediately after creating it.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # create_table :users do |t|
12
+ # t.string :email
13
+ # end
14
+ #
15
+ # add_index :users, :email, unique: true
16
+ #
17
+ # # good
18
+ # create_table :users do |t|
19
+ # t.string :email
20
+ # t.index :email, unique: true
21
+ # end
22
+ class AlterAfterCreateTable < Base
23
+ extend AutoCorrector
24
+ include RangeHelp
25
+
26
+ MSG = 'Prefer defining new table changes inside the create_table block.'
27
+
28
+ ALTER_METHODS = (
29
+ Rails::BulkChangeTable::COMBINABLE_ALTER_METHODS +
30
+ Rails::BulkChangeTable::MYSQL_COMBINABLE_ALTER_METHODS +
31
+ Rails::BulkChangeTable::POSTGRESQL_COMBINABLE_ALTER_METHODS +
32
+ Rails::BulkChangeTable::POSTGRESQL_COMBINABLE_ALTER_METHODS_SINCE_6_1
33
+ ).uniq.freeze
34
+
35
+ RESTRICT_ON_SEND = ALTER_METHODS.freeze
36
+
37
+ def on_block(node)
38
+ return unless node.send_node&.command?(:create_table)
39
+ return unless (alter_node = alter_node_after_create_table(node))
40
+ return unless same_table?(node.send_node, alter_node)
41
+
42
+ add_alter_offense(node, alter_node)
43
+ end
44
+
45
+ private
46
+
47
+ def alter_node_after_create_table(create_table_node)
48
+ siblings = sibling_nodes(create_table_node)
49
+ index = siblings.index(create_table_node)
50
+ return unless index
51
+
52
+ alter_node = siblings[index + 1]
53
+ return unless alter_node&.send_type?
54
+ return unless ALTER_METHODS.include?(alter_node.method_name)
55
+ return unless alter_node.receiver.nil?
56
+
57
+ alter_node
58
+ end
59
+
60
+ def sibling_nodes(node)
61
+ parent = node.parent
62
+ return parent.children if parent&.begin_type?
63
+
64
+ []
65
+ end
66
+
67
+ def same_table?(create_table_node, alter_node)
68
+ create_table_argument = create_table_node.first_argument
69
+ alter_table_argument = alter_node.first_argument
70
+ return false unless create_table_argument && alter_table_argument
71
+
72
+ if create_table_argument.is_a?(RuboCop::AST::BasicLiteralNode) &&
73
+ alter_table_argument.is_a?(RuboCop::AST::BasicLiteralNode)
74
+ literal_value(create_table_argument) == literal_value(alter_table_argument)
75
+ else
76
+ create_table_argument.source == alter_table_argument.source
77
+ end
78
+ end
79
+
80
+ def literal_value(node)
81
+ return unless node.is_a?(RuboCop::AST::BasicLiteralNode)
82
+
83
+ node.value.to_s
84
+ end
85
+
86
+ def add_alter_offense(create_table_node, alter_node)
87
+ replacement = replacement_for(create_table_node, alter_node)
88
+
89
+ add_offense(alter_node.loc.selector) do |corrector|
90
+ next unless replacement
91
+
92
+ end_line_range = processed_source.buffer.line_range(create_table_node.loc.end.line)
93
+ corrector.insert_before(end_line_range, "#{replacement}\n")
94
+ corrector.remove(range_by_whole_lines(alter_node.source_range, include_final_newline: true))
95
+ end
96
+ end
97
+
98
+ def replacement_for(create_table_node, alter_node)
99
+ block_variable = create_table_node.arguments.first&.source
100
+ return unless block_variable
101
+
102
+ case alter_node.method_name
103
+ when :add_index
104
+ replacement_for_add_index(block_variable, alter_node)
105
+ when :add_column
106
+ replacement_for_add_column(block_variable, alter_node)
107
+ when :add_timestamps
108
+ replacement_for_add_timestamps(block_variable, alter_node)
109
+ end
110
+ end
111
+
112
+ def replacement_for_add_index(block_variable, node)
113
+ "#{indentation(node)}#{block_variable}.index #{arguments_after_table(node)}"
114
+ end
115
+
116
+ def replacement_for_add_column(block_variable, node)
117
+ column_node, type_node = node.arguments[1, 2]
118
+ return unless column_node && type_node
119
+ return unless type_node.sym_type?
120
+
121
+ extra_arguments = node.arguments[3..].map(&:source)
122
+ arguments = [column_node.source, *extra_arguments].join(', ')
123
+
124
+ "#{indentation(node)}#{block_variable}.#{type_node.value} #{arguments}"
125
+ end
126
+
127
+ def replacement_for_add_timestamps(block_variable, node)
128
+ arguments = arguments_after_table(node)
129
+ method_call = "#{block_variable}.timestamps"
130
+ method_call = "#{method_call} #{arguments}" unless arguments.empty?
131
+
132
+ "#{indentation(node)}#{method_call}"
133
+ end
134
+
135
+ def arguments_after_table(node)
136
+ node.arguments[1..].map(&:source).join(', ')
137
+ end
138
+
139
+ def indentation(alter_node)
140
+ ' ' * (alter_node.loc.column + 2)
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks that the `:aggregate_failures` tag is only used on examples,
7
+ # not on example groups.
8
+ #
9
+ # The `:aggregate_failures` tag only works on individual examples (`it`, `specify`, etc.),
10
+ # not on example groups (`describe`, `context`, etc.).
11
+ #
12
+ # @example
13
+ # # bad
14
+ # describe '#enabled?', :aggregate_failures do
15
+ # # ...
16
+ # end
17
+ #
18
+ # context 'when enabled', :aggregate_failures do
19
+ # # ...
20
+ # end
21
+ #
22
+ # # good
23
+ # it 'returns true', :aggregate_failures do
24
+ # # ...
25
+ # end
26
+ #
27
+ # specify 'the behavior', :aggregate_failures do
28
+ # # ...
29
+ # end
30
+ #
31
+ class AggregateFailuresTag < Base
32
+ MSG = 'The `:aggregate_failures` tag should only be used on examples, not on example groups.'
33
+
34
+ EXAMPLE_METHODS = %i[
35
+ it
36
+ specify
37
+ example
38
+ scenario
39
+ its
40
+ fit
41
+ fspecify
42
+ fexample
43
+ fscenario
44
+ focus
45
+ xit
46
+ xspecify
47
+ xexample
48
+ xscenario
49
+ skip
50
+ pending
51
+ ].freeze
52
+
53
+ RSPEC_METHODS = %i[
54
+ describe
55
+ context
56
+ feature
57
+ example_group
58
+ xdescribe
59
+ fdescribe
60
+ it
61
+ specify
62
+ example
63
+ scenario
64
+ its
65
+ fit
66
+ fspecify
67
+ fexample
68
+ fscenario
69
+ focus
70
+ xit
71
+ xspecify
72
+ xexample
73
+ xscenario
74
+ skip
75
+ pending
76
+ ].freeze
77
+
78
+ def on_block(node)
79
+ return unless rspec_block?(node)
80
+ return unless aggregate_failures_tag?(node)
81
+ return if example_method?(node)
82
+
83
+ # Create a range that includes the method call up to and including the 'do' keyword
84
+ send_node = node.send_node
85
+ range = send_node.source_range.join(node.loc.begin)
86
+ add_offense(range)
87
+ end
88
+
89
+ private
90
+
91
+ def rspec_block?(node)
92
+ send_node = node.send_node
93
+ return false unless send_node
94
+ return false unless send_node.send_type?
95
+
96
+ RSPEC_METHODS.include?(send_node.method_name)
97
+ end
98
+
99
+ def example_method?(node)
100
+ send_node = node.send_node
101
+ return false unless send_node
102
+
103
+ EXAMPLE_METHODS.include?(send_node.method_name)
104
+ end
105
+
106
+ def aggregate_failures_tag?(node)
107
+ send_node = node.send_node
108
+ return false unless send_node
109
+
110
+ send_node.arguments.any? do |arg|
111
+ if arg.sym_type? && arg.value == :aggregate_failures
112
+ true
113
+ elsif arg.hash_type?
114
+ arg.pairs.any? { |pair| pair.key.sym_type? && pair.key.value == :aggregate_failures }
115
+ else
116
+ false
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -7,7 +7,7 @@ module RuboCop
7
7
  module Helpers
8
8
  NODE_MATCHERS = lambda do
9
9
  def_node_matcher :sidekiq_include?, <<~PATTERN
10
- (send nil? :include (const (const nil? :Sidekiq) :Worker))
10
+ (send nil? :include (const (const {nil? cbase} :Sidekiq) {:Worker :Job}))
11
11
  PATTERN
12
12
 
13
13
  def_node_matcher :includes_sidekiq?, <<~PATTERN
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sidekiq
8
+ # This cop checks that Sidekiq job classes with "Job" suffix are placed in the
9
+ # `app/jobs` directory instead of `app/workers` directory.
10
+ # This follows the modern Sidekiq convention where jobs should be organized
11
+ # in the appropriate directory structure.
12
+ #
13
+ # @example
14
+ # # bad - Job class in app/workers directory
15
+ # # app/workers/my_job.rb
16
+ # class MyJob
17
+ # include Sidekiq::Job
18
+ # end
19
+ #
20
+ # # good - Job class in app/jobs directory
21
+ # # app/jobs/my_job.rb
22
+ # class MyJob
23
+ # include Sidekiq::Job
24
+ # end
25
+ #
26
+ # # good - Worker class can stay in app/workers (though not recommended)
27
+ # # app/workers/my_worker.rb
28
+ # class MyWorker
29
+ # include Sidekiq::Worker
30
+ # end
31
+ class JobLocation < Base
32
+ include Helpers
33
+
34
+ MSG = 'Job class with `Job` suffix should be placed in `app/jobs` directory instead of `app/workers`.'
35
+
36
+ def on_class(node)
37
+ return unless sidekiq_worker?(node)
38
+
39
+ class_name = extract_class_name(node)
40
+ return unless class_name
41
+ return unless class_name.end_with?('Job')
42
+
43
+ file_path = processed_source.buffer.name
44
+ return unless file_path.include?('app/workers/')
45
+
46
+ add_offense(node.children.first, message: MSG)
47
+ end
48
+
49
+ private
50
+
51
+ def extract_class_name(node)
52
+ name_node = node.children.first
53
+ return unless name_node&.const_type?
54
+
55
+ # Get the last part of the constant name (e.g., "EmailJob" from "MyApp::EmailJob")
56
+ name_node.children.last.to_s
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sidekiq
8
+ # This cop checks that Sidekiq job class names end with "Job" instead of "Worker".
9
+ # Since Sidekiq 6.3, it is best practice to use "Job" terminology over "Worker".
10
+ #
11
+ # @example
12
+ # # bad
13
+ # class MyWorker
14
+ # include Sidekiq::Job
15
+ # end
16
+ #
17
+ # class ProcessDataWorker
18
+ # include Sidekiq::Worker
19
+ # end
20
+ #
21
+ # # good
22
+ # class MyJob
23
+ # include Sidekiq::Job
24
+ # end
25
+ #
26
+ # class ProcessDataJob
27
+ # include Sidekiq::Job
28
+ # end
29
+ class JobNaming < Base
30
+ include Helpers
31
+
32
+ MSG = 'Job class name should end with `Job` instead of `Worker`.'
33
+
34
+ def_node_matcher :class_name, <<~PATTERN
35
+ (class (const _ $_) ...)
36
+ PATTERN
37
+
38
+ def on_class(node)
39
+ return unless sidekiq_worker?(node)
40
+
41
+ class_name = extract_class_name(node)
42
+ return unless class_name
43
+ return unless class_name.end_with?('Worker')
44
+
45
+ add_offense(node.children.first, message: MSG)
46
+ end
47
+
48
+ private
49
+
50
+ def extract_class_name(node)
51
+ name_node = node.children.first
52
+ return unless name_node&.const_type?
53
+
54
+ # Get the last part of the constant name (e.g., "EmailWorker" from "MyApp::EmailWorker")
55
+ name_node.children.last.to_s
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sidekiq
8
+ # This cop checks for the use of `Sidekiq::Worker` and suggests using `Sidekiq::Job` instead.
9
+ # Since Sidekiq 6.3, it is best practice to use `Sidekiq::Job` over `Sidekiq::Worker`.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # class MyWorker
14
+ # include Sidekiq::Worker
15
+ # end
16
+ #
17
+ # # good
18
+ # class MyJob
19
+ # include Sidekiq::Job
20
+ # end
21
+ class PreferJob < Base
22
+ extend AutoCorrector
23
+
24
+ MSG = 'Prefer `Sidekiq::Job` over `Sidekiq::Worker`.'
25
+
26
+ def_node_matcher :sidekiq_worker_include?, <<~PATTERN
27
+ (send nil? :include (const (const {nil? cbase} :Sidekiq) :Worker))
28
+ PATTERN
29
+
30
+ def on_send(node)
31
+ return unless sidekiq_worker_include?(node)
32
+
33
+ add_offense(node, message: MSG) do |corrector|
34
+ corrector.replace(node.arguments.first, 'Sidekiq::Job')
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Petal
5
- VERSION = '1.8.0'
5
+ VERSION = '1.10.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-petal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean-Francis Bastien
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-04-11 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: lint_roller
@@ -108,7 +107,6 @@ dependencies:
108
107
  - - "~>"
109
108
  - !ruby/object:Gem::Version
110
109
  version: '2.31'
111
- description:
112
110
  email:
113
111
  - jfbastien@petalmd.com
114
112
  executables: []
@@ -128,6 +126,7 @@ files:
128
126
  - lib/rubocop/cop/grape/helpers_include_module.rb
129
127
  - lib/rubocop/cop/grape/prefer_namespace.rb
130
128
  - lib/rubocop/cop/grape/unnecessary_namespace.rb
129
+ - lib/rubocop/cop/migration/alter_after_create_table.rb
131
130
  - lib/rubocop/cop/migration/always_bulk_change_table.rb
132
131
  - lib/rubocop/cop/migration/change_table_references.rb
133
132
  - lib/rubocop/cop/migration/foreign_key_option.rb
@@ -145,6 +144,7 @@ files:
145
144
  - lib/rubocop/cop/rspec/aggregate_examples/matchers_with_side_effects.rb
146
145
  - lib/rubocop/cop/rspec/aggregate_examples/metadata_helpers.rb
147
146
  - lib/rubocop/cop/rspec/aggregate_examples/node_matchers.rb
147
+ - lib/rubocop/cop/rspec/aggregate_failures_tag.rb
148
148
  - lib/rubocop/cop/rspec/chewy_strategy.rb
149
149
  - lib/rubocop/cop/rspec/create_list_max.rb
150
150
  - lib/rubocop/cop/rspec/json_response.rb
@@ -155,9 +155,12 @@ files:
155
155
  - lib/rubocop/cop/sidekiq/const_argument.rb
156
156
  - lib/rubocop/cop/sidekiq/date_time_argument.rb
157
157
  - lib/rubocop/cop/sidekiq/helpers.rb
158
+ - lib/rubocop/cop/sidekiq/job_location.rb
159
+ - lib/rubocop/cop/sidekiq/job_naming.rb
158
160
  - lib/rubocop/cop/sidekiq/keyword_arguments.rb
159
161
  - lib/rubocop/cop/sidekiq/no_nil_return.rb
160
162
  - lib/rubocop/cop/sidekiq/perform_inline.rb
163
+ - lib/rubocop/cop/sidekiq/prefer_job.rb
161
164
  - lib/rubocop/cop/sidekiq/symbol_argument.rb
162
165
  - lib/rubocop/petal.rb
163
166
  - lib/rubocop/petal/plugin.rb
@@ -172,7 +175,6 @@ metadata:
172
175
  bug_tracker_uri: https://github.com/petalmd/rubocop-petal/issues
173
176
  rubygems_mfa_required: 'true'
174
177
  default_lint_roller_plugin: RuboCop::Petal::Plugin
175
- post_install_message:
176
178
  rdoc_options: []
177
179
  require_paths:
178
180
  - lib
@@ -187,8 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
187
189
  - !ruby/object:Gem::Version
188
190
  version: '0'
189
191
  requirements: []
190
- rubygems_version: 3.4.22
191
- signing_key:
192
+ rubygems_version: 3.6.9
192
193
  specification_version: 4
193
194
  summary: Petal custom cops
194
195
  test_files: []