type_toolkit 0.0.5 → 0.0.7

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +0 -4
  3. data/README.md +61 -0
  4. data/Rakefile +0 -7
  5. data/benchmark/.rubocop.yml +7 -0
  6. data/benchmark/abstract_methods_benchmark.rb +221 -0
  7. data/benchmark/interface_startup_performance.rb +128 -0
  8. data/benchmark/module_benchmark.rb +62 -0
  9. data/config/default.yml +5 -0
  10. data/docs/design/inherited_implementation_problem.md +258 -0
  11. data/docs/design/self_dot_methods.md +79 -0
  12. data/lib/rubocop/cop/type_toolkit/plugin.rb +1 -1
  13. data/lib/rubocop/cop/type_toolkit/prefer_not_nil.rb +150 -0
  14. data/lib/rubocop-type_toolkit.rb +1 -0
  15. data/lib/type_toolkit/abstract_method_receiver.rb +39 -0
  16. data/lib/type_toolkit/dsl.rb +66 -0
  17. data/lib/type_toolkit/ext/method.rb +11 -0
  18. data/lib/type_toolkit/ext/module.rb +7 -0
  19. data/lib/type_toolkit/ext/nil_assertions.rb +3 -1
  20. data/lib/type_toolkit/has_abstract_methods.rb +108 -0
  21. data/lib/type_toolkit/interface.rb +38 -0
  22. data/lib/type_toolkit/method_def_recorder.rb +53 -0
  23. data/lib/type_toolkit/method_patch.rb +16 -0
  24. data/lib/type_toolkit/version.rb +1 -1
  25. data/lib/type_toolkit.rb +3 -0
  26. data/sorbet/config +3 -1
  27. data/sorbet/rbi/gems/benchmark-ips@2.14.0.rbi +981 -0
  28. data/sorbet/rbi/gems/{erb@6.0.1.rbi → erb@6.0.1.1.rbi} +2 -2
  29. data/sorbet/rbi/gems/{json@2.18.1.rbi → json@2.19.9.rbi} +115 -161
  30. data/sorbet/rbi/gems/minitest@5.27.0.rbi +707 -0
  31. data/sorbet/rbi/gems/rexml@3.4.4.rbi +0 -167
  32. data/sorbet/rbi/gems/rubocop-ast@1.49.0.rbi +6 -0
  33. data/sorbet/rbi/gems/rubocop@1.84.2.rbi +7 -0
  34. data/sorbet/rbi/gems/tapioca@0.17.10.rbi +1 -0
  35. data/sorbet/rbi/gems/{yard@0.9.38.rbi → yard@0.9.44.rbi} +1206 -241
  36. data/sorbet/rbi/shims/core.rbi +19 -0
  37. data/sorbet/rbi/shims/minitest.rbi +5 -2
  38. data/sorbet/rbi/shims/rubocop_minitest.rbi +5 -2
  39. data/spec/interface_spec.rb +419 -0
  40. data/spec/misc/how_method_added_hooks_work_spec.rb +63 -0
  41. data/spec/rubocop/cop/type_toolkit/prefer_not_nil_spec.rb +464 -0
  42. data/spec/spec_helper.rb +14 -0
  43. metadata +24 -4
