rubocop-packs 0.0.26 → 0.0.28

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: b087fdd5a5ae2a9a51a04d24966313c19a2892153d656beac60bdb15e959726e
4
- data.tar.gz: e4ff803bc9a2a37dd68867da260df1f68c7d8adc666a9cb141dcc95a6ac1e448
3
+ metadata.gz: e4888c0b36e59d1da1aaa62d1390da897fce811909d99c5e1189bca0efd36050
4
+ data.tar.gz: 7d1957e8c598a6acbcfa3b0b8f49578dcb3424d01c20178c2045459654a2478f
5
5
  SHA512:
6
- metadata.gz: 234d22b504eb15892fd02bdab4783e475ab12ec0b6024f5d36519aca0efc1feaa5ac8616f8ba8f242086e5ac100d76edd282c282045136df26b5035954f6306d
7
- data.tar.gz: 56056baf7a26ab3c2bc21038de1f34e418d01d2f0851fe1eb1234d7ec63e069a930505cf8e858fe6ac913ba5556b8ec230a024efbd7e97b0743756f3fac90c3f
6
+ metadata.gz: b55558140df2b0d0e68f64668a94e814c30a6ac42b144d0dc7fbc7118bdf982fb87b45085592ca219946ae6a2df73bb22562a83ae888fb021343ec0cea7c685c
7
+ data.tar.gz: fa54dc58358e0f32104c2acfa0536ff6239430c287a75e5ad0a346ae9979360f59a67037a34599e98a1df525cbe6c31c7350af95b6c33dba1dcdacb02cbdfe88
data/config/default.yml CHANGED
@@ -5,6 +5,7 @@ Packs/ClassMethodsAsPublicApis:
5
5
  - T::Struct
6
6
  - Struct
7
7
  - OpenStruct
8
+ AcceptableMixins: []
8
9
  FailureMode: default
9
10
 
10
11
  Packs/RootNamespaceIsPackName:
@@ -10,6 +10,7 @@ module RuboCop
10
10
  # Options:
11
11
  #
12
12
  # * `AcceptableParentClasses`: A list of classes that, if inherited from, non-class methods are permitted (useful when value objects are a part of your public API)
13
+ # * `AcceptableMixins`: A list of modules that, if included, non-class methods are permitted
13
14
  #
14
15
  # @example
15
16
  #
@@ -47,11 +48,14 @@ module RuboCop
47
48
 
48
49
  acceptable_parent_classes = cop_config['AcceptableParentClasses'] || []
49
50
 
50
- # Used this PR as inspiration to check if we're within a `class << self` block
51
51
  uses_implicit_static_methods = node.each_ancestor(:sclass).first&.identifier&.source == 'self'
52
52
  class_is_allowed_to_have_instance_methods = acceptable_parent_classes.include?(parent_class&.const_name)
53
53
  return if uses_implicit_static_methods || class_is_allowed_to_have_instance_methods
54
54
 
55
+ is_sorbet_interface_or_abstract_class = !module_node.nil? && module_node.descendants.any? { |d| d.is_a?(RuboCop::AST::SendNode) && (d.method_name == :interface! || d.method_name == :abstract!) }
56
+ return if is_sorbet_interface_or_abstract_class
57
+ return if node_includes_acceptable_mixin?(class_node || module_node)
58
+
55
59
  add_offense(
56
60
  node.source_range,
57
61
  message: format(
@@ -59,6 +63,22 @@ module RuboCop
59
63
  )
60
64
  )
61
65
  end
66
+
67
+ private
68
+
69
+ sig { params(node: T.untyped).returns(T::Boolean) }
70
+ def node_includes_acceptable_mixin?(node)
71
+ acceptable_mixins = cop_config['AcceptableMixins'] || []
72
+ return false if node.nil?
73
+
74
+ node.descendants.any? do |d|
75
+ d.is_a?(RuboCop::AST::SendNode) &&
76
+ d.method_name == :include &&
77
+ d.arguments.count == 1 &&
78
+ d.arguments.first.is_a?(RuboCop::AST::ConstNode) &&
79
+ acceptable_mixins.include?(d.arguments.first.const_name)
80
+ end
81
+ end
62
82
  end
63
83
  end
64
84
  end
@@ -148,20 +148,22 @@ module RuboCop
148
148
  errors
149
149
  end
150
150
 
151
- sig { params(args: T.untyped).returns(String) }
151
+ sig { params(args: T.untyped).void }
152
152
  def self.execute_rubocop(args)
153
- with_captured_stdout do
154
- RuboCop::CLI.new.run(args)
155
- end
153
+ RuboCop::CLI.new.run(args)
156
154
  end
157
155
 
