Dutchie-Style 2.0.11 → 3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98940de414a26efc09108619c8421782a09bf51c25032a357e854856aaaa6505
4
- data.tar.gz: f6b9595bbf810983571ab1d531c0fb5d441ddb126e495df7d993c6bbd30ff398
3
+ metadata.gz: f49ba6bf36f8caa607d18099ca4f8d3810b444c22abee8cbffa1fbd92812da7c
4
+ data.tar.gz: cf1684201eb4a1c27d47db5d43104f392b18ce334582a7a090eb6713287dc518
5
5
  SHA512:
6
- metadata.gz: 82715d41789f44683bf8a58f6ec92f18d17ef50c00e9d13e994f5a3dc9f5b4fdfc606d2adcc35870568059a302c7ba1053356446c68ca69678f675d360c14ee6
7
- data.tar.gz: f05a26ac206b58de4b1e35777776fcb67e6a7578608c63e42e8cadd62fb386df722792146eb7ee69c3ffc2e44d083395f8f3c05cf238a3a4a3fb8d18ac661c91
6
+ metadata.gz: df98f9aef6cd815fb4d1aad802f568ab0dae384e4eba02694b8cd17d067c9524620a2c8e8356bac442bdbc30734b9bb330ac0ade4313034c5ff6b68495b3f5ed
7
+ data.tar.gz: daf381202cc98aa42379171ac8cbe6c441d62e39e803a52dadf26d882301ecd6cc5d5f337c6c88170cb22f2c2dee6e0680814c8866db75fd292c409d11c1782c
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.summary = "Rubocop Settings for all dutchie Ruby Apps"
14
14
  spec.description = "Rubocop Settings for all dutchie Ruby Apps"
15
15
  spec.homepage = "https://github.com/GetDutchie/Dutchie-Style"
16
- spec.required_ruby_version = Gem::Requirement.new(">= 3.1.4")
16
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0.6")
17
17
 
18
18
  spec.metadata["homepage_uri"] = spec.homepage
19
19
  spec.metadata["source_code_uri"] = "https://github.com/GetDutchie/Dutchie-Style"
@@ -29,12 +29,12 @@ Gem::Specification.new do |spec|
29
29
  spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
30
30
  spec.require_paths = ["lib"]
31
31
 
32
- spec.add_dependency "rubocop", "~> 1.72"
33
- spec.add_dependency "rubocop-capybara", "~> 2.18"
34
- spec.add_dependency "rubocop-factory_bot", "~> 2.24"
35
- spec.add_dependency "rubocop-rails", "~> 2.21"
36
- spec.add_dependency "rubocop-rspec", "~> 2.24"
37
- spec.add_dependency "rubocop-rspec_rails", "~> 2.25"
32
+ spec.add_dependency "rubocop", "~> 1.81"
33
+ spec.add_dependency "rubocop-capybara", "~> 3.0"
34
+ spec.add_dependency "rubocop-factory_bot", "~> 2.28"
35
+ spec.add_dependency "rubocop-rails", "~> 2.32"
36
+ spec.add_dependency "rubocop-rspec", "~> 3.0"
37
+ spec.add_dependency "rubocop-rspec_rails", "~> 2.31"
38
38
 
39
39
  spec.metadata["rubygems_mfa_required"] = "true"
40
40
  end
data/Gemfile CHANGED
@@ -5,5 +5,5 @@ gemspec
5
5
 
6
6
  gem "rake", "~> 12.0"
7
7
  gem "rspec", "~> 3.0"