@@ -0,0 +1,464 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "spec_helper"
5
+ require "rubocop"
6
+ require "rubocop/minitest/assert_offense"
7
+ require "rubocop-type_toolkit"
8
+
9
+ module RuboCop
10
+ module Cop
11
+ module TypeToolkit
12
+ class PreferNotNilSpec < ::Minitest::Spec
13
+ include RuboCop::Minitest::AssertOffense
14
+
15
+ MSG = "TypeToolkit/PreferNotNil: #{format(PreferNotNil::MSG, method: :must)}"
16
+ BECAUSE_MSG = "TypeToolkit/PreferNotNil: #{format(PreferNotNil::MSG, method: :must_because)}"
17
+
18
+ before do
19
+ @cop = PreferNotNil.new
20
+ end
21
+
22
+ it "autocorrects T.must" do
23
+ assert_offense(<<~RUBY)
24
+ value = T.must(foo)
25
+ ^^^^^^^^^^^ #{MSG}
26
+ RUBY
27
+
28
+ assert_correction(<<~RUBY)
29
+ value = foo.not_nil!
30
+ RUBY
31
+ end
32
+
33
+ it "autocorrects ::T.must" do
34
+ assert_offense(<<~RUBY)
35
+ value = ::T.must(foo)
36
+ ^^^^^^^^^^^^^ #{MSG}
37
+ RUBY
38
+
39
+ assert_correction(<<~RUBY)
40
+ value = foo.not_nil!
41
+ RUBY
42
+ end
43
+
44
+ it "autocorrects inside string interpolation" do
45
+ assert_offense(<<~RUBY)
46
+ string = "\#{T.must(foo)}"
47
+ ^^^^^^^^^^^ #{MSG}
48
+ RUBY
49
+
50
+ assert_correction(<<~RUBY)
51
+ string = "\#{foo.not_nil!}"
52
+ RUBY
53
+ end
54
+
55
+ it "preserves the precedence of conditional and range expressions" do
56
+ assert_offense(<<~RUBY)
57
+ conditional = T.must(condition ? foo : bar)
58
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{MSG}
59
+ range = T.must(foo..bar)
60
+ ^^^^^^^^^^^^^^^^ #{MSG}
61
+ RUBY
62
+
63
+ assert_correction(<<~RUBY)
64
+ conditional = (condition ? foo : bar).not_nil!
65
+ range = (foo..bar).not_nil!
66
+ RUBY
67
+ end
68
+
69
+ it "parenthesizes every expression that requires it" do
70
+ assert_offense(<<~RUBY)
71
+ operator = T.must(foo + bar)
72
+ ^^^^^^^^^^^^^^^^^ #{MSG}
73
+ logical = T.must(foo || bar)
74
+ ^^^^^^^^^^^^^^^^^^ #{MSG}
75
+ grouped = T.must((foo || bar))
76
+ ^^^^^^^^^^^^^^^^^^^^ #{MSG}
77
+ assignment = T.must(foo = bar)
78
+ ^^^^^^^^^^^^^^^^^ #{MSG}
79
+ block_value = T.must(foo { bar })
80
+ ^^^^^^^^^^^^^^^^^^^ #{MSG}
81
+ block_with_arguments = T.must(foo(arg) { |value| value })
82
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{MSG}
83
+ defined_value = T.must(defined?(foo))
84
+ ^^^^^^^^^^^^^^^^^^^^^ #{MSG}
85
+ def example
86
+ T.must(yield foo)
87
+ ^^^^^^^^^^^^^^^^^ #{MSG}
88
+ end
89
+ def implicit_super
90
+ T.must(super)
91
+ ^^^^^^^^^^^^^ #{MSG}
92
+ end
93
+ def explicit_super
94
+ T.must(super(foo))
95
+ ^^^^^^^^^^^^^^^^^^ #{MSG}
96
+ end
97
+ RUBY
98
+
99
+ assert_correction(<<~RUBY)
100
+ operator = (foo + bar).not_nil!
101
+ logical = (foo || bar).not_nil!
102
+ grouped = (foo || bar).not_nil!
103
+ assignment = (foo = bar).not_nil!
104
+ block_value = foo { bar }.not_nil!
105
+ block_with_arguments = foo(arg) { |value| value }.not_nil!
106
+ defined_value = (defined?(foo)).not_nil!
107
+ def example
108
+ (yield foo).not_nil!
109
+ end
110
+ def implicit_super
111
+ (super).not_nil!
112
+ end
113
+ def explicit_super
114
+ (super(foo)).not_nil!
115
+ end
116
+ RUBY
117
+ end
118
+
119
+ it "preserves the precedence of command calls" do
120
+ assert_offense(<<~RUBY)
121
+ value = T.must(fetch value)
122
+ ^^^^^^^^^^^^^^^^^^^ #{MSG}
123
+ RUBY
124
+
125
+ assert_correction(<<~RUBY)
126
+ value = (fetch value).not_nil!
127
+ RUBY
128
+ end
129
+
130
+ it "autocorrects multiline command calls" do
131
+ assert_offense(<<~RUBY)
132
+ T.must foo
133
+ ^^^^^^^^^^ #{MSG}
134
+ .bar
135
+ RUBY
136
+
137
+ assert_correction(<<~RUBY)
138
+ foo
139
+ .bar.not_nil!
140
+ RUBY
141
+ end
142
+
143
+ it "does not add unnecessary parentheses to parenthesized calls" do
144
+ assert_offense(<<~RUBY)
145
+ value = T.must(fetch(value))
146
+ ^^^^^^^^^^^^^^^^^^^^ #{MSG}
147
+ RUBY
148
+
149
+ assert_correction(<<~RUBY)
150
+ value = fetch(value).not_nil!
151
+ RUBY
152
+ end
153
+
154
+ it "parenthesizes bracket method calls only when required" do
155
+ assert_offense(<<~RUBY)
156
+ bracketed = T.must(foo[bar]).foo
157
+ ^^^^^^^^^^^^^^^^ #{MSG}
158
+ explicit = T.must(foo.[](bar)).foo
159
+ ^^^^^^^^^^^^^^^^^^^ #{MSG}
160
+ command = T.must(foo.[] bar).foo
161
+ ^^^^^^^^^^^^^^^^^^ #{MSG}
162
+ RUBY
163
+
164
+ assert_correction(<<~RUBY)
165
+ bracketed = foo[bar].not_nil!.foo
166
+ explicit = foo.[](bar).not_nil!.foo
167
+ command = (foo.[] bar).not_nil!.foo
168
+ RUBY
169
+ end
170
+
171
+ it "does not group an argument that makes the call multiline" do
172
+ assert_offense(<<~RUBY)
173
+ T.must(foo
174
+ ^^^^^^^^^^ #{MSG}
175
+ .bar)
176
+ RUBY
177
+
178
+ assert_correction(<<~RUBY)
179
+ foo
180
+ .bar.not_nil!
181
+ RUBY
182
+ end
183
+
184
+ it "preserves a multiline receiver without parentheses" do
185
+ assert_offense(<<~RUBY)
186
+ T.must(
187
+ ^^^^^^^ #{MSG}
188
+ foo.
189
+ bar
190
+ )
191
+ RUBY
192
+
193
+ assert_correction(<<~RUBY)
194
+ foo.
195
+ bar.not_nil!
196
+ RUBY
197
+ end
198
+
199
+ it "preserves comments in multiline calls" do
200
+ assert_offense(<<~RUBY)
201
+ value = T.must(
202
+ ^^^^^^^ #{MSG}
203
+ # Proven non-nil by validation.
204
+ foo,
205
+ )
206
+ RUBY
207
+
208
+ assert_correction(<<~RUBY)
209
+ value = (
210
+ # Proven non-nil by validation.
211
+ foo
212
+ ).not_nil!
213
+ RUBY
214
+ end
215
+
216
+ it "drops unnecessary grouping from multiline calls without comments" do
217
+ assert_offense(<<~RUBY)
218
+ first = T .must(
219
+ ^^^^^^^^ #{MSG}
220
+ foo,
221
+ )
222
+ second = T
223
+ ^ #{MSG}
224
+ .must(
225
+ bar,
226
+ )
227
+ RUBY
228
+
229
+ assert_correction(<<~RUBY)
230
+ first = foo.not_nil!
231
+ second = bar.not_nil!
232
+ RUBY
233
+ end
234
+
235
+ it "preserves a multiline call chain without parentheses" do
236
+ assert_offense(<<~RUBY)
237
+ variant = T.must(
238
+ ^^^^^^^ #{MSG}
239
+ InventoryItemVariant.preload(:variant).where(inventory_item_id: @inventory_item_id).first!.variant
240
+ )
241
+ RUBY
242
+
243
+ assert_correction(<<~RUBY)
244
+ variant = InventoryItemVariant.preload(:variant).where(inventory_item_id: @inventory_item_id).first!.variant.not_nil!
245
+ RUBY
246
+ end
247
+
248
+ it "removes a trailing comma before an inline comment" do
249
+ assert_offense(<<~RUBY)
250
+ value = T.must(
251
+ ^^^^^^^ #{MSG}
252
+ foo, # Proven non-nil.
253
+ )
254
+ RUBY
255
+
256
+ assert_correction(<<~RUBY)
257
+ value = (
258
+ foo # Proven non-nil.
259
+ ).not_nil!
260
+ RUBY
261
+ end
262
+
263
+ it "preserves grouping around heredocs in multiline calls" do
264
+ assert_offense(<<~RUBY)
265
+ value = T.must(
266
+ ^^^^^^^ #{MSG}
267
+ <<~TEXT
268
+ hello
269
+ TEXT
270
+ )
271
+ RUBY
272
+
273
+ assert_correction(<<~RUBY)
274
+ value = (
275
+ <<~TEXT
276
+ hello
277
+ TEXT
278
+ ).not_nil!
279
+ RUBY
280
+ end
281
+
282
+ it "preserves grouping around nested heredocs in multiline calls" do
283
+ assert_offense(<<~RUBY)
284
+ value = T.must(
285
+ ^^^^^^^ #{MSG}
286
+ foo(<<~TEXT)
287
+ hello
288
+ TEXT
289
+ )
290
+ RUBY
291
+
292
+ assert_correction(<<~RUBY)
293
+ value = (
294
+ foo(<<~TEXT)
295
+ hello
296
+ TEXT
297
+ ).not_nil!
298
+ RUBY
299
+ end
300
+
301
+ it "preserves grouping around a heredoc T.must nested in another call" do
302
+ assert_offense(<<~RUBY)
303
+ value = foo(
304
+ T.must(
305
+ ^^^^^^^ #{MSG}
306
+ <<~TEXT
307
+ hello
308
+ TEXT
309
+ )
310
+ )
311
+ RUBY
312
+
313
+ assert_correction(<<~RUBY)
314
+ value = foo(
315
+ (
316
+ <<~TEXT
317
+ hello
318
+ TEXT
319
+ ).not_nil!
320
+ )
321
+ RUBY
322
+ end
323
+
324
+ it "autocorrects nested T.must calls" do
325
+ assert_offense(<<~RUBY)
326
+ value = T.must(T.must(foo).bar)
327
+ ^^^^^^^^^^^^^^^^^^^^^^^ #{MSG}
328
+ ^^^^^^^^^^^ #{MSG}
329
+ RUBY
330
+
331
+ assert_correction(<<~RUBY)
332
+ value = foo.not_nil!.bar.not_nil!
333
+ RUBY
334
+ end
335
+
336
+ it "autocorrects three nested T.must calls" do
337
+ assert_offense(<<~RUBY)
338
+ value = T.must(T.must(T.must(foo).bar).baz)
339
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{MSG}
340
+ ^^^^^^^^^^^^^^^^^^^^^^^ #{MSG}
341
+ ^^^^^^^^^^^ #{MSG}
342
+ RUBY
343
+
344
+ assert_correction(<<~RUBY)
345
+ value = foo.not_nil!.bar.not_nil!.baz.not_nil!
346
+ RUBY
347
+ end
348
+
349
+ it "reports heredoc reasons without leaving orphaned heredoc bodies" do
350
+ assert_offense(<<~RUBY)
351
+ value = T.must_because(foo) { <<~REASON }
352
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{BECAUSE_MSG}
353
+ Proven non-nil.
354
+ REASON
355
+ RUBY
356
+
357
+ assert_no_corrections
358
+ end
359
+
360
+ it "preserves the reason without attaching the block to the corrected call" do
361
+ assert_offense(<<~RUBY)
362
+ value = T.must_because(foo) { "reason" }.bar
363
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{BECAUSE_MSG}
364
+ RUBY
365
+
366
+ assert_correction(<<~RUBY)
367
+ value = (
368
+ # reason
369
+ foo.not_nil!
370
+ ).bar
371
+ RUBY
372
+ end
373
+
374
+ it "preserves multiline reasons and argument comments" do
375
+ assert_offense(<<~RUBY)
376
+ value = ::T.must_because(
377
+ ^^^^^^^^^^^^^^^^^ #{BECAUSE_MSG}
378
+ # Validated earlier.
379
+ foo,
380
+ ) do
381
+ # Explain the invariant.
382
+ "validated for \#{user}"
383
+ end
384
+ RUBY
385
+
386
+ assert_correction(<<~RUBY)
387
+ value = (
388
+ # # Explain the invariant.
389
+ # validated for \#{user}
390
+ # Validated earlier.
391
+ foo
392
+ ).not_nil!
393
+ RUBY
394
+ end
395
+
396
+ it "keeps trailing arguments and string interpolation outside the reason comment" do
397
+ assert_offense(<<~'RUBY', message: BECAUSE_MSG)
398
+ values = [T.must_because(foo) { 'reason' }, bar]
399
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ %{message}
400
+ text = "#{T.must_because(foo) { "reason" }}"
401
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ %{message}
402
+ RUBY
403
+
404
+ assert_correction(<<~'RUBY')
405
+ values = [(
406
+ # reason
407
+ foo.not_nil!
408
+ ), bar]
409
+ text = "#{(
410
+ # reason
411
+ foo.not_nil!
412
+ )}"
413
+ RUBY
414
+ end
415
+
416
+ it "corrects mixed nested assertions without overlapping edits" do
417
+ assert_offense(<<~RUBY)
418
+ value = T.must(T.must_because(T.must(foo)) { "reason" })
419
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{MSG}
420
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{BECAUSE_MSG}
421
+ ^^^^^^^^^^^ #{MSG}
422
+ RUBY
423
+
424
+ assert_correction(<<~RUBY)
425
+ value = (
426
+ # reason
427
+ foo.not_nil!.not_nil!
428
+ ).not_nil!
429
+ RUBY
430
+ end
431
+
432
+ it "comments out assertions in the reason rather than correcting them" do
433
+ assert_offense(<<~RUBY)
434
+ value = T.must_because(foo) { T.must(bar) }
435
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{BECAUSE_MSG}
436
+ ^^^^^^^^^^^ #{MSG}
437
+ RUBY
438
+
439
+ assert_correction(<<~RUBY)
440
+ value = (
441
+ # T.must(bar)
442
+ foo.not_nil!
443
+ )
444
+ RUBY
445
+ end
446
+
447
+ it "ignores other receivers, methods, and argument counts" do
448
+ assert_no_offenses(<<~RUBY)
449
+ Other::T.must(foo)
450
+ object.must(foo)
451
+ T.let(foo, String)
452
+ T.must(foo, bar)
453
+ T.must(*values)
454
+ Other::T.must_because(foo) { "reason" }
455
+ object.must_because(foo) { "reason" }
456
+ T.must_because(foo, bar) { "reason" }
457
+ T.must_because(*values) { "reason" }
458
+ T.must_because(foo, &reason)
459
+ RUBY
460
+ end
461
+ end
462
+ end
463
+ end
464
+ end
data/spec/spec_helper.rb CHANGED
@@ -6,3 +6,17 @@ require "type_toolkit"
6
6
 
