rubocop-petal 1.3.0 → 1.4.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: 15da074670760958ac74e715caa688618632bcbb5379e5fc47dd1a1055c8e956
4
- data.tar.gz: 0d70524ec6b85733e931c09c864151454cde3fc4287c87f6a1722d1d3c6ea81d
3
+ metadata.gz: 99a53e19d1caafc491f963ad3a723fb1b06f9e15fc9d0914db50287c68af1dd6
4
+ data.tar.gz: c1c37cbc85d3c4e56bdd2fffa928f8ce7f16db9afa5541014275a519d75146a5
5
5
  SHA512:
6
- metadata.gz: 2271b3c9bdc14b033c248ec2b7492a2248e017c98a48daded7cee9621e4e5a42c5c30f8036b5c16fcd182408af56d6b213ee9c6a8f719c65506f913aac741b1e
7
- data.tar.gz: 8780348f8074c78603b2fc8988297c12f4531080bd07f54af0cdaa4d9b63d021f5328b8e192b0c8cb34ee53d4933e3d04769240f6907e550a8e7c4e6e042f965
6
+ metadata.gz: 862c765c3e4c638372c04b441d569d25b260f0333e887cb25b76064ac82214173e4367b0225e4023b9ee4de9f804bb57b224bc524fb4beec46b9eff4bde8546d
7
+ data.tar.gz: 263fe7fd0120e188d681dd68f7f44533e01c32d0a71596bc064781628175c7a40f0acea5cb22ec00707a8a67917fdb1dda1ee6811fea92ebf1047887346da20a
data/config/base.yml CHANGED
@@ -78,6 +78,8 @@ RSpec/StubbedMock:
78
78
  RSpec/DescribeClass:
79
79
  Exclude:
80
80
  - spec/integration/**/*.rb
81
+ Rails/Date:
82
+ AllowToTime: false
81
83
  Rails/SaveBang:
82
84
  Enabled: true
83
85
  Rails/NotNullColumn:
data/config/default.yml CHANGED
@@ -55,12 +55,6 @@ RSpec/AggregateExamples:
55
55
  - validate_inclusion_of
56
56
  - validates_exclusion_of
57
57
 
58
- RSpec/AuthenticatedAs:
59
- Description: 'Suggest to use authenticated_as instead of legacy api_key.'
60
- Enabled: true
61
- Include:
62
- - spec/api/**/*_spec.rb
63
-
64
58
  RSpec/CreateListMax:
65
59
  Description: 'Prevent creating to most records with `FactoryBot.create_list`.'
66
60
  Enabled: true
@@ -132,6 +126,26 @@ Performance/Snif:
132
126
  Description: 'Prevent snif in favor of detect'
133
127
  Enabled: true
134
128
 
129
+ Sidekiq/ConstArgument:
130
+ Description: "Prevent passing constant like Class as arguments in worker's perform method"
131
+ Enabled: true
132
+
133
+ Sidekiq/DateTimeArgument:
134
+ Description: "Prevent passing Date/Time arguments in worker's perform method"
135
+ Enabled: true
136
+
137
+ Sidekiq/KeywordArguments:
138
+ Description: "Prevent define keywords arguments in worker's perform method"
139
+ Enabled: true
140
+ Include:
141
+ - app/workers/**/*
142
+
135
143
  Sidekiq/NoNilReturn:
136
144
  Description: 'Prevent early nil return in workers'
137
145
  Enabled: false
146
+ Include:
147
+ - app/workers/**/*
148
+
149
+ Sidekiq/SymbolArgument:
150
+ Description: "Prevent passing keywords arguments in worker's perform method"
151
+ Enabled: true
@@ -24,7 +24,9 @@ module RuboCop
24
24
  self.class.new(selectors + other.selectors)
25
25
  end
26
26
 
27
- delegate :include?, to: :selectors
27
+ def include?(selector)
28
+ selectors.include?(selector)
29
+ end
28
30
 
