type_toolkit 0.0.2 → 0.0.3
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 +4 -4
- data/.github/workflows/ci.yml +33 -0
- data/README.md +106 -15
- data/Rakefile +26 -2
- data/config/default.yml +4 -0
- data/lib/rubocop/cop/type_toolkit/dont_expect_unexpected_nil.rb +115 -0
- data/lib/rubocop-type_toolkit.rb +7 -0
- data/lib/type_toolkit/ext/nil_assertions.rb +36 -0
- data/lib/type_toolkit/version.rb +2 -1
- data/lib/type_toolkit.rb +2 -2
- data/sorbet/config +7 -0
- data/sorbet/rbi/annotations/.gitattributes +1 -0
- data/sorbet/rbi/annotations/minitest.rbi +120 -0
- data/sorbet/rbi/annotations/rainbow.rbi +269 -0
- data/sorbet/rbi/gems/.gitattributes +1 -0
- data/sorbet/rbi/gems/ast@2.4.3.rbi +586 -0
- data/sorbet/rbi/gems/benchmark@0.5.0.rbi +637 -0
- data/sorbet/rbi/gems/date@3.5.1.rbi +403 -0
- data/sorbet/rbi/gems/erb@6.0.1.rbi +816 -0
- data/sorbet/rbi/gems/erubi@1.13.1.rbi +157 -0
- data/sorbet/rbi/gems/io-console@0.8.2.rbi +9 -0
- data/sorbet/rbi/gems/json@2.18.1.rbi +2340 -0
- data/sorbet/rbi/gems/language_server-protocol@3.17.0.5.rbi +9 -0
- data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +119 -0
- data/sorbet/rbi/gems/logger@1.7.0.rbi +963 -0
- data/sorbet/rbi/gems/minitest@5.27.0.rbi +1549 -0
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +177 -0
- data/sorbet/rbi/gems/parallel@1.27.0.rbi +291 -0
- data/sorbet/rbi/gems/parser@3.3.10.2.rbi +5537 -0
- data/sorbet/rbi/gems/pp@0.6.3.rbi +376 -0
- data/sorbet/rbi/gems/prettyprint@0.2.0.rbi +477 -0
- data/sorbet/rbi/gems/prism@1.9.0.rbi +43414 -0
- data/sorbet/rbi/gems/psych@5.3.1.rbi +2542 -0
- data/sorbet/rbi/gems/racc@1.8.1.rbi +168 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +403 -0
- data/sorbet/rbi/gems/rake@13.3.1.rbi +3036 -0
- data/sorbet/rbi/gems/rbi@0.3.9.rbi +5238 -0
- data/sorbet/rbi/gems/rbs@4.0.0.dev.5.rbi +8393 -0
- data/sorbet/rbi/gems/rdoc@7.2.0.rbi +13378 -0
- data/sorbet/rbi/gems/regexp_parser@2.11.3.rbi +3883 -0
- data/sorbet/rbi/gems/reline@0.6.3.rbi +2995 -0
- data/sorbet/rbi/gems/require-hooks@0.2.2.rbi +110 -0
- data/sorbet/rbi/gems/rexml@3.4.4.rbi +5258 -0
- data/sorbet/rbi/gems/rubocop-ast@1.49.0.rbi +7456 -0
- data/sorbet/rbi/gems/rubocop-minitest@0.38.2.rbi +2649 -0
- data/sorbet/rbi/gems/rubocop-rake@0.7.1.rbi +328 -0
- data/sorbet/rbi/gems/rubocop-shopify@2.18.0.rbi +9 -0
- data/sorbet/rbi/gems/rubocop@1.84.2.rbi +64803 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1318 -0
- data/sorbet/rbi/gems/spoom@1.7.11.rbi +5878 -0
- data/sorbet/rbi/gems/stringio@3.2.0.rbi +9 -0
- data/sorbet/rbi/gems/tapioca@0.17.10.rbi +3513 -0
- data/sorbet/rbi/gems/thor@1.5.0.rbi +4476 -0
- data/sorbet/rbi/gems/tsort@0.2.0.rbi +393 -0
- data/sorbet/rbi/gems/unicode-display_width@3.2.0.rbi +132 -0
- data/sorbet/rbi/gems/unicode-emoji@4.2.0.rbi +254 -0
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +430 -0
- data/sorbet/rbi/gems/yard@0.9.38.rbi +18425 -0
- data/sorbet/rbi/shims/lint_roller.rbi +6 -0
- data/sorbet/rbi/shims/minitest.rbi +43 -0
- data/sorbet/rbi/shims/rubocop_minitest.rbi +17 -0
- data/sorbet/rbi/todo.rbi +5 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +5 -0
- data/spec/.rubocop.yml +13 -0
- data/spec/nil_assertions_spec.rb +19 -0
- data/spec/rubocop/cop/type_toolkit/dont_expect_unexpected_nil_spec.rb +194 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/type_toolkit_spec.rb +11 -0
- metadata +65 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d495c6e64eda206b11d7dc9eaaa07bc3eaedfd94e08404ed199e3e8fc55be488
|
|
4
|
+
data.tar.gz: c3265eb72e3675220942f392f4a67d9df862dfae292b24b3602cd572997448cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 615492be957fad036838266b6521e5a98f540ca37128b7d116a1524c4e0fa9c412f70933e51edb3465415e1ff390105a040e4072f8b62770f9140cd50789f001
|
|
7
|
+
data.tar.gz: 17b8774a7b86b8a89081448c56ef834ef463941835b5a48ca9b300e288cf009d48e79acbb3438ca70981f3165ff67d0a49f36539ab6ac7ab706207b0fb29b957
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
ci:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
name: CI checks
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
15
|
+
- name: Set up Ruby
|
|
16
|
+
uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0
|
|
17
|
+
with:
|
|
18
|
+
bundler-cache: true
|
|
19
|
+
rubygems: 4.0.4
|
|
20
|
+
- name: Run type check
|
|
21
|
+
run: bundle exec rake typecheck
|
|
22
|
+
- name: Run type check with Prism parser
|
|
23
|
+
run: bundle exec rake typecheck_prism
|
|
24
|
+
continue-on-error: true
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: bundle exec rake test
|
|
27
|
+
continue-on-error: true
|
|
28
|
+
- name: Lint Ruby files
|
|
29
|
+
run: bundle exec rake rubocop
|
|
30
|
+
- name: Verify gem RBIs are up-to-date
|
|
31
|
+
run: bin/tapioca gem --verify
|
|
32
|
+
- name: Verify duplicates in shims
|
|
33
|
+
run: bin/tapioca check-shims
|
data/README.md
CHANGED
|
@@ -1,35 +1,126 @@
|
|
|
1
1
|
# TypeToolkit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[💎 RubyGems](https://rubygems.org/gems/type_toolkit)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A minimal runtime library for implementing abstract classes, interfaces, and more.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Install Type Toolkit into your bundle, add it to your `Gemfile`:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
```rb
|
|
12
|
+
gem "type_toolkit"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And then run `bundle install`.
|
|
16
|
+
|
|
17
|
+
### RuboCop Cops
|
|
18
|
+
|
|
19
|
+
This gem ships with RuboCop cops that we recommend you enable for your application. You can do so by adding it to the `plugins` list of your `rubocop.yml`:
|
|
20
|
+
|
|
21
|
+
```yml
|
|
22
|
+
plugins:
|
|
23
|
+
- rubocop-other-extension
|
|
24
|
+
- rubocop-type_toolkit
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Cherry-picking features
|
|
28
|
+
|
|
29
|
+
Simply writing `gem "type_toolkit"` in your `Gemfile` will grab all the tools from the toolkit, which we highly recommend. This adds methods to Ruby's core classes, to make them feel like a native part of the language.
|
|
12
30
|
|
|
13
|
-
|
|
14
|
-
|
|
31
|
+
Alternatively, you can cherry-pick only the tools you need. For example, if you only want to use the `not_nil!` assertion, you can add the following to your `Gemfile`:
|
|
32
|
+
|
|
33
|
+
```rb
|
|
34
|
+
gem "type_toolkit", require: ["type_toolkit/ext/nil_assertions"]
|
|
15
35
|
```
|
|
16
36
|
|
|
17
|
-
|
|
37
|
+
or you can skip the require in the `Gemfile`, and later manually require it in a specific file:
|
|
18
38
|
|
|
19
|
-
```
|
|
20
|
-
gem
|
|
39
|
+
```ruby
|
|
40
|
+
gem "type_toolkit", require: false
|
|
21
41
|
```
|
|
22
42
|
|
|
23
|
-
|
|
43
|
+
```ruby
|
|
44
|
+
# your_script.rb
|
|
45
|
+
require "type_toolkit/ext/nil_assertions"
|
|
46
|
+
```
|
|
24
47
|
|
|
25
|
-
|
|
48
|
+
|
|
49
|
+
## Tools
|
|
50
|
+
|
|
51
|
+
### `not_nil!` assertion
|
|
52
|
+
|
|
53
|
+
When debugging a `nil`-related error, it can be difficult to trace back where the `nil` actually originated from. It could have come in from a parameter, whose argument was read from an instance variable, on an object loaded from a cache, populated by some totally different request.
|
|
54
|
+
|
|
55
|
+
If a value can't be nil, it's best for that to be clearly asserted as close to where that nilable value was first generated. That way, a rogue `nil` isn't allowed to propagate arbitrarily far away in downstream code.
|
|
56
|
+
|
|
57
|
+
Type Toolkit provides a `not_nil!` assertion, which will raise an `UnexpectedNilError` if the receiver is `nil`.
|
|
58
|
+
|
|
59
|
+
```rb
|
|
60
|
+
# `__dir__` can be nil in an "eval", but never in a Ruby file.
|
|
61
|
+
gemfile = Pathname.new(__dir__.not_nil!) / "Gemfile"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`not_nil!` method calls can be chained, to fail early if any value in the chain is `nil`:
|
|
65
|
+
|
|
66
|
+
```rb
|
|
67
|
+
last_delivery = user.not_nil!
|
|
68
|
+
.orders.last.not_nil!
|
|
69
|
+
.deliveries.last.not_nil!
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Guiding Principles
|
|
73
|
+
|
|
74
|
+
### Blazingly fast™
|
|
75
|
+
|
|
76
|
+
All tools should aim to have 0 overhead at runtime, as compared to hand-written Ruby.
|
|
77
|
+
|
|
78
|
+
### Pay only for what you use
|
|
79
|
+
|
|
80
|
+
There should be no performance cost for a tool that you're not using.
|
|
81
|
+
|
|
82
|
+
Wherever there's an unavoidable cost, it should only ever apply to code that actually uses the tool. **No global costs.**
|
|
83
|
+
|
|
84
|
+
Tools should be optimized for the common case, even if that slows them in rare cases. For example, calling an abstract method is as fast as calling any other method, but only if it's implemented. Calling an unimplemented method call hits the `#method_missing` path, which is significantly slower. This is an acceptable trade-off, because no production code should be calling `#method_missing`, anyway.
|
|
85
|
+
|
|
86
|
+
In all cases, performance costs are quantified and well-documented.
|
|
87
|
+
|
|
88
|
+
### Feels like Ruby
|
|
89
|
+
|
|
90
|
+
Tools should feel like part of a programming language itself, and less like its standard library. Type Toolkit takes on the implementation complexity so your code doesn't have to.
|
|
91
|
+
|
|
92
|
+
To keep that bar high, the toolkit is lean and deliberately focused on
|
|
93
|
+
language-level primitives. Where practical, new
|
|
94
|
+
features should be built in separate libraries that _use_ the Type Toolkit.
|
|
26
95
|
|
|
27
96
|
## Development
|
|
28
97
|
|
|
29
|
-
|
|
98
|
+
This repo has Rake tasks configured for common development tasks:
|
|
99
|
+
1. `bundle exec rake` to do the next 3 steps all together
|
|
100
|
+
1. `bundle exec rake rubocop` for linting
|
|
101
|
+
1. `bundle exec rake typecheck` to typecheck with Sorbet
|
|
102
|
+
1. `bundle exec rake test` to run all the tests
|
|
103
|
+
1. `bundle exec rake -T` to list all the tasks
|
|
104
|
+
|
|
105
|
+
### Releasing
|
|
106
|
+
|
|
107
|
+
This gem is automatically released to [RubyGems.org](https://rubygems.org/gems/type_toolkit) via [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/). To publish a new version of the gem, all you have to do is:
|
|
108
|
+
|
|
109
|
+
1. Update the version number in [`version.rb`](https://github.com/Shopify/type_toolkit/blob/main/lib/type_toolkit/version.rb)
|
|
110
|
+
* This can either be part of an existing PR, or a new standalone PR.
|
|
111
|
+
|
|
112
|
+
2. Once that PR is merged, create a tag at the new head of the `main` branch:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
git pull origin main && git checkout main && git tag v1.2.3
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
3. Push the new tag.
|
|
30
119
|
|
|
31
|
-
|
|
120
|
+
```sh
|
|
121
|
+
git push origin v1.2.3
|
|
122
|
+
```
|
|
32
123
|
|
|
33
|
-
|
|
124
|
+
This will automatically trigger our [release workflow](https://github.com/Shopify/type_toolkit/actions/workflows/release.yml). It must be approved by a member of the Ruby and Rails Infrastructure team at Shopify before it will run.
|
|
34
125
|
|
|
35
|
-
|
|
126
|
+
Once approved, the workflow will automatically publish the new gem version to RubyGems.org, and create a new [GitHub release](https://github.com/Shopify/type_toolkit/releases).
|
data/Rakefile
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
|
+
|
|
5
|
+
desc "Type-check the code base with Sorbet"
|
|
6
|
+
task :typecheck do
|
|
7
|
+
sh "bundle exec srb tc" do |ok, _res|
|
|
8
|
+
abort unless ok
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Aliases for common names for this task.
|
|
13
|
+
desc "alias for typecheck"; task tc: :typecheck
|
|
14
|
+
desc "alias for typecheck"; task srb: :typecheck
|
|
15
|
+
desc "alias for typecheck"; task sorbet: :typecheck
|
|
16
|
+
|
|
17
|
+
desc "Type-check the code base with Sorbet using Prism"
|
|
18
|
+
task :typecheck_prism do
|
|
19
|
+
sh "bundle exec srb tc --parser=prism" do |ok, _res|
|
|
20
|
+
abort unless ok
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
4
24
|
require "minitest/test_task"
|
|
5
25
|
|
|
6
|
-
Minitest::TestTask.create
|
|
26
|
+
Minitest::TestTask.create do |t|
|
|
27
|
+
t.libs.delete("test")
|
|
28
|
+
t.libs << "spec"
|
|
29
|
+
t.test_globs = ["spec/**/*_spec.rb"]
|
|
30
|
+
end
|
|
7
31
|
|
|
8
32
|
require "rubocop/rake_task"
|
|
9
33
|
|
|
10
34
|
RuboCop::RakeTask.new
|
|
11
35
|
|
|
12
|
-
task default:
|
|
36
|
+
task default: [:typecheck, :test, :rubocop]
|
data/config/default.yml
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "type_toolkit/ext/nil_assertions"
|
|
5
|
+
|
|
6
|
+
module RuboCop
|
|
7
|
+
module Cop
|
|
8
|
+
module TypeToolkit
|
|
9
|
+
# This cop detects attempts to raise, rescue, or otherwise use the `UnexpectedNilError` class.
|
|
10
|
+
class DontExpectUnexpectedNil < Base
|
|
11
|
+
RESTRICT_ON_SEND = [:assert_raises, :raise].freeze
|
|
12
|
+
|
|
13
|
+
UNEXPECTED_NIL_ERROR_NAME = ::TypeToolkit::UnexpectedNilError.name.not_nil!
|
|
14
|
+
.split("::").last.not_nil!
|
|
15
|
+
.to_sym #: Symbol
|
|
16
|
+
private_constant :UNEXPECTED_NIL_ERROR_NAME
|
|
17
|
+
|
|
18
|
+
#: (RuboCop::AST::SendNode) -> void
|
|
19
|
+
def on_send(node)
|
|
20
|
+
case node.method_name
|
|
21
|
+
when :raise then check_raise(node)
|
|
22
|
+
when :assert_raises then check_assert_raises(node)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#: (RuboCop::AST::ResbodyNode) -> void
|
|
27
|
+
def on_resbody(node)
|
|
28
|
+
if (rescued_cls = node.exceptions.find { |ex_class| ex_class.const_type? && unexpected_nil_error?(ex_class) })
|
|
29
|
+
message = "It is always a mistake for `not_nil!` to be called on nil, " \
|
|
30
|
+
"so you should never try to rescue `UnexpectedNilError` specifically. " \
|
|
31
|
+
"Change your code to gracefully handle `nil` instead."
|
|
32
|
+
add_offense(rescued_cls, message:)
|
|
33
|
+
ignore_const_node(rescued_cls)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# This is a catch-all for cases where the `UnexpectedNilError` class is used outside of a raise, rescue, etc.
|
|
38
|
+
#: (RuboCop::AST::ConstNode) -> void
|
|
39
|
+
def on_const(node)
|
|
40
|
+
# Don't report this node right away, in case its parent AST is reported by one of the other cases.
|
|
41
|
+
# Instead, record it for now, and maybe report it at the end of the investigation.
|
|
42
|
+
if unexpected_nil_error?(node)
|
|
43
|
+
@const_read_nodes ||= Set.new.compare_by_identity
|
|
44
|
+
@const_read_nodes << node
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @override
|
|
49
|
+
#: -> void
|
|
50
|
+
def on_investigation_end
|
|
51
|
+
@const_read_nodes&.each do |node|
|
|
52
|
+
next if @ignored_const_nodes&.include?(node)
|
|
53
|
+
|
|
54
|
+
message = "`UnexpectedNilError` should only ever be used by `#not_nil!`."
|
|
55
|
+
add_offense(node, message:)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
#: (RuboCop::AST::ConstNode) -> bool
|
|
64
|
+
def unexpected_nil_error?(node)
|
|
65
|
+
return false unless node.short_name == UNEXPECTED_NIL_ERROR_NAME
|
|
66
|
+
|
|
67
|
+
ns = node.namespace
|
|
68
|
+
return true if ns.nil? || ns.cbase_type?
|
|
69
|
+
|
|
70
|
+
ns.const_type? && ns.short_name == :TypeToolkit && (ns.namespace.nil? || ns.namespace.cbase_type?)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Check for `raise UnexpectedNilError`
|
|
74
|
+
#: (RuboCop::AST::SendNode) -> void
|
|
75
|
+
def check_raise(node)
|
|
76
|
+
constant = case (first_arg = node.arguments.first)
|
|
77
|
+
when RuboCop::AST::ConstNode
|
|
78
|
+
node.arguments.first
|
|
79
|
+
when RuboCop::AST::SendNode
|
|
80
|
+
return unless first_arg.method_name == :new && first_arg.receiver.const_type?
|
|
81
|
+
|
|
82
|
+
first_arg.receiver
|
|
83
|
+
else return
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if unexpected_nil_error?(constant)
|
|
87
|
+
message = "`UnexpectedNilError` should only ever be raised by `NilClass#not_nil!`."
|
|
88
|
+
add_offense(node, message:)
|
|
89
|
+
ignore_const_node(constant)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Check for `assert_raises UnexpectedNilError`
|
|
94
|
+
#: (RuboCop::AST::SendNode) -> void
|
|
95
|
+
def check_assert_raises(node)
|
|
96
|
+
if (constants = node.arguments.filter { |arg| arg.const_type? && unexpected_nil_error?(arg) }).any?
|
|
97
|
+
message = "It is always a mistake for `not_nil!` to be called on nil, " \
|
|
98
|
+
"so tests should not expect any code to raise `UnexpectedNilError`. " \
|
|
99
|
+
"Change your code to gracefully handle `nil` instead."
|
|
100
|
+
add_offense(node, message:)
|
|
101
|
+
ignore_const_node(*constants)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Call this when this constant node is part of a node tree that already has an offense.
|
|
106
|
+
# This way we don't report a second offense for the same constant.
|
|
107
|
+
#: (*RuboCop::AST::ConstNode) -> void
|
|
108
|
+
def ignore_const_node(*nodes)
|
|
109
|
+
@ignored_const_nodes ||= Set.new.compare_by_identity
|
|
110
|
+
@ignored_const_nodes.merge(nodes)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Asserts that the receiver is not nil.
|
|
5
|
+
#
|
|
6
|
+
# You should use `not_nil!` in places where you're absolutely sure a `nil` value can't occur.
|
|
7
|
+
# This should be done as closely to where the value is created as possible, so that the `nil`
|
|
8
|
+
# value doesn't have a chance to be passed around the system. This way, failures occur close to
|
|
9
|
+
# the source of the problem, and are easier to fix.
|
|
10
|
+
module Kernel
|
|
11
|
+
#: -> self
|
|
12
|
+
def not_nil!
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class NilClass
|
|
18
|
+
# @override
|
|
19
|
+
#: -> bot
|
|
20
|
+
def not_nil!
|
|
21
|
+
raise TypeToolkit::UnexpectedNilError
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module TypeToolkit
|
|
26
|
+
# An error raised when calling `#not_nil!` on a `nil` value.
|
|
27
|
+
#
|
|
28
|
+
# `UnexpectedNilError` should never occur in well-formed code, so it should never be rescued.
|
|
29
|
+
# This is why it inherits from `Exception` instead of `StandardError`,
|
|
30
|
+
# so that bare rescues clauses (like `rescue => e`) don't rescue it.
|
|
31
|
+
class UnexpectedNilError < Exception # rubocop:disable Lint/InheritException
|
|
32
|
+
def initialize(message = "Called `not_nil!` on nil.")
|
|
33
|
+
super
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/type_toolkit/version.rb
CHANGED
data/lib/type_toolkit.rb
CHANGED
data/sorbet/config
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
**/*.rbi linguist-vendored=true
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
# DO NOT EDIT MANUALLY
|
|
4
|
+
# This file was pulled from a central RBI files repository.
|
|
5
|
+
# Please run `bin/tapioca annotations` to update it.
|
|
6
|
+
|
|
7
|
+
module Minitest::Assertions
|
|
8
|
+
sig { params(test: T.anything, msg: T.anything).returns(TrueClass) }
|
|
9
|
+
def assert(test, msg = nil); end
|
|
10
|
+
|
|
11
|
+
sig { params(obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
12
|
+
def assert_empty(obj, msg = nil); end
|
|
13
|
+
|
|
14
|
+
sig { params(exp: T.anything, act: T.anything, msg: T.anything).returns(TrueClass) }
|
|
15
|
+
def assert_equal(exp, act, msg = nil); end
|
|
16
|
+
|
|
17
|
+
sig { params(exp: T.anything, act: T.anything, delta: Numeric, msg: T.anything).returns(TrueClass) }
|
|
18
|
+
def assert_in_delta(exp, act, delta = T.unsafe(nil), msg = nil); end
|
|
19
|
+
|
|
20
|
+
sig { params(a: T.anything, b: T.anything, epsilon: Numeric, msg: T.anything).returns(TrueClass) }
|
|
21
|
+
def assert_in_epsilon(a, b, epsilon = T.unsafe(nil), msg = nil); end
|
|
22
|
+
|
|
23
|
+
sig { params(collection: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
24
|
+
def assert_includes(collection, obj, msg = nil); end
|
|
25
|
+
|
|
26
|
+
sig { params(cls: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
27
|
+
def assert_instance_of(cls, obj, msg = nil); end
|
|
28
|
+
|
|
29
|
+
sig { params(cls: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
30
|
+
def assert_kind_of(cls, obj, msg = nil); end
|
|
31
|
+
|
|
32
|
+
sig { params(matcher: T.any(String, Regexp), obj: T.anything, msg: T.anything).returns(MatchData) }
|
|
33
|
+
def assert_match(matcher, obj, msg = nil); end
|
|
34
|
+
|
|
35
|
+
sig { params(obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
36
|
+
def assert_nil(obj, msg = nil); end
|
|
37
|
+
|
|
38
|
+
sig { params(o1: T.anything, op: T.any(Symbol, String), o2: T.anything, msg: T.anything).returns(TrueClass) }
|
|
39
|
+
def assert_operator(o1, op, o2 = T.unsafe(nil), msg = nil); end
|
|
40
|
+
|
|
41
|
+
sig { params(stdout: T.nilable(T.any(String, Regexp)), stderr: T.nilable(T.any(String, Regexp)), block: T.proc.void).returns(T::Boolean) }
|
|
42
|
+
def assert_output(stdout = nil, stderr = nil, &block); end
|
|
43
|
+
|
|
44
|
+
sig { params(path: T.any(String, Pathname), msg: T.anything).returns(TrueClass) }
|
|
45
|
+
def assert_path_exists(path, msg = nil); end
|
|
46
|
+
|
|
47
|
+
sig { params(block: T.proc.void).returns(TrueClass) }
|
|
48
|
+
def assert_pattern(&block); end
|
|
49
|
+
|
|
50
|
+
sig { params(o1: T.anything, op: T.any(String, Symbol), msg: T.anything).returns(TrueClass) }
|
|
51
|
+
def assert_predicate(o1, op, msg = nil); end
|
|
52
|
+
|
|
53
|
+
sig { params(exp: NilClass, block: T.proc.void).returns(StandardError) }
|
|
54
|
+
sig { type_parameters(:T).params(exp: T.any(T::Class[T.type_parameter(:T)], Regexp, String), block: T.proc.void).returns(T.type_parameter(:T)) }
|
|
55
|
+
def assert_raises(*exp, &block); end
|
|
56
|
+
|
|
57
|
+
sig { params(obj: T.anything, meth: T.any(String, Symbol), msg: T.anything, include_all: T::Boolean).returns(TrueClass) }
|
|
58
|
+
def assert_respond_to(obj, meth, msg = nil, include_all: false); end
|
|
59
|
+
|
|
60
|
+
sig { params(exp: T.anything, act: T.anything, msg: T.anything).returns(TrueClass) }
|
|
61
|
+
def assert_same(exp, act, msg = nil); end
|
|
62
|
+
|
|
63
|
+
# @version < 6.0.0
|
|
64
|
+
sig { params(send_ary: T::Array[T.anything], m: T.anything).returns(T::Boolean) }
|
|
65
|
+
def assert_send(send_ary, m = nil); end
|
|
66
|
+
|
|
67
|
+
sig { params(block: T.proc.void).returns(T::Boolean) }
|
|
68
|
+
def assert_silent(&block); end
|
|
69
|
+
|
|
70
|
+
sig { params(sym: Symbol, msg: T.anything, block: T.proc.void).returns(T.anything) }
|
|
71
|
+
def assert_throws(sym, msg = nil, &block); end
|
|
72
|
+
|
|
73
|
+
sig { params(test: T.anything, msg: T.anything).returns(TrueClass) }
|
|
74
|
+
def refute(test, msg = nil); end
|
|
75
|
+
|
|
76
|
+
sig { params(obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
77
|
+
def refute_empty(obj, msg = nil); end
|
|
78
|
+
|
|
79
|
+
sig { params(exp: T.anything, act: T.anything, msg: T.anything).returns(TrueClass) }
|
|
80
|
+
def refute_equal(exp, act, msg = nil); end
|
|
81
|
+
|
|
82
|
+
sig { params(exp: T.anything, act: T.anything, delta: Numeric, msg: T.anything).returns(TrueClass) }
|
|
83
|
+
def refute_in_delta(exp, act, delta = T.unsafe(nil), msg = nil); end
|
|
84
|
+
|
|
85
|
+
sig { params(a: T.anything, b: T.anything, epsilon: Numeric, msg: T.anything).returns(TrueClass) }
|
|
86
|
+
def refute_in_epsilon(a, b, epsilon = T.unsafe(nil), msg = nil); end
|
|
87
|
+
|
|
88
|
+
sig { params(collection: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
89
|
+
def refute_includes(collection, obj, msg = nil); end
|
|
90
|
+
|
|
91
|
+
sig { params(cls: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
92
|
+
def refute_instance_of(cls, obj, msg = nil); end
|
|
93
|
+
|
|
94
|
+
sig { params(cls: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
95
|
+
def refute_kind_of(cls, obj, msg = nil); end
|
|
96
|
+
|
|
97
|
+
sig { params(matcher: T.any(String, Regexp), obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
98
|
+
def refute_match(matcher, obj, msg = nil); end
|
|
99
|
+
|
|
100
|
+
sig { params(obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
101
|
+
def refute_nil(obj, msg = nil); end
|
|
102
|
+
|
|
103
|
+
sig { params(block: T.proc.void).returns(TrueClass) }
|
|
104
|
+
def refute_pattern(&block); end
|
|
105
|
+
|
|
106
|
+
sig { params(o1: T.anything, op: T.any(Symbol, String), o2: T.anything, msg: T.anything).returns(TrueClass) }
|
|
107
|
+
def refute_operator(o1, op, o2 = T.unsafe(nil), msg = nil); end
|
|
108
|
+
|
|
109
|
+
sig { params(path: T.any(String, Pathname), msg: T.anything).returns(TrueClass) }
|
|
110
|
+
def refute_path_exists(path, msg = nil); end
|
|
111
|
+
|
|
112
|
+
sig { params(o1: T.anything, op: T.any(String, Symbol), msg: T.anything).returns(TrueClass) }
|
|
113
|
+
def refute_predicate(o1, op, msg = nil); end
|
|
114
|
+
|
|
115
|
+
sig { params(obj: T.anything, meth: T.any(String, Symbol), msg: T.anything, include_all: T::Boolean).returns(TrueClass) }
|
|
116
|
+
def refute_respond_to(obj, meth, msg = nil, include_all: false); end
|
|
117
|
+
|
|
118
|
+
sig { params(exp: T.anything, act: T.anything, msg: T.anything).returns(TrueClass) }
|
|
119
|
+
def refute_same(exp, act, msg = nil); end
|
|
120
|
+
end
|