7
7
  require "minitest/autorun"
8
8
  require "minitest/spec"
9
+
10
+ module Minitest
11
+ class Spec
12
+ #: () { () -> void } -> TypeToolkit::AbstractMethodNotImplementedError
13
+ def assert_abstract(&block)
14
+ assert_raises(TypeToolkit::AbstractMethodNotImplementedError, &block)
15
+ end
16
+ end
17
+ end
18
+
19
+ #: -> bot
20
+ def assert_never_called!
21
+ raise Minitest::Assertion, "This should never be called"
22
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: type_toolkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Momchilov
@@ -52,12 +52,27 @@ files:
52
52
  - ".ruby-version"
53
53
  - README.md
54
54
  - Rakefile
55
+ - benchmark/.rubocop.yml
56
+ - benchmark/abstract_methods_benchmark.rb
57
+ - benchmark/interface_startup_performance.rb
58
+ - benchmark/module_benchmark.rb
55
59
  - config/default.yml
60
+ - docs/design/inherited_implementation_problem.md
61
+ - docs/design/self_dot_methods.md
56
62
  - lib/rubocop-type_toolkit.rb
57
63
  - lib/rubocop/cop/type_toolkit/dont_expect_unexpected_nil.rb
58
64
  - lib/rubocop/cop/type_toolkit/plugin.rb
