rubocop-petal 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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