8
- gem "rubocop", "~> 1.72"
9
- gem "rubocop-rails", "~> 2.14"
8
+ gem "rubocop", "~> 1.81"
9
+ gem "rubocop-rails", "~> 2.32"
data/Gemfile.lock CHANGED
@@ -1,41 +1,56 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- Dutchie-Style (2.0.11)
5
- rubocop (~> 1.72)
6
- rubocop-capybara (~> 2.18)
7
- rubocop-factory_bot (~> 2.24)
8
- rubocop-rails (~> 2.21)
9
- rubocop-rspec (~> 2.24)
10
- rubocop-rspec_rails (~> 2.25)
4
+ Dutchie-Style (3.0.0)
5
+ rubocop (~> 1.81)
6
+ rubocop-capybara (~> 3.0)
7
+ rubocop-factory_bot (~> 2.28)
8
+ rubocop-rails (~> 2.32)
9
+ rubocop-rspec (~> 3.0)
10
+ rubocop-rspec_rails (~> 2.31)
11
11
 
12
12
  GEM
13
13
  remote: https://rubygems.org/
14
14
  specs:
15
- activesupport (7.0.7.2)
16
- concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ activesupport (8.1.3)
16
+ base64
17
+ bigdecimal
18
+ concurrent-ruby (~> 1.0, >= 1.3.1)
19
+ connection_pool (>= 2.2.5)
20
+ drb
17
21
  i18n (>= 1.6, < 2)
22
+ json
23
+ logger (>= 1.4.2)
18
24
  minitest (>= 5.1)
19
- tzinfo (~> 2.0)
25
+ securerandom (>= 0.3)
26
+ tzinfo (~> 2.0, >= 2.0.5)
27
+ uri (>= 0.13.1)
20
28
  ast (2.4.3)
21
- concurrent-ruby (1.2.2)
29
+ base64 (0.3.0)
30
+ bigdecimal (4.1.2)
31
+ concurrent-ruby (1.3.8)
32
+ connection_pool (3.0.2)
22
33
  diff-lcs (1.4.2)
23
- i18n (1.14.1)
34
+ drb (2.2.3)
35
+ i18n (1.15.2)
24
36
  concurrent-ruby (~> 1.0)
25
- json (2.13.2)
26
- language_server-protocol (3.17.0.5)
37
+ json (2.21.1)
38
+ language_server-protocol (3.17.0.6)
27
39
  lint_roller (1.1.0)
28
- minitest (5.20.0)
29
- parallel (1.27.0)
30
- parser (3.3.9.0)
40
+ logger (1.7.0)
41
+ minitest (6.0.6)
42
+ drb (~> 2.0)
43
+ prism (~> 1.5)
44
+ parallel (2.1.0)
45
+ parser (3.3.12.0)
31
46
  ast (~> 2.4.1)
32
47
  racc
33
- prism (1.4.0)
48
+ prism (1.9.0)
34
49
  racc (1.8.1)
35
- rack (3.0.8)
50
+ rack (3.2.6)
36
51
  rainbow (3.1.1)
37
52
  rake (12.3.3)
38
- regexp_parser (2.10.0)
53
+ regexp_parser (2.12.0)
39
54
  rspec (3.9.0)
40
55
  rspec-core (~> 3.9.0)
41
56
  rspec-expectations (~> 3.9.0)
@@ -49,44 +64,48 @@ GEM
49
64
  diff-lcs (>= 1.2.0, < 2.0)
50
65
  rspec-support (~> 3.9.0)
51
66
  rspec-support (3.9.3)
52
- rubocop (1.79.0)
67
+ rubocop (1.88.2)
53
68
  json (~> 2.3)
54
69
  language_server-protocol (~> 3.17.0.2)
55
70
  lint_roller (~> 1.1.0)
56
- parallel (~> 1.10)
71
+ parallel (>= 1.10)
57
72
  parser (>= 3.3.0.2)
58
73
  rainbow (>= 2.2.2, < 4.0)
59
74
  regexp_parser (>= 2.9.3, < 3.0)
60
- rubocop-ast (>= 1.46.0, < 2.0)
75
+ rubocop-ast (>= 1.49.0, < 2.0)
61
76
  ruby-progressbar (~> 1.7)
