Skip to content

Commit 68ef60c

Browse files
committed
feat: Added additional Boolean, Union and Nilable types
fixes #6
1 parent 4898a9a commit 68ef60c

File tree

7 files changed

+159
-3
lines changed

7 files changed

+159
-3
lines changed

.rubocop.yml

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ Style/Documentation:
77
Enabled: false
88
Style/HashAsLastArrayItem:
99
Enabled: false
10+
Style/CaseEquality:
11+
Enabled: false
12+
Style/NilComparison:
13+
Enabled: false
14+
15+
Naming/MethodName:
16+
Exclude:
17+
- "lib/delivered/types.rb"
1018

1119
Layout/LineLength:
1220
Max: 100
@@ -23,7 +31,6 @@ Metrics/CyclomaticComplexity:
2331
Enabled: false
2432
Metrics/PerceivedComplexity:
2533
Enabled: false
26-
2734
Metrics/BlockLength:
2835
Exclude:
2936
- "test/**/*"

README.md

+54
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,57 @@ def create(name, age:)
4444
"User #{name} created with age #{age}"
4545
end
4646
```
47+
48+
### Delivered Types
49+
50+
As well as Ruby's native types (ie. `String`, `Integer`, etc.), _Delivered_ provides a couple of
51+
extra types in `Delivered::Types`.
52+
53+
You can call these directly with `Delivered::Types.Boolean`, or for brevity, assign
54+
`Delivered::Types` to `T` in your classes:
55+
56+
```ruby
57+
class User
58+
extend Delivered::Signature
59+
T = Delivered::Types
60+
end
61+
```
62+
63+
The following examples all use the `T` alias, and assumes the above.
64+
65+
#### `Boolean`
66+
67+
Value **MUST** be `true` or `false`. Does not support "truthy" or "falsy" values.
68+
69+
```ruby
70+
sig validate: T.Boolean
71+
def create(validate:); end
72+
```
73+
74+
#### `Union`
75+
76+
Value **MUST** be a union of the given list of values, that is, the value must be one of the given list.
77+
78+
```ruby
79+
sig T.Union(:male, :female)
80+
def create(gender); end
81+
```
82+
83+
#### `Nilable`
84+
85+
When a type is given, the value **MUST** be nil or of the given type.
86+
87+
```ruby
88+
sig save: T.Nilable(String)
89+
def create(save: nil); end
90+
91+
sig T.Nilable(String)
92+
def update(name = nil); end
93+
```
94+
95+
If no type is given, the value **CAN** be nil. This essentially allows any value, including nil.
96+
97+
```ruby
98+
sig save: T.Nilable
99+
def create(save: nil); end
100+
```

lib/delivered.rb

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
module Delivered
44
autoload :Signature, 'delivered/signature'
5+
autoload :Types, 'delivered/types'
56
end

lib/delivered/signature.rb

-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
module Delivered
44
module Signature
5-
NULL = Object.new
6-
75
def sig(*sig_args, **sig_kwargs, &return_blk)
86
# ap [sig_args, sig_kwargs, return_blk]
97

lib/delivered/types.rb

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# frozen_string_literal: true
2+
3+
module Delivered
4+
class UnionType
5+
def initialize(*types)
6+
@types = types
7+
end
8+
9+
def ===(value)
10+
@types.any? { |type| type === value }
11+
end
12+
end
13+
14+
class NilableType
15+
def initialize(type = nil)
16+
@type = type
17+
end
18+
19+
def ===(value)
20+
(@type.nil? ? true : nil === value) || @type === value
21+
end
22+
end
23+
24+
class BooleanType
25+
def initialize
26+
freeze
27+
end
28+
29+
def ===(value)
30+
[true, false].include?(value)
31+
end
32+
end
33+
34+
module Types
35+
module_function
36+
37+
def Nilable(type = nil) = NilableType.new(type)
38+
def Union(*types) = UnionType.new(*types)
39+
def Boolean = BooleanType.new
40+
end
41+
end

test/delivered/signature.rb

+8
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def to_s = "#{@name}, #{@age}"
3535
sig { Integer }
3636
def age = @age.to_s
3737

38+
sig(Delivered::Types.Boolean)
39+
def active=(val); end
40+
3841
sig(String, _age: Integer) { Array }
3942
def self.where(_name, _age: nil) = []
4043

@@ -106,6 +109,11 @@ def self.find_by_name(name) = User.new(name)
106109
end
107110
end
108111

112+
it 'raises on incorrect Delivered type' do
113+
user = User.new('Joel')
114+
expect { user.active = 1 }.to raise_exception NoMatchingPatternError
115+
end
116+
109117
it 'raises on missing args' do
110118
expect { User.new }.to raise_exception NoMatchingPatternError
111119
end

test/delivered/types.rb

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
describe Delivered::Types do
4+
T = Delivered::Types
5+
6+
describe 'Union' do
7+
it 'is within union' do
8+
assert T.Union(:one, :two) === :one
9+
end
10+
11+
it 'raises when value is not in union' do
12+
expect { :three => ^(T.Union(:one, :two)) }.to raise_exception NoMatchingPatternError
13+
expect { :one => ^(T.Union(:one, :two)) }.not.to raise_exception
14+
end
15+
end
16+
17+
describe 'Boolean' do
18+
it 'should be true or false' do
19+
assert T.Boolean === true
20+
assert T.Boolean === false
21+
end
22+
23+
it 'raises when not boolean' do
24+
expect { 0 => ^(T.Boolean) }.to raise_exception NoMatchingPatternError
25+
end
26+
end
27+
28+
describe 'Nilable' do
29+
it 'can be nil' do
30+
assert T.Nilable === nil
31+
end
32+
33+
with 'no given type' do
34+
it 'can be any type' do
35+
assert T.Nilable === 'hello'
36+
expect { 'hello' => ^(T.Nilable) }.not.to raise_exception NoMatchingPatternError
37+
end
38+
end
39+
40+
it 'can receive another type' do
41+
assert T.Nilable(String) === 'hello'
42+
assert T.Nilable(String) === nil
43+
expect { 'hello' => ^(T.Nilable(String)) }.not.to raise_exception
44+
expect { 1 => ^(T.Nilable(String)) }.to raise_exception NoMatchingPatternError
45+
end
46+
end
47+
end

0 commit comments

Comments
 (0)