rubocop-gusto 10.1.1 → 10.3.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: 8a06f9920ea80223223c9353783cfc0a8824f232cc3e18c323195811a28eb28a
4
- data.tar.gz: 2943d0fd74fc5f31be0f0527e136ee034514d4215357c33e5c64207e377855e1
3
+ metadata.gz: 5b80d8e2199d3f26a7c573b61905ea30b866eb0a9f5ca2bf6eb0f6ef9891ecd4
4
+ data.tar.gz: 88a6fac9e262c3c40caa6ce939b194badf71a84cd05e63165b11a3f36ed4b54d
5
5
  SHA512:
6
- metadata.gz: 7dffff3eae0680c7ced3dfd4fc42eb98b37431a86aebcce90c0492a3c05f9c7f13a38c3634037aa3da96647d9857e8553af6ca155ba3707bbcf0a7e9d3fa2e42
7
- data.tar.gz: ebc7a8072aa0e99bf33f1596aa4502573c99006d28ba179f15fa5c32394d6dc27f94bebc322942c586157e6549a7b2c25b907fd3f4092f4f9f604c58bbd1579a
6
+ metadata.gz: 830e8793b87b42c2a727dcbbda20a93de35f5a4887009455e8ff65bc8002dc9499d5fdfadd6971e2e5ce73660e47ddbd4cdf53eeae67c5c26d4840dadda0f092
7
+ data.tar.gz: 1d6b49aa37716ac6095ae0e261f8673e5c2389bff7432ba5775d39b64a86f8f17725b2f35c4c1ffbea1e32cac36751c1ee7924d6251af30b5ea7bed60466bc1c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  ## Pending
2
2
 
3
+ ## 10.3.0
4
+
5
+ - Add Gusto/RspecDateTimeMock cop
6
+
7
+ ## 10.2.0
8
+
9
+ - Fix Sorbet sigil exclusions, exclude specs from strict, and add rubocop-sorbet dependency
10
+ - Add ActiveSupportExtensionsEnabled to rails config
11
+ - Fix infinite loop and splitting problem in init command
12
+ - Use default Style/InverseMethods configuration
13
+
14
+ ## 10.1.1
15
+
16
+ - Opt in to Rails/IgnoredColumnsAssignment
17
+ - Revert "Add Gusto/IgnoredColumnAssignment"
18
+ - Update rubocop dependency from 1.79.0 to 1.79.1
19
+
20
+ ## 10.1.0
21
+
22
+ - Add Gusto/IgnoredColumnAssignment cop
23
+
3
24
  ## 10.0.1
4
25
 
5
26
  - Require rubocop >= 1.76 to ensure Naming/PredicatePrefix is available.
data/config/default.yml CHANGED
@@ -2,6 +2,7 @@ plugins:
2
2
  - rubocop-rspec
3
3
  - rubocop-performance
4
4
  - rubocop-rake
5
+ - rubocop-sorbet
5
6
 
6
7
  # After you add a rule, sort this file with `bundle exec rubocop-gusto sort config/default.yml`
7
8
 
@@ -112,6 +113,12 @@ Gusto/RegexpBypass:
112
113
  - '**/spec/**/*'
113
114
  Safe: false
114
115
 
116
+ Gusto/RspecDateTimeMock:
117
+ Description: "Don't mock Date/Time/DateTime directly. Use Rails Testing Time Helpers (eg `freeze_time` and `travel_to`) instead."
118
+ Include:
119
+ - '**/spec/**/*'
120
+ Safe: false
121
+
115
122
  Gusto/SidekiqParams:
116
123
  Description: 'Sidekiq perform methods cannot take keyword arguments.'
117
124
 
@@ -133,6 +140,7 @@ Gusto/ToplevelConstants:
133
140
 
134
141
  Gusto/UsePaintNotColorize:
135
142
  Description: 'Use Paint instead of colorize for terminal colors.'
143
+ SafeAutoCorrect: false
136
144
 
137
145
  Gusto/VcrRecordings:
138
146
  Description: 'VCR should be set to not record in tests. Use vcr: {record: :none}.'
@@ -339,12 +347,6 @@ RSpec/DescribeMethod:
339
347
  RSpec/DescribedClass:
340
348
  Enabled: false
341
349
 
342
- RSpec/DescribedClassModuleWrapping:
343
- Enabled: true
344
- # TODO: improve this file matching pattern
345
- Include:
346
- - '**/spec/**/*_spec.rb'
347
-
348
350
  RSpec/ExampleLength:
349
351
  Enabled: false
350
352
 
@@ -426,7 +428,6 @@ Rake/ClassDefinitionInTask:
426
428
  Enabled: false
427
429
 
428
430
  Rake/Desc:
429
- # TODO: Roll this out.
430
431
  Enabled: true
431
432
 
432
433
  Security/YAMLLoad:
@@ -443,11 +444,7 @@ Sorbet:
443
444
  Sorbet/FalseSigil:
444
445
  # We want to avoid `typed: ignore` as much as possible, as it breaks LSP tooling.
445
446
  Include:
446
- - "**/*.{rb,rbi,rake,ru}"
447
- Exclude:
448
- - bin/**/*
449
- - db/**/*.rb
450
- - script/**/*
447
+ - "**/*.{rb,rbi,rake,ru}"
451
448
 
452
449
  Sorbet/Refinement:
453
450
  # Still marked pending upstream, we contributed this and enable it here.
@@ -457,6 +454,13 @@ Sorbet/StrictSigil:
457
454
  # Forgot the difference between typed levels? (ignore, false, true, strict, and strong)
458
455
  # Check this out: https://sorbet.org/docs/static#file-level-granularity-strictness-levels
459
456
  Enabled: true
457
+ Include:
458
+ - "**/*.{rb,rbi,rake,ru}"
459
+ Exclude:
460
+ - bin/**/*
461
+ - db/**/*.rb
462
+ - script/**/*
463
+ - spec/**/*spec.rb
460
464
 
461
465
  Sorbet/ValidSigil:
462
466
  Enabled: true
@@ -466,7 +470,6 @@ Sorbet/ValidSigil:
466
470
  # We do suggest that the user type their file as `typed: strict`
467
471
  SuggestedStrictness: strict
468
472
  Exclude:
469
- - '**/spec/**/*'
470
473
  - '**/db/migrate/**/*'
471
474
  - '**/*.{rake,arb,erb,rabl}'
472
475
  - '**/{Gemfile,Rakefile}'
@@ -479,7 +482,6 @@ Style/Alias:
479
482
  EnforcedStyle: prefer_alias_method
480
483
 
481
484
  Style/AsciiComments:
482
- # TODO: roll this out fully
483
485
  Enabled: true
484
486
 
485
487
  Style/AutoResourceCleanup:
@@ -542,9 +544,9 @@ Style/FrozenStringLiteralComment:
542
544
  EnforcedStyle: always
543
545
  Enabled: true
544
546
 
547
+ # This can make lines longer and impair readability
545
548
  Style/GuardClause:
546
549
  Enabled: false
547
- MinBodyLength: 4
548
550
 
549
551
  Style/HashEachMethods:
550
552
  Enabled: true
@@ -559,7 +561,7 @@ Style/HashTransformValues:
559
561
  Enabled: false
560
562
 
561
563
  Style/IfInsideElse:
562
- Enabled: false
564
+ Enabled: false
563
565
 
564
566
  Style/IfUnlessModifier:
565
567
  Enabled: false
@@ -567,9 +569,6 @@ Style/IfUnlessModifier:
567
569
  Style/ImplicitRuntimeError:
568
570
  Enabled: false
569
571
 
570
- Style/InverseMethods:
571
- Enabled: false
572
-
573
572
  Style/Lambda:
574
573
  EnforcedStyle: literal
575
574
 
@@ -753,7 +752,6 @@ Style/SymbolProc:
753
752
 
754
753
  Style/TernaryParentheses:
755
754
  Enabled: false
756
- EnforcedStyle: require_parentheses_when_complex
757
755
 
758
756
  Style/TrailingCommaInArguments:
759
757
  Enabled: true
data/config/rails.yml CHANGED
@@ -7,6 +7,7 @@ plugins:
7
7
  - rubocop-rails
8
8
 
9
9
  AllCops:
10
+ ActiveSupportExtensionsEnabled: true
10
11
  TargetRubyVersion: <%= RbConfig::CONFIG['RUBY_API_VERSION'] %>
11
12
  MaxFilesInCache: 100000
12
13
  Exclude:
@@ -16,6 +17,9 @@ AllCops:
16
17
  - 'db/**/*schema.rb'
17
18
  - 'db/seeds{.rb,/**/*}'
18
19
 
20
+ Performance/DoubleStartEndWith:
21
+ IncludeActiveSupportAliases: true
22
+
19
23
  Rails:
20
24
  Enabled: true
21
25
 
@@ -0,0 +1,100 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Gusto
7
+ # Never mock time in specs. Use Rails Testing Time Helpers (eg `freeze_time` and `travel_to`) instead.
8
+ # .and_call_original is allowed.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # context 'time specific tests' do
13
+ # before { allow(Time).to receive(:now).and_return(current_time) }
14
+ # it 'does stuff in a fixed time' do
15
+ # subject
16
+ # end
17
+ # end
18
+ #
19
+ # # good
20
+ # context 'time specific tests' do
21
+ # it 'does stuff in a fixed time' do
22
+ # freeze_time do
23
+ # subject
24
+ # end
25
+ # end
26
+ # end
27
+ class RspecDateTimeMock < Base
28
+ CLASSES = Set[
29
+ :Date,
30
+ :Time,
31
+ :DateTime
32
+ ].freeze
33
+ MSG = "Don't mock #{CLASSES.join('/')} directly. Use Rails Testing Time Helpers (eg `freeze_time` and `travel_to`) instead.".freeze
34
+ RESTRICT_ON_SEND = %i(to).freeze
35
+
36
+ # Matches allow/expect with a time class (or chain) receiver and a `receive` or `receive_message_chain`
37
+ # Examples matched:
38
+ # allow(Time).to receive(:now)
39
+ # allow(Time.zone).to receive(:now)
40
+ # allow(Time).to receive_message_chain(:zone, :now)
41
+ # expect(Date.today).to receive(:wday)
42
+ # @!method time_mock?(node)
43
+ def_node_matcher :time_mock?, <<~PATTERN
44
+ (send
45
+ (send nil? {:allow :expect} #rooted_in_time_class?)
46
+ :to
47
+ {
48
+ (send (send nil? :receive _) ...)
49
+ (send (send nil? :receive_message_chain ...) ...)
50
+ }
51
+ )
52
+ PATTERN
53
+
54
+ def on_send(node)
55
+ time_mock?(node) do
56
+ # Allow usages that explicitly call `.and_call_original` after `receive`
57
+ # Example: allow(Time).to receive(:parse).and_call_original
58
+ receive_chain = node.first_argument
59
+ return if and_call_original_in_chain?(receive_chain)
60
+
61
+ add_offense(node)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # Returns true if the given node is a const or a call chain whose root receiver
68
+ # is one of the protected time classes (Date/Time/DateTime)
69
+ def rooted_in_time_class?(node)
70
+ return false if node.nil?
71
+
72
+ current = node
73
+ while current.respond_to?(:send_type?) && current.send_type?
74
+ current = current.receiver
75
+ end
76
+
77
+ if current.nil?
78
+ return false
79
+ end
80
+
81
+ return false unless current.const_type?
82
+
83
+ # Accept both `Time` and `::Time` as root-level constants
84
+ namespace = current.namespace
85
+ is_root_level = namespace.nil? || namespace.cbase_type?
86
+ is_root_level && CLASSES.include?(current.children[1])
87
+ end
88
+
89
+ def and_call_original_in_chain?(node)
90
+ return false if node.nil?
91
+ return false unless node.send_type?
92
+
93
+ return true if node.method?(:and_call_original)
94
+
95
+ node.each_descendant(:send).any? { |send_node| send_node.method?(:and_call_original) }
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -5,6 +5,12 @@ module RuboCop
5
5
  module Gusto
6
6
  # Requires the use of the `paint` gem for terminal color methods on strings
7
7
  #
8
+ # @safety
9
+ # This cop's autocorrection is unsafe because it replaces colorize gem
10
+ # methods with Paint gem methods. If the Paint gem is not included in the
11
+ # project's dependencies, the corrected code will fail at runtime with a
12
+ # NameError (uninitialized constant Paint).
13
+ #
8
14
  # @example
9
15
  #
10
16
  # # bad
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "pathname"
4
+ require "yaml"
4
5
 
5
6
  module RuboCop
6
7
  module Gusto
@@ -110,21 +111,46 @@ module RuboCop
110
111
  # Try to find a line that exactly matches KEY_REGEX
111
112
  # Use rstrip, not strip, to preserve indentation
112
113
  name_line = chunk.find { |line| line.rstrip.match?(KEY_REGEX) }
113
- name_line&.rstrip&.delete_suffix(":")
114
+ if name_line
115
+ name_line.rstrip.delete_suffix(":")
116
+ else
117
+ # Try to find a key with value on the same line (e.g., "inherit_from: .rubocop_todo.yml")
118
+ first_line = chunk.find { |line| !line.strip.empty? && !line.strip.start_with?("#") }
119
+ if first_line&.include?(":")
120
+ first_line.split(":").first.strip
121
+ end
122
+ end
114
123
  end
115
124
 
116
125
  # Splits the lines into blocks whenever we drop from indented to unindented
117
126
  def chunk_blocks(lines)
118
- # slice whenever we drop from indented to unindented
127
+ # slice whenever we drop from indented to unindented line
128
+ # or when we encounter a new top-level key after blank line(s)
119
129
  chunks = lines.slice_when do |prev, line|
120
- prev.match?(INDENT_REGEX) && !prev.strip.empty? && !line.match?(INDENT_REGEX)
130
+ if prev.strip.empty?
131
+ # split when going from blank to non-indented non-blank
132
+ !line.match?(INDENT_REGEX) && !line.strip.empty?
133
+ elsif prev.match?(INDENT_REGEX)
134
+ # split when going from indented to non-blank unindented
135
+ !line.match?(INDENT_REGEX) && !line.strip.empty?
136
+ else
137
+ # prev is non-indented, split on blank lines too
138
+ line.strip.empty?
139
+ end
121
140
  end
122
141
 
123
142
  # Process each chunk to remove leading newlines and add 1 trailing newline
124
143
  chunks.filter_map do |chunk|
125
144
  # Remove leading and trailing empty lines
126
- chunk.shift while chunk.first.to_s.strip.empty?
127
- chunk.pop while chunk.last.to_s.strip.empty?
145
+ until chunk.empty? || !chunk.first.strip.empty?
146
+ chunk.shift
147
+ end
148
+ until chunk.empty? || !chunk.last.strip.empty?
149
+ chunk.pop
150
+ end
151
+
152
+ # Skip empty chunks
153
+ next if chunk.empty?
128
154
 
129
155
  # Ensure each chunk ends with a blank newline
130
156
  chunk << "\n"
@@ -1,5 +1,3 @@
1
- inherit_from: .rubocop_todo.yml
2
-
3
1
  inherit_mode:
4
2
  merge:
5
3
  - Include
@@ -8,6 +6,8 @@ inherit_gem:
8
6
  rubocop-gusto:
9
7
  - config/default.yml
10
8
 
9
+ inherit_from: .rubocop_todo.yml
10
+
11
11
  plugins:
12
12
  - rubocop-gusto
13
13
  - rubocop-rspec
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Gusto
5
- VERSION = "10.1.1"
5
+ VERSION = "10.3.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-gusto
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.1.1
4
+ version: 10.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineering
@@ -10,7 +10,7 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: bigdecimal
13
+ name: lint_roller
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
@@ -24,35 +24,35 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
26
  - !ruby/object:Gem::Dependency
27
- name: lint_roller
27
+ name: rubocop
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '0'
32
+ version: '1.76'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: '1.76'
40
40
  - !ruby/object:Gem::Dependency
41
- name: rubocop
41
+ name: rubocop-performance
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '1.76'
46
+ version: '0'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: '1.76'
53
+ version: '0'
54
54
  - !ruby/object:Gem::Dependency
55
- name: rubocop-performance
55
+ name: rubocop-rake
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - ">="
@@ -66,7 +66,7 @@ dependencies:
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0'
68
68
  - !ruby/object:Gem::Dependency
69
- name: rubocop-rake
69
+ name: rubocop-rspec
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - ">="
@@ -80,7 +80,7 @@ dependencies:
80
80
  - !ruby/object:Gem::Version
81
81
  version: '0'
82
82
  - !ruby/object:Gem::Dependency
83
- name: rubocop-rspec
83
+ name: rubocop-sorbet
84
84
  requirement: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - ">="
@@ -140,6 +140,7 @@ files:
140
140
  - lib/rubocop/cop/gusto/rails_env.rb
141
141
  - lib/rubocop/cop/gusto/rake_constants.rb
142
142
  - lib/rubocop/cop/gusto/regexp_bypass.rb
143
+ - lib/rubocop/cop/gusto/rspec_date_time_mock.rb
143
144
  - lib/rubocop/cop/gusto/sidekiq_params.rb
144
145
  - lib/rubocop/cop/gusto/toplevel_constants.rb
145
146
  - lib/rubocop/cop/gusto/use_paint_not_colorize.rb