62
- tsort (>= 0.2.0)
63
77
  unicode-display_width (>= 2.4.0, < 4.0)
64
- rubocop-ast (1.46.0)
78
+ rubocop-ast (1.50.0)
65
79
  parser (>= 3.3.7.2)
66
- prism (~> 1.4)
67
- rubocop-capybara (2.18.0)
68
- rubocop (~> 1.41)
69
- rubocop-factory_bot (2.27.1)
80
+ prism (~> 1.7)
81
+ rubocop-capybara (3.0.0)
82
+ lint_roller (~> 1.1)
83
+ rubocop (~> 1.81)
84
+ rubocop-factory_bot (2.28.0)
70
85
  lint_roller (~> 1.1)
71
86
  rubocop (~> 1.72, >= 1.72.1)
72
- rubocop-rails (2.22.1)
87
+ rubocop-rails (2.36.0)
73
88
  activesupport (>= 4.2.0)
89
+ lint_roller (~> 1.1)
74
90
  rack (>= 1.1)
75
- rubocop (>= 1.33.0, < 2.0)
76
- rubocop-rspec (2.31.0)
77
- rubocop (~> 1.40)
78
- rubocop-capybara (~> 2.17)
79
- rubocop-factory_bot (~> 2.22)
80
- rubocop-rspec_rails (~> 2.28)
81
- rubocop-rspec_rails (2.29.0)
82
- rubocop (~> 1.40)
91
+ rubocop (>= 1.75.0, < 2.0)
92
+ rubocop-ast (>= 1.44.0, < 2.0)
93
+ rubocop-rspec (3.10.2)
94
+ lint_roller (~> 1.1)
95
+ regexp_parser (>= 2.0)
96
+ rubocop (~> 1.86, >= 1.86.2)
97
+ rubocop-rspec_rails (2.32.0)
98
+ lint_roller (~> 1.1)
99
+ rubocop (~> 1.72, >= 1.72.1)
100
+ rubocop-rspec (~> 3.5)
83
101
  ruby-progressbar (1.13.0)
84
- tsort (0.2.0)
102
+ securerandom (0.4.1)
85
103
  tzinfo (2.0.6)
86
104
  concurrent-ruby (~> 1.0)
87
- unicode-display_width (3.1.4)
88
- unicode-emoji (~> 4.0, >= 4.0.4)
89
- unicode-emoji (4.0.4)
105
+ unicode-display_width (3.2.0)
106
+ unicode-emoji (~> 4.1)
107
+ unicode-emoji (4.2.0)
108
+ uri (1.1.1)
90
109
 
91
110
  PLATFORMS
92
111
  ruby
@@ -95,8 +114,8 @@ DEPENDENCIES
95
114
  Dutchie-Style!
96
115
  rake (~> 12.0)
97
116
  rspec (~> 3.0)
98
- rubocop (~> 1.72)
99
- rubocop-rails (~> 2.14)
117
+ rubocop (~> 1.81)
118
+ rubocop-rails (~> 2.32)
100
119
 
101
120
  BUNDLED WITH
102
121
  2.4.8
data/config/default.yml CHANGED
@@ -1,8 +1,9 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-rspec
3
3
  - rubocop-rspec_rails
4
4
  - rubocop-rails
5
5
  - rubocop-capybara
6
+ - rubocop-factory_bot
6
7
 
7
8
  AllCops:
8
9
  TargetRubyVersion: 3.1.4
@@ -280,6 +281,10 @@ Rails/PluckId:
280
281
  Rails/PluckInWhere:
281
282
  Enabled: false
282
283
 
284
+ # Disable I18n locale texts cop
285
+ Rails/I18nLocaleTexts:
286
+ Enabled: false
287
+
283
288
  # RSpec
284
289
 
285
290
  # Start context description without 'when', 'with', or 'without'.
@@ -301,3 +306,22 @@ RSpec/NestedGroups:
301
306
  # Allows us to mark specs as "skip"