65
+ - lib/rubocop/cop/type_toolkit/prefer_not_nil.rb
59
66
  - lib/type_toolkit.rb
67
+ - lib/type_toolkit/abstract_method_receiver.rb
68
+ - lib/type_toolkit/dsl.rb
69
+ - lib/type_toolkit/ext/method.rb
70
+ - lib/type_toolkit/ext/module.rb
60
71
  - lib/type_toolkit/ext/nil_assertions.rb
72
+ - lib/type_toolkit/has_abstract_methods.rb
73
+ - lib/type_toolkit/interface.rb
74
+ - lib/type_toolkit/method_def_recorder.rb
75
+ - lib/type_toolkit/method_patch.rb
61
76
  - lib/type_toolkit/version.rb
62
77
  - sig/type_toolkit.rbs
63
78
  - sorbet/config
@@ -66,12 +81,13 @@ files:
66
81
  - sorbet/rbi/annotations/rainbow.rbi
67
82
  - sorbet/rbi/gems/.gitattributes
68
83
  - sorbet/rbi/gems/ast@2.4.3.rbi
84
+ - sorbet/rbi/gems/benchmark-ips@2.14.0.rbi
69
85
  - sorbet/rbi/gems/benchmark@0.5.0.rbi
70
86
  - sorbet/rbi/gems/date@3.5.1.rbi
