rubocop-gusto 10.0.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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +4 -0
  3. data/LICENSE +21 -0
  4. data/README.md +53 -0
  5. data/config/default.yml +781 -0
  6. data/config/rails.yml +122 -0
  7. data/exe/gusto-rubocop +12 -0
  8. data/exe/rubocop-gusto +9 -0
  9. data/lib/rubocop/cop/gusto/bootsnap_load_file.rb +57 -0
  10. data/lib/rubocop/cop/gusto/datadog_constant.rb +16 -0
  11. data/lib/rubocop/cop/gusto/execute_migration.rb +16 -0
  12. data/lib/rubocop/cop/gusto/factory_classes_or_modules.rb +19 -0
  13. data/lib/rubocop/cop/gusto/min_by_max_by.rb +45 -0
  14. data/lib/rubocop/cop/gusto/no_metaprogramming.rb +131 -0
  15. data/lib/rubocop/cop/gusto/no_rescue_error_message_checking.rb +66 -0
  16. data/lib/rubocop/cop/gusto/no_send.rb +32 -0
  17. data/lib/rubocop/cop/gusto/object_in.rb +36 -0
  18. data/lib/rubocop/cop/gusto/paperclip_or_attachable.rb +17 -0
  19. data/lib/rubocop/cop/gusto/perform_class_method.rb +73 -0
  20. data/lib/rubocop/cop/gusto/polymorphic_type_validation.rb +89 -0
  21. data/lib/rubocop/cop/gusto/prefer_process_last_status.rb +35 -0
  22. data/lib/rubocop/cop/gusto/rabl_extends.rb +43 -0
  23. data/lib/rubocop/cop/gusto/rails_env.rb +72 -0
  24. data/lib/rubocop/cop/gusto/rake_constants.rb +68 -0
  25. data/lib/rubocop/cop/gusto/regexp_bypass.rb +90 -0
  26. data/lib/rubocop/cop/gusto/sidekiq_params.rb +21 -0
  27. data/lib/rubocop/cop/gusto/toplevel_constants.rb +55 -0
  28. data/lib/rubocop/cop/gusto/use_paint_not_colorize.rb +240 -0
  29. data/lib/rubocop/cop/gusto/vcr_recordings.rb +49 -0
  30. data/lib/rubocop/cop/internal_affairs/assignment_first.rb +56 -0
  31. data/lib/rubocop/cop/internal_affairs/require_restrict_on_send.rb +62 -0
  32. data/lib/rubocop/gusto/cli.rb +22 -0
  33. data/lib/rubocop/gusto/config_yml.rb +135 -0
  34. data/lib/rubocop/gusto/init.rb +59 -0
  35. data/lib/rubocop/gusto/plugin.rb +29 -0
  36. data/lib/rubocop/gusto/templates/rubocop.yml +25 -0
  37. data/lib/rubocop/gusto/version.rb +7 -0
  38. data/lib/rubocop/gusto.rb +9 -0
  39. data/lib/rubocop-gusto.rb +13 -0
  40. metadata +178 -0