302
307
  RSpec/Pending:
303
308
  Enabled: false
309
+
310
+ # Dutchie Custom Cops
311
+
312
+ # Ensures all LaunchDarkly feature flag calls have default values
313
+ # Supports both Armageddon (DutchieFeatureFlags) and MenuConnector (ld_client.variation) patterns
314
+ Dutchie/LaunchDarklyDefaults:
315
+ Enabled: true
316
+ Severity: warning
317
+ AutoCorrect: true
318
+
319
+ # Flags questionable usage of safety_assured in migrations
320
+ # The Include directive overrides the global Exclude: db/**/* for this cop only
321
+ Dutchie/MigrationSafetyAssured:
322
+ Enabled: true
323
+ Severity: warning
324
+ Include:
325
+ - 'db/migrate/**/*.rb'
326
+ - 'lib/migration_helpers.rb'
327
+ - 'lib/**/migration_helpers.rb'
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Dutchie
4
4
  module Style
5
- VERSION = "2.0.11"
5
+ VERSION = "3.0.0"
6
6
  public_constant :VERSION
7
7
  end
8
8
  end
data/lib/dutchie-style.rb CHANGED
@@ -1,3 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "Dutchie/Style"
4
+ require_relative "rubocop/cop/dutchie/launchdarkly_defaults"
5
+ require_relative "rubocop/cop/dutchie/migration_safety_assured"
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Dutchie
8
+ # Ensures all LaunchDarkly feature flag calls have default values
9
+ # to prevent failures during LaunchDarkly service outages
10
+ #
11
+ # This cop supports two LaunchDarkly patterns:
12
+ # 1. Armageddon pattern: DutchieFeatureFlags.flag(), .context_flag(), etc.
13
+ # 2. MenuConnector pattern: ld_client.variation(), MenuConnector::App[:launchdarkly].variation()
14
+ #
15
+ # @example Armageddon pattern
16
+ # # bad
17
+ # DutchieFeatureFlags.flag("some.flag")
18
+ # DutchieFeatureFlags.context_flag("some.flag", dispensary_id: id)
19
+ # DutchieFeatureFlags.on?("some.flag")
20
+ #
21
+ # # good
22
+ # DutchieFeatureFlags.flag("some.flag", false)
23
+ # DutchieFeatureFlags.context_flag("some.flag", default: false, dispensary_id: id)
24
+ # DutchieFeatureFlags.on?("some.flag", default: false)
25
+ #
26
+ # @example MenuConnector pattern
27
+ # # bad
28
+ # ld_client.variation("flag", context)
29
+ # MenuConnector::App[:launchdarkly].variation("flag", context)
30
+ #
31
+ # # good
32
+ # ld_client.variation("flag", context, false)
33
+ # MenuConnector::App[:launchdarkly].variation("flag", context, false)
34
+ #
35
+ class LaunchDarklyDefaults < ::RuboCop::Cop::Base
36
+ extend AutoCorrector
37
+
38
+ MSG_FLAG = 'DutchieFeatureFlags.flag must have a default value as 2nd parameter'
39
+ MSG_CONTEXT_FLAG = 'DutchieFeatureFlags.context_flag must have a default: parameter'
40
+ MSG_DISPENSARY_FLAG = 'DutchieFeatureFlags.dispensary_flag must have a default: parameter'
41
+ MSG_ENTERPRISE_FLAG = 'DutchieFeatureFlags.enterprise_flag must have a default: parameter'
42
+ MSG_ON = 'DutchieFeatureFlags.on? should have a default: parameter'
43
+ MSG_OFF = 'DutchieFeatureFlags.off? should have a default: parameter'
44
+ MSG_VARIATION = 'LaunchDarkly variation must have a default value as 3rd parameter'
45
+
46
+ # DutchieFeatureFlags.flag("key", default, ...)
47
+ def_node_matcher :dutchie_flag_call?, <<~PATTERN
48
+ (send
49
+ (const nil? :DutchieFeatureFlags) :flag
50
+ $_
51
+ $...
52
+ )
53
+ PATTERN
54
+
55
+ # DutchieFeatureFlags.context_flag("key", ...)
56
+ def_node_matcher :context_flag_call?, <<~PATTERN
57
+ (send
58
+ (const nil? :DutchieFeatureFlags) :context_flag
59
+ $_
60
+ $...
61
+ )
62
+ PATTERN
63
+
64
+ # DutchieFeatureFlags.dispensary_flag("key", ...)
65
+ def_node_matcher :dispensary_flag_call?, <<~PATTERN
66
+ (send
67
+ (const nil? :DutchieFeatureFlags) :dispensary_flag
68
+ $_
69
+ $...
70
+ )
71
+ PATTERN
72
+
73
+ # DutchieFeatureFlags.enterprise_flag("key", ...)
74
+ def_node_matcher :enterprise_flag_call?, <<~PATTERN
75
+ (send
76
+ (const nil? :DutchieFeatureFlags) :enterprise_flag
77
+ $_
78
+ $...
79
+ )
80
+ PATTERN
81
+
82
+ # DutchieFeatureFlags.on?("key", ...)
83
+ def_node_matcher :on_call?, <<~PATTERN
84
+ (send
85
+ (const nil? :DutchieFeatureFlags) :on?
86
+ $_
87
+ $...
88
+ )
89
+ PATTERN
90
+
91
+ # DutchieFeatureFlags.off?("key", ...)
92
+ def_node_matcher :off_call?, <<~PATTERN
93
+ (send
94
+ (const nil? :DutchieFeatureFlags) :off?
95
+ $_
96
+ $...
97
+ )
98
+ PATTERN
99
+
100
+ # Direct variation calls on ld_client or MenuConnector::App[:launchdarkly]
101
+ def_node_matcher :variation_call?, <<~PATTERN
102
+ (send
103
+ {
104
+ (send nil? :ld_client)
105
+ (send
106
+ (const (const nil? :MenuConnector) :App)
107
+ :[]
108
+ (sym :launchdarkly)
109
+ )
110
+ (send _ :ld_client)
111
+ }
112
+ :variation
113
+ $_
114
+ $...
115
+ )
116
+ PATTERN
117
+
118
+ # MockLaunchDarkly variation calls (for test environments)
119
+ def_node_matcher :mock_variation_call?, <<~PATTERN
120
+ (send
121
+ (const (const nil? :MenuConnector) :MockLaunchDarkly)
122
+ :variation
123
+ $_
124
+ $...
125
+ )
126
+ PATTERN
127
+
128
+ def on_send(node)
129
+ # Check Armageddon patterns
130
+ check_flag_method(node)
131
+ check_context_flag_method(node)
132
+ check_dispensary_flag_method(node)
133
+ check_enterprise_flag_method(node)
134
+ check_on_method(node)
135
+ check_off_method(node)
136
+
137
+ # Check MenuConnector patterns
138
+ check_variation_method(node)
139
+ check_mock_variation_method(node)
140
+ end
141
+
142
+ private
143
+
144
+ # Armageddon pattern checks
145
+
146
+ def check_flag_method(node)
147
+ flag_key, args = dutchie_flag_call?(node)
148
+ return unless flag_key
149
+
150
+ # Need at least one more argument after the key (the default value)
151
+ return if args.any?
152
+
153
+ add_offense(node, message: MSG_FLAG) do |corrector|
154
+ corrector.insert_after(flag_key, ', false')
155
+ end
156
+ end
157
+
158
+ def check_context_flag_method(node)
159
+ flag_key, args = context_flag_call?(node)
160
+ return unless flag_key
161
+
162
+ return if has_default_kwarg?(args)
163
+
164
+ add_offense(node, message: MSG_CONTEXT_FLAG) do |corrector|
165
+ insert_default_kwarg(corrector, flag_key, args)
166
+ end
167
+ end
168
+
169
+ def check_dispensary_flag_method(node)
170
+ flag_key, args = dispensary_flag_call?(node)
171
+ return unless flag_key
172
+
173
+ return if has_default_kwarg?(args)
174
+
175
+ add_offense(node, message: MSG_DISPENSARY_FLAG) do |corrector|
176
+ insert_default_kwarg(corrector, flag_key, args)
177
+ end
178
+ end
179
+
180
+ def check_enterprise_flag_method(node)
181
+ flag_key, args = enterprise_flag_call?(node)
182
+ return unless flag_key
183
+
184
+ return if has_default_kwarg?(args)
185
+
186
+ add_offense(node, message: MSG_ENTERPRISE_FLAG) do |corrector|
187
+ insert_default_kwarg(corrector, flag_key, args)
188
+ end
189
+ end
190
+
191
+ def check_on_method(node)
192
+ flag_key, args = on_call?(node)
193
+ return unless flag_key
194
+
195
+ # on? accepts default: as keyword or as second positional argument
196
+ # Return if there's a positional default (non-hash arg) or default: kwarg
197
+ return if has_positional_default?(args) || has_default_kwarg?(args)
198
+
199
+ add_offense(node, message: MSG_ON) do |corrector|
200
+ insert_default_kwarg(corrector, flag_key, args)
201
+ end
202
+ end
203
+
204
+ def check_off_method(node)
205
+ flag_key, args = off_call?(node)
206
+ return unless flag_key
207
+
208
+ # off? accepts default: as keyword or as second positional argument
209
+ # Return if there's a positional default (non-hash arg) or default: kwarg
210
+ return if has_positional_default?(args) || has_default_kwarg?(args)
211
+
212
+ add_offense(node, message: MSG_OFF) do |corrector|
213
+ insert_default_kwarg(corrector, flag_key, args)
214
+ end
215
+ end
216
+
217
+ # MenuConnector pattern checks
218
+
219
+ def check_variation_method(node)
220
+ flag_key, args = variation_call?(node)
221
+ return unless flag_key
222
+
223
+ # variation(flag, context, default) - need at least 2 args after flag
224
+ return if args.size >= 2
225
+
226
+ add_offense(node, message: MSG_VARIATION) do |corrector|
227
+ if args.empty?
228
+ # No context provided, add both context and default
229
+ corrector.insert_after(flag_key, ', nil, false')
230
+ elsif args.size == 1
231
+ # Context provided but no default
232
+ corrector.insert_after(args.last, ', false')
233
+ end
234
+ end
235
+ end
236
+
237
+ def check_mock_variation_method(node)
238
+ # Don't check mock variation calls in test code
239
+ return if in_test_file?(node)
240
+
241
+ flag_key, args = mock_variation_call?(node)
242
+ return unless flag_key
243
+
244
+ # Same logic as regular variation
245
+ return if args.size >= 2
246
+
247
+ add_offense(node, message: MSG_VARIATION) do |corrector|
248
+ if args.empty?
249
+ corrector.insert_after(flag_key, ', nil, false')
250
+ elsif args.size == 1
251
+ corrector.insert_after(args.last, ', false')
252
+ end
253
+ end
254
+ end
255
+
256
+ # Helper methods
257
+
258
+ def has_default_kwarg?(args)
259
+ args.any? do |arg|
260
+ next false unless arg.hash_type?
261
+
262
+ arg.pairs.any? do |pair|
263
+ pair.key.sym_type? && pair.key.value == :default
264
+ end
265
+ end
266
+ end
267
+
268
+ def has_positional_default?(args)
269
+ args.any? { |arg| !arg.hash_type? }
270
+ end
271
+
272
+ def insert_default_kwarg(corrector, flag_key, args)
273
+ if args.any? && args.last.hash_type?
274
+ # Add to existing hash
275
+ corrector.insert_after(args.last.source_range.end.adjust(begin_pos: -1), ', default: false')
276
+ elsif args.any?
277
+ # Add as new keyword argument after existing args
278
+ corrector.insert_after(args.last, ', default: false')
279
+ else
280
+ # Add as first argument after flag key
281
+ corrector.insert_after(flag_key, ', default: false')
282
+ end
283
+ end
284
+
285
+ def in_test_file?(node)
286
+ processed_source.file_path.include?('spec/')
287
+ end
288
+ end
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Dutchie
8
+ # Flags usage of safety_assured in migrations
9
+ #
10
+ # The safety_assured block bypasses strong_migrations safety checks,
11
+ # which should only be used when you're certain the operation is safe
12
+ # and have documented why it's necessary.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # def change
17
+ # safety_assured do
18
+ # remove_column :users, :email
19
+ # end
20
+ # end
21
+ #
22
+ # # good - avoid safety_assured when possible
23
+ # def change
24
+ # # Use strong_migrations approved patterns instead
25
+ # remove_column :users, :email, type: :string
26
+ # end
27
+ #
28
+ # # acceptable - with clear documentation
29
+ # def change
30
+ # # This operation is safe because:
31
+ # # 1. The column was already removed from the model
32
+ # # 2. No code references this column anymore
33
+ # # 3. We've verified in production logs that no queries use this column
34
+ # safety_assured do
35
+ # remove_column :users, :deprecated_field
36
+ # end
37
+ # end
38
+ #
39
+ class MigrationSafetyAssured < ::RuboCop::Cop::Base
40
+ MSG = 'Avoid `safety_assured` in migrations. It bypasses strong_migrations safety checks. ' \
41
+ 'Ensure this operation is truly safe, consider safer alternatives, and document why ' \
42
+ 'safety_assured is necessary.'
43
+
44
+ # Matches: safety_assured do...end or safety_assured {...}
45
+ def_node_matcher :safety_assured_block?, <<~PATTERN
46
+ (block
47
+ (send nil? :safety_assured)
48
+ ...
49
+ )
50
+ PATTERN
51
+
52
+ def on_block(node)
53
+ return unless safety_assured_block?(node)
54
+
55
+ add_offense(node)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
data/old-default.yml CHANGED
@@ -1,4 +1,4 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-rspec
3
3
  - rubocop-rails