158
156
  sig { params(paths: T::Array[String], cop_names: T::Array[String]).returns(T::Array[Offense]) }
159
157
  def self.offenses_for(paths:, cop_names:)
160
158
  cop_arguments = cop_names.join(',')
161
159
  # I think we can potentially use `RuboCop::CLI.new(args)` for this to avoid shelling out and starting another process that needs to reload the bundle
162
- args = [*paths, "--only=#{cop_arguments}", '--format=json']
160
+ args = [*paths, "--only=#{cop_arguments}", '--format=json', '--out=tmp/rubocop-output']
161
+ FileUtils.mkdir_p('tmp')
163
162
  puts "Executing: bundle exec rubocop #{args.join(' ')}"
164
- json = JSON.parse(Private.execute_rubocop(args))
163
+ Private.execute_rubocop(args)
164
+ output = Pathname.new('tmp/rubocop-output')
165
+ json = JSON.parse(Pathname.new('tmp/rubocop-output').read)
166
+ output.delete
165
167
  offenses = T.let([], T::Array[Offense])
166
168
  json['files'].each do |file_hash|
167
169
  filepath = file_hash['path']
@@ -175,16 +177,6 @@ module RuboCop
175
177
 
176
178
  offenses
177
179
  end
178
-
179
- sig { params(block: T.untyped).returns(String) }
180
- def self.with_captured_stdout(&block)
181
- original_stdout = $stdout # capture previous value of $stdout
182
- $stdout = StringIO.new # assign a string buffer to $stdout
183
- yield # perform the body of the user code
184
- $stdout.string # return the contents of the string buffer
185
- ensure
186
- $stdout = original_stdout # restore $stdout to its previous value
187
- end
188
180
  end
189
181
 
190
182
  private_constant :Private
data/lib/rubocop/packs.rb CHANGED
@@ -28,6 +28,12 @@ module RuboCop
28
28
  #
29
29
  sig { params(packs: T::Array[ParsePackwerk::Package], files: T::Array[String]).void }
30
30
  def self.regenerate_todo(packs: [], files: [])
31
+ # Delete the old pack-level rubocop todo files so that we can regenerate the new one from scratch
32
+ packs.each do |pack|
33
+ rubocop_todo_yml = pack.directory.join(PACK_LEVEL_RUBOCOP_TODO_YML)
34
+ rubocop_todo_yml.delete if rubocop_todo_yml.exist?
35
+ end
36
+
31
37
  paths = packs.empty? ? files : packs.map(&:name).reject { |name| name == ParsePackwerk::ROOT_PACKAGE_NAME }
32
38
  offenses = Private.offenses_for(
33
39
  paths: paths,
@@ -39,19 +45,17 @@ module RuboCop
39
45
  next if !pack.directory.join(PACK_LEVEL_RUBOCOP_YML).exist?
40
46
 
41
47
  rubocop_todo_yml = pack.directory.join(PACK_LEVEL_RUBOCOP_TODO_YML)
42
- # If the user is passing in packs, then regenerate from scratch.
43
- if packs.any? && rubocop_todo_yml.exist?
44
- rubocop_todo_yml.delete
45
- rubocop_todo = {}
46
- elsif rubocop_todo_yml.exist?
48
+ if rubocop_todo_yml.exist?
47
49
  rubocop_todo = YAML.load_file(rubocop_todo_yml)
48
50
  else
49
51
  rubocop_todo = {}
50
52
  end
51
53
 
52
- offenses_for_pack.each do |offense|
53
- rubocop_todo[offense.cop_name] ||= { 'Exclude' => [] }
54
- rubocop_todo[offense.cop_name]['Exclude'] << offense.filepath
54
+ offenses_for_pack.group_by(&:filepath).each do |filepath, offenses_by_filepath|
55
+ offenses_by_filepath.map(&:cop_name).uniq.each do |cop_name|
56
+ rubocop_todo[cop_name] ||= { 'Exclude' => [] }
57
+ rubocop_todo[cop_name]['Exclude'] << filepath
58
+ end
55
59
  end
56
60
 
57
61
  next if rubocop_todo.empty?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-packs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.26
4
+ version: 0.0.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-19 00:00:00.000000000 Z
11
+ date: 2022-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -178,7 +178,7 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
- description: Fill this out!
181
+ description: A collection of Rubocop rules for gradually modularizing a ruby codebase
182
182
  email:
183
183
  - dev@gusto.com
184
184
  executables: []
@@ -230,5 +230,5 @@ requirements: []
230
230
  rubygems_version: 3.1.6
231
231
  signing_key:
232
232
  specification_version: 4
233
- summary: Fill this out!
233
+ summary: A collection of Rubocop rules for gradually modularizing a ruby codebase
234
234
  test_files: []