data/config/rails.yml ADDED
@@ -0,0 +1,122 @@
1
+ #
2
+ # This file should be inherited alongside default.yml for Rails projects
3
+ #
4
+ # After you add a rule, sort this file with `bundle exec rubocop-gusto sort config/rails.yml`
5
+
6
+ plugins:
7
+ - rubocop-rails
8
+
9
+ AllCops:
10
+ TargetRubyVersion: <%= RbConfig::CONFIG['RUBY_API_VERSION'] %>
11
+ MaxFilesInCache: 100000
12
+ Exclude:
13
+ - './.[!.]*' # ignore all dotfiles
14
+ - './.[!.]*/**/*' # ignore all dot directories
15
+ - 'config/boot.rb'
16
+ - 'db/**/*schema.rb'
17
+ - 'db/seeds{.rb,/**/*}'
18
+
19
+ Rails:
20
+ Enabled: true
21
+
22
+ Rails/ActiveRecordAliases:
23
+ Enabled: false
24
+
25
+ Rails/ActiveRecordOverride:
26
+ Enabled: true
27
+
28
+ Rails/ApplicationRecord:
29
+ AutoCorrect: true
30
+
31
+ Rails/BulkChangeTable:
32
+ Enabled: false
33
+
34
+ Rails/Date:
35
+ AutoCorrect: false
36
+ Enabled: true
37
+
38
+ Rails/Delegate:
39
+ # Incompatible with Sorbet
40
+ Enabled: false
41
+ EnforceForPrefixed: false
42
+
43
+ Rails/DynamicFindBy:
44
+ AllowedMethods:
45
+ - find_by_sql
46
+ - find_by_uuid_or_id
47
+ - find_by_uuid_or_id!
48
+
49
+ Rails/EagerEvaluationLogMessage:
50
+ Enabled: true
51
+ AutoCorrect: true
52
+
53
+ Rails/EnumHash:
54
+ Include:
55
+ - '**/app/models/**/*'
56
+
57
+ Rails/Exit:
58
+ Include:
59
+ - app/**/*
60
+ - config/**/*
61
+ - lib/**/*
62
+ - packs/**/{app,lib}/**/*
63
+
64
+ Rails/FindEach:
65
+ Include:
66
+ - '**/app/models/**/*'
67
+
68
+ Rails/HasAndBelongsToMany:
69
+ Include:
70
+ - '**/app/models/**/*'
71
+
72
+ Rails/HasManyOrHasOneDependent:
73
+ Enabled: false
74
+
75
+ Rails/HelperInstanceVariable:
76
+ Include:
77
+ - '**/app/helpers/**/*'
78
+
79
+ Rails/HttpPositionalArguments:
80
+ AutoCorrect: true
81
+ Include:
82
+ - packs/**/spec/{controllers,fixture_generators}/**/*
83
+ - spec/{controllers,fixture_generators}/**/*
84
+
85
+ Rails/InverseOf:
86
+ Include:
87
+ - '**/app/models/**/*'
88
+
89
+ Rails/LexicallyScopedActionFilter:
90
+ Include:
91
+ - '**/app/controllers/**/*'
92
+ - '**/app/mailers/**/*'
93
+
94
+ Rails/NotNullColumn:
95
+ Enabled: false
96
+
97
+ Rails/ReadWriteAttribute:
98
+ Enabled: false
99
+
100
+ Rails/SaveBang:
101
+ SafeAutoCorrect: false
102
+
103
+ Rails/SkipsModelValidations:
104
+ Enabled: true
105
+
106
+ Rails/TimeZone:
107
+ Enabled: true
108
+
109
+ Rails/UniqueValidationWithoutIndex:
110
+ Enabled: true
111
+
112
+ Rails/UnknownEnv:
113
+ Environments:
114
+ - demo
115
+ - development
116
+ - production
117
+ - staging
118
+ - test
119
+
120
+ Rails/Validation:
121
+ Include:
122
+ - '**/models/**/*'
data/exe/gusto-rubocop ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ # typed: strict
4
+
5
+ # This file is here to conform to our gusto-* convention.
6
+ # rubocop gems have their own naming convention so we're doing both.
7
+
8
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
9
+
10
+ require 'rubocop/gusto/cli'
11
+
12
+ RuboCop::Gusto::Cli.start(ARGV)
data/exe/rubocop-gusto ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ # typed: strict
4
+
5
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
6
+
7
+ require 'rubocop/gusto/cli'
8
+
9
+ RuboCop::Gusto::Cli.start(ARGV)
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ # Do not use Bootsnap to load files. Use `require` instead.
7
+ class BootsnapLoadFile < Base
8
+ PROHIBITED_CONSTANTS = Set[:YAML, :JSON].freeze
9
+ RESTRICT_ON_SEND = %i(load).freeze
10
+
11
+ # @!method yaml_or_json_load(node)
12
+ def_node_matcher :yaml_or_json_load, '(send $(const nil? PROHIBITED_CONSTANTS) :load ...)'
13
+
14
+ # @!method file_read(node)
15
+ def_node_matcher :file_read, '(send (const nil? :File) :read $_)'
16
+
17
+ # @!method load_inside_file_open(node)
18
+ def_node_matcher :load_inside_file_open, <<~PATTERN
19
+ (block
20
+ (send
21
+ (const nil? :File) :open
22
+ $(str _))
23
+ (args
24
+ (arg _file))
25
+ (send
26
+ $(const nil? :YAML) :load
27
+ (lvar _file))
28
+ )
29
+ PATTERN
30
+
31
+ def on_block(node)
32
+ load_inside_file_open(node) do |file_path_node, constant_node|
33
+ add_offense(node, message: "Use #{constant_node.source}.load_file(#{file_path_node.source}) to improve load time with bootsnap")
34
+ end
35
+ end
36
+ alias_method :on_numblock, :on_block
37
+
38
+ def on_send(node)
39
+ yaml_or_json_load(node) do |constant_node|
40
+ on_load(node, constant_node)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ # Look for File.read as the first argument
47
+ def on_load(node, constant_node)
48
+ return unless node.first_argument
49
+
50
+ file_read(node.first_argument) do |read_file_node|
51
+ add_offense(node, message: "Use #{constant_node.source}.load_file(#{read_file_node.source}) to improve load time with bootsnap")
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ class DatadogConstant < Base
7
+ MSG = 'Do not call Datadog directly, use an appropriate wrapper library.'
8
+ NAMESPACE = 'Datadog'
9
+
10
+ def on_const(node)
11
+ add_offense(node) if node.const_name == NAMESPACE
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ class ExecuteMigration < Base
7
+ MSG = 'Do not use `execute` to run raw SQL in a migration. Run the query from a backfill rake task or pass the SQL options to the `add_column`/`change_column` method.'
8
+ RESTRICT_ON_SEND = [:execute].freeze
9
+
10
+ def on_send(node)
11
+ add_offense(node)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ class FactoryClassesOrModules < Base
7
+ MSG = 'Do not define modules or classes in factory directories - they break reloading'
8
+
9
+ def on_class(node)
10
+ add_offense(node)
11
+ end
12
+
13
+ def on_module(node)
14
+ add_offense(node)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ # Checks for the use of `min` or `max` with a proc. Corrects to `min_by` or `max_by`.
7
+ #
8
+ # @safety This cop is unsafe because it will change the behavior of the code.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # arr = [[3, 3, 3], [2, 2], [1]]
13
+ # arr.min(&:count)
14
+ # => [3, 3, 3] - oh no how did this happen?
15
+ # arr = [[2,2],[1,1],[3,3,3]]
16
+ # arr.min &:first # => TypeError: no implicit conversion of Array into Integer
17
+ # arr = [[1, 1], [3, 3], [2, 2]]
18
+ # arr.max { |pair| pair.first } # => [2, 2] (semantically incorrect)
19
+ #
20
+ # # good
21
+ # arr = [[2,2],[1],[3,3,3]]
22
+ # arr.min_by &:first # => [1]
23
+ # arr = [[1, 1], [3, 3], [2, 2]]
24
+ # arr.max_by { |pair| pair.first } # => [3, 3]
25
+ #
26
+ class MinByMaxBy < Base
27
+ extend AutoCorrector
28
+
29
+ MSG = 'Use `%{method}_by` instead of `%{method}` with a proc like `&:my_method_proc`. `%{method}` expects Comparable elements.'
30
+ RESTRICT_ON_SEND = %i(min max).freeze
31
+
32
+ def on_send(node)
33
+ return unless node.arguments?
34
+ return unless node.first_argument.block_pass_type?
35
+
36
+ method_name = node.method_name
37
+ add_offense(node, message: format(MSG, method: method_name)) do |corrector|
38
+ corrector.replace(node, node.source.sub(method_name.to_s, "#{method_name}_by"))
39
+ end
40
+ end
41
+ alias_method :on_csend, :on_send
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ # Checks and discourages the use of metaprogramming techniques that make code harder to
7
+ # understand, debug, and maintain.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad - using define_method
12
+ # define_method(:my_method) do |arg|
13
+ # puts arg
14
+ # end
15
+ #
16
+ # # good - using regular method definition
17
+ # def my_method(arg)
18
+ # puts arg
19
+ # end
20
+ #
21
+ # # bad - using instance_eval
22
+ # object.instance_eval do
23
+ # def foo
24
+ # bar
25
+ # end
26
+ # end
27
+ #
28
+ # # good - defining methods on the class
29
+ # class MyClass
30
+ # def foo
31
+ # bar
32
+ # end
33
+ # end
34
+ #
35
+ # # bad - using method_missing
36
+ # def method_missing(method, *args)
37
+ # if method.to_s.start_with?('find_by_')
38
+ # # ...
39
+ # end
40
+ # end
41
+ #
42
+ # # bad - using define_singleton_method
43
+ # object.define_singleton_method(:foo) { bar }
44
+ #
45
+ # # good - define class methods directly
46
+ # def self.foo
47
+ # bar
48
+ # end
49
+ #
50
+ # # bad - using class_eval
51
+ # MyClass.class_eval do
52
+ # def foo
53
+ # bar
54
+ # end
55
+ # end
56
+ #
57
+ class NoMetaprogramming < Base
58
+ RESTRICT_ON_SEND = %i(define_method instance_eval define_singleton_method class_eval).freeze
59
+
60
+ # @!method included_definition?(node)
61
+ def_node_matcher :included_definition?, <<~PATTERN
62
+ (defs self :included ...)
63
+ PATTERN
64
+
65
+ # @!method inherited_definition?(node)
66
+ def_node_matcher :inherited_definition?, <<~PATTERN
67
+ (defs self :inherited ...)
68
+ PATTERN
69
+
70
+ # @!method using_method_missing?(node)
71
+ def_node_matcher :using_method_missing?, <<~PATTERN
72
+ (def :method_missing ...)
73
+ PATTERN
74
+
75
+ # @!method using_define_method?(node)
76
+ def_node_matcher :using_define_method?, <<~PATTERN
77
+ (send _ :define_method ...)
78
+ PATTERN
79
+
80
+ # @!method using_instance_eval?(node)
81
+ def_node_matcher :using_instance_eval?, <<~PATTERN
82
+ (send _ :instance_eval ...)
83
+ PATTERN
84
+
85
+ # @!method using_class_eval?(node)
86
+ def_node_matcher :using_class_eval?, <<~PATTERN
87
+ (send _ :class_eval ...)
88
+ PATTERN
89
+
90
+ # @!method using_define_singleton_method_on_klass_instance?(node)
91
+ def_node_matcher :using_define_singleton_method_on_klass_instance?, <<~PATTERN
92
+ (send _ :define_singleton_method ...)
93
+ PATTERN
94
+
95
+ def on_defs(node)
96
+ included_definition?(node) do
97
+ add_offense(node, message: 'self.included modifies the behavior of classes at runtime. Please avoid using if possible.')
98
+ end
99
+
100
+ inherited_definition?(node) do
101
+ add_offense(node, message: 'self.inherited modifies the behavior of classes at runtime. Please avoid using if possible.')
102
+ end
103
+ end
104
+
105
+ def on_def(node)
106
+ using_method_missing?(node) do
107
+ add_offense(node, message: 'Please do not use method_missing. Instead, explicitly define the methods you expect to receive.')
108
+ end
109
+ end
110
+
111
+ def on_send(node)
112
+ using_define_method?(node) do
113
+ add_offense(node, message: 'Please do not define methods dynamically, instead define them using `def` and explicitly. This helps readability for both humans and machines.')
114
+ end
115
+
116
+ using_define_singleton_method_on_klass_instance?(node) do
117
+ add_offense(node, message: 'Please do not use define_singleton_method. Instead, define the method explicitly using `def self.my_method; end`')
118
+ end
119
+
120
+ using_instance_eval?(node) do
121
+ add_offense(node, message: 'Please do not use instance_eval to augment behavior onto an instance. Instead, define the method you want to use in the class definition.')
122
+ end
123
+
124
+ using_class_eval?(node) do
125
+ add_offense(node, message: 'Please do not use class_eval to augment behavior onto a class. Instead, define the method you want to use in the class definition.')
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ # Checks for the presence of error message checking within rescue blocks.
7
+ #
8
+ # This is brittle and can break easily.
9
+ #
10
+ # NOTE: We submitted this upstream here: https://github.com/rubocop/rubocop/pull/13352
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # begin
16
+ # something
17
+ # rescue => e
18
+ # if e.message.match?(/Duplicate entry/)
19
+ # handle_error
20
+ # end
21
+ # end
22
+ #
23
+ # # bad
24
+ # begin
25
+ # something
26
+ # rescue => e
27
+ # unless e.message.match?(/Duplicate entry/)
28
+ # handle_error
29
+ # end
30
+ # end
31
+ #
32
+ # # good
33
+ # begin
34
+ # something
35
+ # rescue ActiveRecord::RecordNotUnique => e
36
+ # handle_error
37
+ # end
38
+ #
39
+ class NoRescueErrorMessageChecking < Base
40
+ MSG = 'Avoid checking error message while handling exceptions. This is brittle and can break easily.'
41
+ METHODS_TO_CHECK = %i(match? include? ==).to_set.freeze
42
+
43
+ def on_rescue(node)
44
+ node.resbody_branches.last.each_descendant(:if, :unless).each do |condition_node|
45
+ add_offense(condition_node) if message_check?(condition_node)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def message_check?(condition_node)
52
+ return unless condition_node.condition.send_type?
53
+ return unless condition_node.condition.receiver
54
+ return unless METHODS_TO_CHECK.include?(condition_node.condition.method_name)
55
+
56
+ if condition_node.condition.method?(:==)
57
+ (condition_node.condition.receiver.str_type? && condition_node.condition.first_argument.method?(:message)) ||
58
+ (condition_node.condition.receiver.send_type? && condition_node.condition.receiver.method?(:message))
59
+ else
60
+ condition_node.condition.receiver.type?(:send, :regexp, :array)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ # Do not call a private method via __send__
7
+ #
8
+ # @example
9
+ # # bad
10
+ # foo.__send__(:bar)
11
+ # __send__(:run_baz)
12
+ #
13
+ # # good
14
+ # There's no better alternative, don't call private methods.
15
+ #
16
+ class NoSend < Base
17
+ MSG = 'Do not call a private method via `__send__`.'
18
+ RESTRICT_ON_SEND = %i(__send__).freeze
19
+
20
+ # @!method invoke_private_method_send?(node)
21
+ def_node_matcher :invoke_private_method_send?, <<~PATTERN
22
+ (call _ :__send__ (sym _))
23
+ PATTERN
24
+
25
+ def on_send(node)
26
+ invoke_private_method_send?(node) { add_offense(node) }
27
+ end
28
+ alias_method :on_csend, :on_send
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ # Identifies uses of `Object#in?`, which iterates over each
7
+ # item in a `Range` to see if a specified item is there. In contrast,
8
+ # `Range#cover?` simply compares the target item with the beginning and
9
+ # end points of the `Range`. In a great majority of cases, this is what
10
+ # is wanted.
11
+ #
12
+ # @safety
13
+ # This cop is unsafe. Here is an example of a case where `Range#cover?`
14
+ # may not provide the desired result:
15
+ #
16
+ # ('a'..'z').cover?('yellow') # => true
17
+ #
18
+ class ObjectIn < Base
19
+ MSG = 'Use `Range#cover?` instead of `Object#in?`.'
20
+ RESTRICT_ON_SEND = [:in?].freeze
21
+
22
+ # @!method object_in(node)
23
+ def_node_matcher :object_in, <<-PATTERN
24
+ (call _ :in? {range (begin range)})
25
+ PATTERN
26
+
27
+ def on_send(node)
28
+ return unless object_in(node)
29
+
30
+ add_offense(node)
31
+ end
32
+ alias_method :on_csend, :on_send
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ # typed: ignore
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Gusto
7
+ class PaperclipOrAttachable < Base
8
+ MSG = 'No more new paperclip or Attachable are allowed. New attachments should use ActiveStorage instead'
9
+ RESTRICT_ON_SEND = %i(has_attached_file has_pdf_attachment has_attachment).freeze
10
+
11
+ def on_send(node)
12
+ add_offense(node)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gusto
6
+ # Checks for a defined `.perform` class method in Sidekiq workers. These
7
+ # are most likely intended to have been instance methods.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # class MyWorker
12
+ # include Sidekiq::Worker
13
+ #
14
+ # def self.perform
15
+ # # ...
16
+ # end
17
+ # end
18
+ #
19
+ # # good
20
+ # class MyWorker
21
+ # include Sidekiq::Worker
22
+ #
23
+ # def perform
24
+ # # ...
25
+ # end
26
+ # end
27
+ #
28
+ class PerformClassMethod < Base
29
+ MSG = 'Class-level `perform` method is being defined. Did you mean to use an instance method?'
30
+ WORKER_FALLBACK = %w(Sidekiq::Worker).freeze
31
+ WORKER_MODULES = 'WorkerModules'
32
+
33
+ def on_def(node)
34
+ return unless node.method?(:perform)
35
+ return unless (method_type = perform_class_method_type(node))
36
+ return unless is_sidekiq_worker?(node, method_type)
37
+
38
+ add_offense(node)
39
+ end
40
+ alias_method :on_defs, :on_def
41
+
42
+ private
43
+
44
+ def perform_class_method_type(node)
45
+ if node.receiver&.self_type?
46
+ :self
47
+ elsif node.parent.sclass_type?
48
+ :sclass
49
+ end
50
+ end
51
+
52
+ def is_sidekiq_worker?(search_node, method_type)
53
+ search_node = search_node.parent if method_type == :sclass
54
+ search_node.parent.children.any? do |sibling|
55
+ next if sibling.nil?
56
+ next unless is_include?(sibling)
57
+ next unless sibling.first_argument.const_type?
58
+
59
+ worker_modules.include?(sibling.first_argument.const_name)
60
+ end
61
+ end
62
+
63
+ def is_include?(node)
64
+ node.send_type? && node.method?(:include)
65
+ end
66
+
67
+ def worker_modules
68
+ @worker_modules ||= cop_config.fetch(WORKER_MODULES, WORKER_FALLBACK)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end