4
4
 
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: Dutchie-Style
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.11
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Ostrowski
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 2025-11-07 00:00:00.000000000 Z
11
+ date: 2026-08-21 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: rubocop
@@ -15,84 +16,84 @@ dependencies:
15
16
  requirements:
16
17
  - - "~>"
17
18
  - !ruby/object:Gem::Version
18
- version: '1.72'
19
+ version: '1.81'
19
20
  type: :runtime
20
21
  prerelease: false
21
22
  version_requirements: !ruby/object:Gem::Requirement
22
23
  requirements:
23
24
  - - "~>"
24
25
  - !ruby/object:Gem::Version
25
- version: '1.72'
26
+ version: '1.81'
26
27
  - !ruby/object:Gem::Dependency
27
28
  name: rubocop-capybara
28
29
  requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
31
  - - "~>"
31
32
  - !ruby/object:Gem::Version
32
- version: '2.18'
33
+ version: '3.0'
33
34
  type: :runtime
34
35
  prerelease: false
35
36
  version_requirements: !ruby/object:Gem::Requirement
36
37
  requirements:
37
38
  - - "~>"
38
39
  - !ruby/object:Gem::Version
39
- version: '2.18'
40
+ version: '3.0'
40
41
  - !ruby/object:Gem::Dependency