71
- - sorbet/rbi/gems/erb@6.0.1.rbi
87
+ - sorbet/rbi/gems/erb@6.0.1.1.rbi
72
88
  - sorbet/rbi/gems/erubi@1.13.1.rbi
73
89
  - sorbet/rbi/gems/io-console@0.8.2.rbi
74
- - sorbet/rbi/gems/json@2.18.1.rbi
90
+ - sorbet/rbi/gems/json@2.19.9.rbi
75
91
  - sorbet/rbi/gems/language_server-protocol@3.17.0.5.rbi
76
92
  - sorbet/rbi/gems/lint_roller@1.1.0.rbi
77
93
  - sorbet/rbi/gems/logger@1.7.0.rbi
@@ -107,15 +123,19 @@ files:
107
123
  - sorbet/rbi/gems/unicode-display_width@3.2.0.rbi
108
124
  - sorbet/rbi/gems/unicode-emoji@4.2.0.rbi
109
125
  - sorbet/rbi/gems/yard-sorbet@0.9.0.rbi
110
- - sorbet/rbi/gems/yard@0.9.38.rbi
126
+ - sorbet/rbi/gems/yard@0.9.44.rbi
127
+ - sorbet/rbi/shims/core.rbi
111
128
  - sorbet/rbi/shims/minitest.rbi
112
129
  - sorbet/rbi/shims/rubocop_minitest.rbi
113
130
  - sorbet/rbi/todo.rbi
114
131
  - sorbet/tapioca/config.yml
115
132
  - sorbet/tapioca/require.rb
116
133
  - spec/.rubocop.yml
134
+ - spec/interface_spec.rb
135
+ - spec/misc/how_method_added_hooks_work_spec.rb
117
136
  - spec/nil_assertions_spec.rb
118
137
  - spec/rubocop/cop/type_toolkit/dont_expect_unexpected_nil_spec.rb
138
+ - spec/rubocop/cop/type_toolkit/prefer_not_nil_spec.rb
119
139
  - spec/spec_helper.rb
120
140
  - spec/type_toolkit_spec.rb
121
141
  homepage: https://github.com/Shopify/type_kit