29
31
  def block_pattern
30
32
  "(block #{send_pattern} ...)"
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+
5
+ # credit: https://github.com/dvandersluis/rubocop-sidekiq/blob/master/lib/rubocop/cop/sidekiq/const_argument.rb
6
+ module RuboCop
7
+ module Cop
8
+ module Sidekiq
9
+ # This cop checks for Sidekiq worker perform arguments that look like classes or modules.
10
+ # These cannot be serialized for Redis, and should not be used with Sidekiq.
11
+ #
12
+ # Constants other than classes/modules are not flagged by this cop.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # MyWorker.perform_async(MyClass)
17
+ # MyWorker.perform_async(MyModule)
18
+ # MyWorker.perform_async(Namespace::Class)
19
+ #
20
+ # # good
21
+ # MyWorker.perform_async(MY_CONSTANT)
22
+ # MyWorker.perform_async(MyClass::MY_CONSTANT)
23
+ class ConstArgument < Base
24
+ CONSTANT_NAME = /\A[A-Z0-9_]+\z/.freeze
25
+
26
+ include Helpers
27
+
28
+ MSG = 'Objects are not Sidekiq-serializable.'
29
+ MSG_SELF = '`self` is not Sidekiq-serializable.'
30
+
31
+ def_node_matcher :initializer?, <<~PATTERN
32
+ (send const :new)
33
+ PATTERN
34
+
35
+ def_node_matcher :constant?, <<~PATTERN
36
+ (const _ _)
37
+ PATTERN
38
+
39
+ def_node_matcher :const_argument?, <<~PATTERN
40
+ {#initializer? #constant? self}
41
+ PATTERN
42
+
43
+ def on_send(node)
44
+ sidekiq_arguments(node).each do |arg|
45
+ next unless const_argument?(arg)
46
+ next if non_class_constant?(arg)
47
+
48
+ add_offense(arg, message: arg.self_type? ? MSG_SELF : MSG)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def non_class_constant?(arg)
55
+ arg.const_type? && arg.children[1].to_s.match?(CONSTANT_NAME)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+
5
+ # credit: https://github.com/dvandersluis/rubocop-sidekiq/blob/master/lib/rubocop/cop/sidekiq/date_time_argument.rb
6
+ module RuboCop
7
+ module Cop
8
+ module Sidekiq
9
+ # This cop checks for date/time objects being passed as arguments to perform a Sidekiq
10
+ # worker. Dates, times, durations, and related classes cannot be serialized to Redis.
11
+ # Use an integer or string representation of the date/time instead.
12
+ #
13
+ # By default, this only allows `to_i` and `to_s` as valid, serializable methods for these
14
+ # classes. Use `AllowedMethods` to specify other allowed methods.
15
+ #
16
+ # @example
17
+ # # bad
18
+ # MyWorker.perform_async(Time.now)
19
+ # MyWorker.perform_async(Date.today)
20
+ # MyWorker.perform_async(DateTime.now)
21
+ # MyWorker.perform_async(ActiveSupport::TimeWithZone.new)
22
+ # MyWorker.perform_async(1.hour)
23
+ # MyWorker.perform_async(1.hour.ago)
24
+ #
25
+ # # good
26
+ # MyWorker.perform_async(Time.now.to_i)
27
+ # MyWorker.perform_async(Date.today.to_s)
28
+ #
29
+ # @example AllowedMethods: [] (default)
30
+ # # bad
31
+ # MyWorker.perform_async(Time.now.mday)
32
+ #
33
+ # @example AllowedMethods: ['mday']
34
+ # # good
35
+ # MyWorker.perform_async(Time.now.mday)
36
+ #
37
+ class DateTimeArgument < ::RuboCop::Cop::Cop
38
+ DURATION_METHODS = %i[
39
+ second
40
+ seconds
41
+ minute
42
+ minutes
43
+ hour
44
+ hours
45
+ day
46
+ days
47
+ week
48
+ weeks
49
+ fortnight
50
+ fortnights
51
+ ].freeze
52
+
53
+ DURATION_TO_TIME_METHODS = %i[
54
+ from_now
55
+ since
56
+ after
57
+ ago
58
+ until
59
+ before
60
+ ].freeze
61
+
62
+ include Helpers
63
+
64
+ DURATION_MSG = 'Durations are not Sidekiq-serializable; use the integer instead.'
65
+ MSG = 'Date/Time objects are not Sidekiq-serializable; convert to integers or strings instead.'
66
+ ALLOWED_METHODS = %i[to_i to_s].freeze
67
+
68
+ def_node_matcher :rational_literal?, <<~PATTERN
69
+ (send
70
+ (int _) :/
71
+ (rational _))
72
+ PATTERN
73
+
74
+ def_node_matcher :duration?, <<~PATTERN
75
+ {
76
+ (send {int float rational #rational_literal?} #duration_method?)
77
+ (send (const (const _ :ActiveSupport) :Duration) ...)
78
+ }
79
+ PATTERN
80
+
81
+ def_node_matcher :date_time_send?, <<~PATTERN
82
+ $(send
83
+ `{
84
+ (const _ {:Date :DateTime :Time})
85
+ (const (const _ :ActiveSupport) :TimeWithZone)
86
+ }
87
+ ...
88
+ )
89
+ PATTERN
90
+
91
+ def_node_matcher :date_time_arg?, <<~PATTERN
92
+ { #duration? #date_time_send? (send `#duration? #duration_to_time_method?) }
93
+ PATTERN
94
+
95
+ def on_send(node)
96
+ sidekiq_arguments(node).each do |arg|
97
+ next unless date_time_arg?(arg)
98
+ next if node_approved?(arg)
99
+
100
+ # If the outer send (ie. the last method in the chain) is in the allowed method
101
+ # list, approve the node (so that sub chains aren't flagged).
102
+ if allowed_methods.include?(arg.method_name)
103
+ approve_node(arg)
104
+ next
105
+ end
106
+
107
+ add_offense(arg)
108
+ end
109
+ end
110
+
111
+ def allowed_methods
112
+ Array(cop_config['AllowedMethods']).concat(ALLOWED_METHODS).map(&:to_sym)
113
+ end
114
+
115
+ def duration_method?(sym)
116
+ DURATION_METHODS.include?(sym)
117
+ end
118
+
119
+ def duration_to_time_method?(sym)
120
+ DURATION_TO_TIME_METHODS.include?(sym)
121
+ end
122
+
123
+ def message(node)
124
+ return DURATION_MSG if duration?(node)
125
+
126
+ super
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ # credit: https://github.com/dvandersluis/rubocop-sidekiq/blob/master/lib/rubocop/cop/helpers.rb
4
+ module RuboCop
5
+ module Cop
6
+ module Sidekiq
7
+ module Helpers
8
+ NODE_MATCHERS = lambda do
9
+ def_node_matcher :sidekiq_include?, <<~PATTERN
10
+ (send nil? :include (const (const nil? :Sidekiq) :Worker))
11
+ PATTERN
12
+
13
+ def_node_matcher :includes_sidekiq?, <<~PATTERN
14
+ {
15
+ (begin <#sidekiq_include? ...>)
16
+ #sidekiq_include?
17
+ }
18
+ PATTERN
19
+
20
+ def_node_matcher :worker_class_def?, <<~PATTERN
21
+ (class _ _ #includes_sidekiq?)
22
+ PATTERN
23
+
24
+ def_node_matcher :worker_anon_class_def?, <<~PATTERN
25
+ (block (send (const nil? :Class) :new ...) _ #includes_sidekiq?)
26
+ PATTERN
27
+
28
+ def_node_matcher :worker_class_application_worker?, <<~PATTERN
29
+ (class _ (const {nil? cbase} :ApplicationWorker) ...)
30
+ PATTERN
31
+
32
+ def_node_matcher :worker_anon_class_application_worker_def?, <<~PATTERN
33
+ (block (send (const nil? :Class) :new (const {nil? cbase} :ApplicationWorker)) ...)
34
+ PATTERN
35
+
36
+ def_node_matcher :sidekiq_worker?, <<~PATTERN
37
+ {#worker_class_def? #worker_anon_class_def? #worker_class_application_worker? #worker_anon_class_application_worker_def?}
38
+ PATTERN
39
+
40
+ def_node_matcher :sidekiq_perform?, <<~PATTERN
41
+ (send const ${:perform_async :perform_in :perform_at} ...)
42
+ PATTERN
43
+ end
44
+
45
+ def self.included(klass)
46
+ klass.class_exec(&NODE_MATCHERS)
47
+ end
48
+
49
+ def in_sidekiq_worker?(node)
50
+ node.each_ancestor(:class, :block).detect { |anc| sidekiq_worker?(anc) }
51
+ end
52
+
53
+ def sidekiq_arguments(node)
54
+ return [] unless node.send_type? && (method_name = sidekiq_perform?(node))
55
+
56
+ # Drop the first argument for perform_at and perform_in
57
+ expand_arguments(method_name == :perform_async ? node.arguments : node.arguments[1..])
58
+ end
59
+
60
+ def expand_arguments(arguments)
61
+ arguments.flat_map do |argument|
62
+ if argument.array_type? || argument.hash_type?
63
+ expand_arguments(argument.values)
64
+ else
65
+ argument
66
+ end
67
+ end
68
+ end
69
+
70
+ def node_approved?(node)
71
+ @approved_nodes ||= []
72
+ @approved_nodes.any? { |r| within?(node.source_range, r) }
73
+ end
74
+ alias node_denied? node_approved?
75
+
76
+ def approve_node(node)
77
+ @approved_nodes ||= []
78
+ @approved_nodes << node.source_range
79
+ end
80
+ alias deny_node approve_node
81
+
82
+ def within?(inner, outer)
83
+ inner.begin_pos >= outer.begin_pos && inner.end_pos <= outer.end_pos
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+
5
+ # credit: https://github.com/dvandersluis/rubocop-sidekiq/blob/master/lib/rubocop/cop/sidekiq/keyword_arguments.rb
6
+ module RuboCop
7
+ module Cop
8
+ module Sidekiq
9
+ # This cop checks for Sidekiq worker `perform` methods that use keyword args. Keyword args
10
+ # cannot be properly serialized to Redis and are thus not recommended. Use regular arguments
11
+ # instead.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # class MyWorker
16
+ # include Sidekiq::Worker
17
+ #
18
+ # def perform(id:, keyword_with_default: false, **other_kwargs)
19
+ # end
20
+ # end
21
+ #
22
+ # # good
23
+ # class MyWorker
24
+ # include Sidekiq::Worker
25
+ #
26
+ # def perform(id, arg_with_default = false, *other_args)
27
+ # end
28
+ # end
29
+ class KeywordArguments < Base
30
+ include Helpers
31
+
32
+ MSG = "Keyword arguments are not allowed in a sidekiq worker's perform method."
33
+ KWARG_TYPES = %i[kwarg kwoptarg kwrestarg].freeze
34
+
35
+ def_node_matcher :perform_with_kwargs?, <<~PATTERN
36
+ (def :perform (args ... {kwarg kwoptarg kwrestarg}) ...)
37
+ PATTERN
38
+
39
+ def on_def(node)
40
+ return unless perform_with_kwargs?(node)
41
+ return unless in_sidekiq_worker?(node)
42
+
43
+ node.arguments.each do |arg|
44
+ next unless KWARG_TYPES.include?(arg.type)
45
+
46
+ add_offense(arg)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+
5
+ # credit: https://github.com/dvandersluis/rubocop-sidekiq/blob/master/lib/rubocop/cop/sidekiq/symbol_argument.rb
6
+ module RuboCop
7
+ module Cop
8
+ module Sidekiq
9
+ # This cop checks for symbols passed as arguments to a Sidekiq worker's perform method.
10
+ # Symbols cannot be properly serialized for Redis and should be avoided. Use strings instead.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # MyWorker.perform_async(:foo)
15
+ #
16
+ # # good
17
+ # MyWorker.perform_async('foo')
18
+ class SymbolArgument < Base
19
+ include Helpers
20
+
21
+ MSG = 'Symbols are not Sidekiq-serializable; use strings instead.'
22
+
23
+ def on_send(node)
24
+ sidekiq_arguments(node).select(&:sym_type?).each do |argument|
25
+ add_offense(argument)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Petal
5
- VERSION = '1.3.0'
5
+ VERSION = '1.4.0'
6
6
  end
7
7
  end
data/lib/rubocop/petal.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'petal/version'
4
+ require 'yaml'
4
5
 
5
6
  module RuboCop
6
7
  module Petal
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-petal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean-Francis Bastien
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-09 00:00:00.000000000 Z
11
+ date: 2024-06-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -116,13 +116,17 @@ files:
116
116
  - lib/rubocop/cop/rspec/aggregate_examples/matchers_with_side_effects.rb
117
117
  - lib/rubocop/cop/rspec/aggregate_examples/metadata_helpers.rb
118
118
  - lib/rubocop/cop/rspec/aggregate_examples/node_matchers.rb
119
- - lib/rubocop/cop/rspec/authenticated_as.rb
120
119
  - lib/rubocop/cop/rspec/create_list_max.rb
121
120
  - lib/rubocop/cop/rspec/json_response.rb
122
121
  - lib/rubocop/cop/rspec/multiple_expectations_auto_correct.rb
123
122
  - lib/rubocop/cop/rspec/sidekiq_inline.rb
124
123
  - lib/rubocop/cop/rspec/stub_products.rb
124
+ - lib/rubocop/cop/sidekiq/const_argument.rb
125
+ - lib/rubocop/cop/sidekiq/date_time_argument.rb
126
+ - lib/rubocop/cop/sidekiq/helpers.rb
127
+ - lib/rubocop/cop/sidekiq/keyword_arguments.rb
125
128
  - lib/rubocop/cop/sidekiq/no_nil_return.rb
129
+ - lib/rubocop/cop/sidekiq/symbol_argument.rb
126
130
  - lib/rubocop/petal.rb
127
131
  - lib/rubocop/petal/inject.rb
128
132
  - lib/rubocop/petal/version.rb
@@ -150,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
154
  - !ruby/object:Gem::Version
151
155
  version: '0'
152
156
  requirements: []
153
- rubygems_version: 3.4.18
157
+ rubygems_version: 3.4.22
154
158
  signing_key:
155
159
  specification_version: 4
156
160
  summary: Petal custom cops
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module RSpec
6
- # Suggest to use authenticated_as instead of legacy api_key.
7
- # It is faster, can be setup for multiple test.
8
- #
9
- # # bad
10
- # get 'api/my_endpoint', headers: { HTTP_API_KEY: user.api_key }
11
- #
12
- # # good
13
- # authenticated_as user
14
- # get 'api/my_endpoint'
15
- #
16
- class AuthenticatedAs < Base
17
- MSG = 'Use `authenticated_as` instead of legacy api_key.'
18
-
19
- def_node_search :use_header_api_key, <<~PATTERN
20
- (sym :HTTP_API_KEY)
21
- PATTERN
22
-
23
- def on_send(node)
24
- api_key_usage = use_header_api_key(node).to_a.first
25
- return unless api_key_usage
26
-
27
- add_offense(api_key_usage)
28
- end
29
- end
30
- end
31
- end
32
- end