41
42
  name: rubocop-factory_bot
42
43
  requirement: !ruby/object:Gem::Requirement
43
44
  requirements:
44
45
  - - "~>"
45
46
  - !ruby/object:Gem::Version
46
- version: '2.24'
47
+ version: '2.28'
47
48
  type: :runtime
48
49
  prerelease: false
49
50
  version_requirements: !ruby/object:Gem::Requirement
50
51
  requirements:
51
52
  - - "~>"
52
53
  - !ruby/object:Gem::Version
53
- version: '2.24'
54
+ version: '2.28'
54
55
  - !ruby/object:Gem::Dependency
55
56
  name: rubocop-rails
56
57
  requirement: !ruby/object:Gem::Requirement
57
58
  requirements:
58
59
  - - "~>"
59
60
  - !ruby/object:Gem::Version
60
- version: '2.21'
61
+ version: '2.32'
61
62
  type: :runtime
62
63
  prerelease: false
63
64
  version_requirements: !ruby/object:Gem::Requirement
64
65
  requirements:
65
66
  - - "~>"
66
67
  - !ruby/object:Gem::Version
67
- version: '2.21'
68
+ version: '2.32'
68
69
  - !ruby/object:Gem::Dependency
69
70
  name: rubocop-rspec
70
71
  requirement: !ruby/object:Gem::Requirement
