Skip to content

Commit

Permalink
feat: ArrayOf and Enumerable types
Browse files Browse the repository at this point in the history
  • Loading branch information
joelmoss committed Jun 6, 2024
1 parent f83fcb0 commit d585139
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 1 deletion.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,29 @@ sig name: T.RangeOf(Integer)
def create(name: 1...2); end
```
#### `ArrayOf`
Value **MUST** be an Array of the given type
```ruby
sig names: T.ArrayOf(String)
def create(names: ['Joel', 'Ash']); end
```
#### `Enumerable`
Value **MUST** be an Enumerable of the optional given type
```ruby
sig users: T.Enumerable
def create(users: [1, 2]); end
```
```ruby
sig users: T.Enumerable(User)
def create(users: User.all); end
```
#### `Any`
Value **MUST** be any of the given list of values, that is, the value must be one of the given list.
Expand Down
2 changes: 2 additions & 0 deletions lib/delivered.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

module Delivered
EXPENSIVE_TYPE_CHECKS = ENV['DELIVERED_EXPENSIVE_TYPE_CHECKS'] != 'false'

class ArgumentError < ArgumentError
end

Expand Down
40 changes: 39 additions & 1 deletion lib/delivered/types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,42 @@ def ===(value)
end
end

class EnumerableType
def initialize(type)
@type = type
end

def inspect = "Enumerable(#{@type.inspect})"

if Delivered::EXPENSIVE_TYPE_CHECKS
def ===(value)
Enumerable === value && value.all? { |item| @type === item }
end
else
def ===(value)
Enumerable === value && (value.empty? || @type === value.first)
end
end
end

class ArrayOfType
def initialize(type)
@type = type
end

def inspect = "ArrayOf(#{@type.inspect})"

if Delivered::EXPENSIVE_TYPE_CHECKS
def ===(value)
Array === value && value.all? { |item| @type === item }
end
else
def ===(value)
Array === value && (value.empty? || @type === value[0])
end
end
end

class RangeOfType
def initialize(type)
@type = type
Expand Down Expand Up @@ -68,7 +104,9 @@ module Types
module_function

def Nilable(type = nil) = NilableType.new(type)
def RangeOf(type = nil) = RangeOfType.new(type)
def Enumerable(type = nil) = EnumerableType.new(type)
def ArrayOf(type) = ArrayOfType.new(type)
def RangeOf(type) = RangeOfType.new(type)
def RespondTo(*methods) = RespondToType.new(*methods)
def Any(*types) = AnyType.new(*types)
def Boolean = BooleanType.new
Expand Down
33 changes: 33 additions & 0 deletions test/delivered/types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,39 @@
end
end

describe 'ArrayOf' do
it 'should be an Array of given type' do
assert T.ArrayOf(Integer) === [1, 2]
assert T.ArrayOf(Integer) === []
end

it 'raises when not an Array' do
expect { 1 => ^(T.ArrayOf(Integer)) }.to raise_exception NoMatchingPatternError
end

it 'raises when not an Array of given type' do
expect { [1] => ^(T.ArrayOf(String)) }.to raise_exception NoMatchingPatternError
expect { ['w', 1] => ^(T.ArrayOf(String)) }.to raise_exception NoMatchingPatternError
end
end

describe 'Enumerable' do
it 'should be an Enumerable of given type' do
assert T.Enumerable === []
assert T.Enumerable(Integer) === [1, 2]
assert T.Enumerable(Integer) === []
end

it 'raises when not an Enumerable' do
expect { 1 => ^(T.Enumerable(Integer)) }.to raise_exception NoMatchingPatternError
end

it 'raises when not an Enumerable of given type' do
expect { [1] => ^(T.Enumerable(String)) }.to raise_exception NoMatchingPatternError
expect { ['w', 1] => ^(T.Enumerable(String)) }.to raise_exception NoMatchingPatternError
end
end

describe 'RangeOf' do
it 'should be a Range of given type' do
assert T.RangeOf(Integer) === (1..2)
Expand Down

0 comments on commit d585139

Please sign in to comment.