71
72
  requirements:
72
73
  - - "~>"
73
74
  - !ruby/object:Gem::Version
74
- version: '2.24'
75
+ version: '3.0'
75
76
  type: :runtime
76
77
  prerelease: false
77
78
  version_requirements: !ruby/object:Gem::Requirement
78
79
  requirements:
79
80
  - - "~>"
80
81
  - !ruby/object:Gem::Version
81
- version: '2.24'
82
+ version: '3.0'
82
83
  - !ruby/object:Gem::Dependency
83
84
  name: rubocop-rspec_rails
84
85
  requirement: !ruby/object:Gem::Requirement
85
86
  requirements:
86
87
  - - "~>"
87
88
  - !ruby/object:Gem::Version
88
- version: '2.25'
89
+ version: '2.31'
89
90
  type: :runtime
90
91
  prerelease: false
91
92
  version_requirements: !ruby/object:Gem::Requirement
92
93
  requirements:
93
94
  - - "~>"
94
95
  - !ruby/object:Gem::Version
95
- version: '2.25'
96
+ version: '2.31'
96
97
  description: Rubocop Settings for all dutchie Ruby Apps
97
98
  email:
98
99
  - chris@dutchie.com
@@ -110,6 +111,8 @@ files:
110
111
  - lib/Dutchie/Style.rb
111
112
  - lib/Dutchie/Style/version.rb
112
113
  - lib/dutchie-style.rb
114
+ - lib/rubocop/cop/dutchie/launchdarkly_defaults.rb
115
+ - lib/rubocop/cop/dutchie/migration_safety_assured.rb
113
116
  - old-default.yml
114
117
  homepage: https://github.com/GetDutchie/Dutchie-Style
115
118
  licenses:
@@ -119,6 +122,7 @@ metadata:
119
122
  source_code_uri: https://github.com/GetDutchie/Dutchie-Style
120
123
  changelog_uri: https://github.com/GetDutchie/Dutchie-Style/releases
121
124
  rubygems_mfa_required: 'true'
125
+ post_install_message:
122
126
  rdoc_options: []
123
127
  require_paths:
124
128
  - lib
@@ -126,14 +130,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
130
  requirements:
127
131
  - - ">="
128
132
  - !ruby/object:Gem::Version
129
- version: 3.1.4
133
+ version: 3.0.6
130
134
  required_rubygems_version: !ruby/object:Gem::Requirement
131
135
  requirements:
132
136
  - - ">="
133
137
  - !ruby/object:Gem::Version
134
138
  version: '0'
135
139
  requirements: []
136
- rubygems_version: 3.6.2
140
+ rubygems_version: 3.4.19
141
+ signing_key:
137
142
  specification_version: 4
138
143
  summary: Rubocop Settings for all dutchie Ruby Apps
139
144
  test_files: []