mal 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.md +46 -0
- data/Rakefile +53 -0
- data/lib/mal.rb +215 -0
- data/mal.gemspec +62 -0
- data/spec/mal_spec.rb +221 -0
- data/spec/spec_helper.rb +9 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 460e0eb3883bf8c602f014845e5b878d8e07abbc
|
4
|
+
data.tar.gz: 52f32e1f129c60b75edfe5f4e4b8b2f979895e00
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 453e66b57683c0afc28c7a5ec1a28ae721208d4e113701e685cfc2e475c03e9edca0a06c27d95e949a9912172a6060b05aaca89d9116c64f07e4e6d12d20b9b9
|
7
|
+
data.tar.gz: cbfeb3a92260eeeb93639471b86e83b18efbd2f7da9f532b866f850cd6ab53446c0eaa3f830bb483b499781cf48cea74f7b1a3e22c8e4c7d82fb9bf2316b00db
|
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2016 Julik Tarkhanov
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# mal
|
2
|
+
|
3
|
+
Minuscule (Type) Algerba - a type-matching thing for Ruby.
|
4
|
+
|
5
|
+
The library allows you to do simple type matching based on _any_ Ruby types that support the "triqual"
|
6
|
+
operator (type equality). It is distantly related to libraries like rb_dry_types
|
7
|
+
|
8
|
+
A basic building block for these checks can be any object that supports `===`.
|
9
|
+
For example, to verify that a certain variable is either a String or a Numeric
|
10
|
+
|
11
|
+
Mal.Either(Numeric, String) === "ohai" # => true
|
12
|
+
Mal.Either(Numeric, String) === false # => false
|
13
|
+
|
14
|
+
Where this gets interesting is when you use combinations and unions. For instance, you can check if an
|
15
|
+
Array only contains booleans:
|
16
|
+
|
17
|
+
Mal.ArrayOf(Mal.Either(true, false)) === [true, false, false] #=> true
|
18
|
+
|
19
|
+
You can also match Hashes, which lends itself to JSON assertions quite nicely:
|
20
|
+
|
21
|
+
Mal.HashOf(name: String, age: Numeric) === {name: 'Jane', age: 22, accepted_license_agreement: true} #=> true
|
22
|
+
|
23
|
+
You can also use these type matchers for case statements:
|
24
|
+
|
25
|
+
case fetch_api_response
|
26
|
+
when Mal.HashWith(error: Mal.Anything())
|
27
|
+
raise ...
|
28
|
+
when Mal.HashWith(inserted: Mal.Boolean())
|
29
|
+
# insert successful
|
30
|
+
end
|
31
|
+
|
32
|
+
## Contributing to mal
|
33
|
+
|
34
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
35
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
36
|
+
* Fork the project.
|
37
|
+
* Start a feature/bugfix branch.
|
38
|
+
* Commit and push until you are happy with your contribution.
|
39
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
40
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
41
|
+
|
42
|
+
## Copyright
|
43
|
+
|
44
|
+
Copyright (c) 2016 Julik Tarkhanov. See LICENSE.txt for
|
45
|
+
further details.
|
46
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
require_relative 'lib/mal'
|
16
|
+
|
17
|
+
Jeweler::Tasks.new do |gem|
|
18
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
19
|
+
gem.version = Mal::VERSION
|
20
|
+
gem.name = "mal"
|
21
|
+
gem.homepage = "http://github.com/julik/mal"
|
22
|
+
gem.license = "MIT"
|
23
|
+
gem.summary = 'Minuscule algebraic types for Ruby'
|
24
|
+
gem.description = 'Minuscule algebraic types for Ruby'
|
25
|
+
gem.email = "me@julik.nl"
|
26
|
+
gem.authors = ["Julik Tarkhanov"]
|
27
|
+
# dependencies defined in Gemfile
|
28
|
+
end
|
29
|
+
Jeweler::RubygemsDotOrgTasks.new
|
30
|
+
|
31
|
+
require 'rspec/core'
|
32
|
+
require 'rspec/core/rake_task'
|
33
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
34
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Code coverage detail"
|
38
|
+
task :simplecov do
|
39
|
+
ENV['COVERAGE'] = "true"
|
40
|
+
Rake::Task['spec'].execute
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :spec
|
44
|
+
|
45
|
+
require 'rdoc/task'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "mal #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/lib/mal.rb
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
module Mal
|
2
|
+
VERSION = '0.0.1'
|
3
|
+
|
4
|
+
class AnythingT
|
5
|
+
def ===(value)
|
6
|
+
true
|
7
|
+
end
|
8
|
+
def inspect
|
9
|
+
'Anything()'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class OnlyT
|
14
|
+
def initialize(matchable)
|
15
|
+
@matchable = matchable
|
16
|
+
end
|
17
|
+
|
18
|
+
def ===(value)
|
19
|
+
@matchable === value
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
'Only(%s)' % @matchable.inspect
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class NilT < OnlyT
|
28
|
+
def inspect; 'Nil()'; end
|
29
|
+
end
|
30
|
+
|
31
|
+
class HashT
|
32
|
+
def initialize(**required_keys_to_matchers)
|
33
|
+
@required_keys_to_matchers = required_keys_to_matchers
|
34
|
+
end
|
35
|
+
|
36
|
+
def ===(value)
|
37
|
+
return false unless value.is_a?(Hash)
|
38
|
+
@required_keys_to_matchers.each_pair do |k,value_matcher|
|
39
|
+
return false unless value.key? k
|
40
|
+
return false unless value_matcher === value[k]
|
41
|
+
end
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
'HashWith(%s)' % @required_keys_to_matchers.inspect[1..-2]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class HashOfOnlyT < HashT
|
51
|
+
def ===(value)
|
52
|
+
return false unless super
|
53
|
+
@required_keys_to_matchers.keys == value.keys
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
'HashOf(%s)' % @required_keys_to_matchers.inspect[1..-2]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class ArrayT < OnlyT
|
62
|
+
def initialize(matcher_for_each_array_element)
|
63
|
+
@matcher_for_each_array_element = matcher_for_each_array_element
|
64
|
+
end
|
65
|
+
|
66
|
+
def ===(value)
|
67
|
+
return false unless Array === value
|
68
|
+
return false unless value.any?
|
69
|
+
value.each do |value_element|
|
70
|
+
return false unless @matcher_for_each_array_element === value_element
|
71
|
+
end
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect
|
76
|
+
'ArrayOf(%s)' % @matcher_for_each_array_element.inspect
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class LengthT < OnlyT
|
81
|
+
def initialize(desired_length)
|
82
|
+
@desired_length = length
|
83
|
+
end
|
84
|
+
|
85
|
+
def ===(value)
|
86
|
+
return false unless value.respond_to? :length
|
87
|
+
@desired_length == value.length
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class MinLengthT < LengthT
|
92
|
+
def ===(value)
|
93
|
+
return false unless value.respond_to? :length
|
94
|
+
@desired_length >= value.length
|
95
|
+
end
|
96
|
+
def inspect; 'OfAtLeastElements(%d)' % @desired_length; end
|
97
|
+
end
|
98
|
+
|
99
|
+
class MaxLengthT < LengthT
|
100
|
+
def ===(value)
|
101
|
+
return false unless value.respond_to? :length
|
102
|
+
@desired_length <= value.length
|
103
|
+
end
|
104
|
+
def inspect; 'OfAtMostElements(%d)' % @desired_length; end
|
105
|
+
end
|
106
|
+
|
107
|
+
class EitherT < OnlyT
|
108
|
+
def initialize(*types)
|
109
|
+
@types = types
|
110
|
+
end
|
111
|
+
|
112
|
+
def ===(value)
|
113
|
+
@types.any? {|type| type === value }
|
114
|
+
end
|
115
|
+
|
116
|
+
def inspect
|
117
|
+
'Either(%s)' % @types.map{|e| e.inspect }.join(', ')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class UnionT < OnlyT
|
122
|
+
def initialize(*types)
|
123
|
+
@types = types
|
124
|
+
end
|
125
|
+
|
126
|
+
def ===(value)
|
127
|
+
@types.all? {|type| type === value}
|
128
|
+
end
|
129
|
+
|
130
|
+
def inspect
|
131
|
+
'Both(%s)' % @types.map{|e| e.inspect }.join(', ')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class MaybeT < EitherT
|
136
|
+
def inspect; 'Maybe(%s)' % @types[1].inspect; end
|
137
|
+
end
|
138
|
+
|
139
|
+
class BoolT < EitherT
|
140
|
+
def initialize; super(TrueClass, FalseClass); end
|
141
|
+
def inspect; 'Bool()'; end
|
142
|
+
end
|
143
|
+
|
144
|
+
private_constant :NilT, :BoolT, :EitherT, :MaybeT, :ArrayT, :HashT, :BoolT, :HashOfOnlyT, :MaxLengthT, :MinLengthT
|
145
|
+
|
146
|
+
# Specifies a value that may only ever be `nil` and nothing else
|
147
|
+
def Nil()
|
148
|
+
NilT.new(NilClass)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Specifies a value that is either `true` or `false` (truthy or falsy values do not work)
|
152
|
+
def Bool()
|
153
|
+
BoolT.new
|
154
|
+
end
|
155
|
+
|
156
|
+
# Specifies a value that matches only the given matcher
|
157
|
+
def Only(matchable)
|
158
|
+
OnlyT.new(matchable)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Specifies a value that matches both the given matchers. For instance,
|
162
|
+
# can be a matcher for both a String and a Regexp
|
163
|
+
def Both(*matchables)
|
164
|
+
UnionT.new(*matchables)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Specifies a value that matches either one of the given speciciers
|
168
|
+
def Either(*matchables)
|
169
|
+
EitherT.new(*matchables)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Specifies a value that is either matching the given typespec, or is nil
|
173
|
+
def Maybe(matchable)
|
174
|
+
MaybeT.new(NilClass, matchable)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Specifies an Array of at least 1 element, where each element matches the
|
178
|
+
# typespec for the array element
|
179
|
+
def ArrayOf(typespec_for_array_element)
|
180
|
+
ArrayT.new(typespec_for_array_element)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Specifies a Hash containing at least the given keys, with values at those
|
184
|
+
# keys matching the given matchers. For example, for a Hash having at
|
185
|
+
# least the `:name` key with a corresponding value that is a String:
|
186
|
+
# HashOf(name: String)
|
187
|
+
# Since the match is non-strict, it will also match a Hash having more keys
|
188
|
+
# HashOf(name: String) === {name: 'John Doe', age: 21} #=> true
|
189
|
+
def HashWith(**keys_to_values)
|
190
|
+
HashT.new(**keys_to_values)
|
191
|
+
end
|
192
|
+
|
193
|
+
def OfAtLeastElements(n)
|
194
|
+
MinLengthT.new(n)
|
195
|
+
end
|
196
|
+
|
197
|
+
def OfAtMostElements(n)
|
198
|
+
MaxLengthT.new(n)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Specifies a Hash containing only the given keys, with values at those
|
202
|
+
# keys matching the given matchers. For example, for a Hash having at
|
203
|
+
# the `:name` key with a corresponding value that is a String:
|
204
|
+
#
|
205
|
+
# HashOfOnly(name: String)
|
206
|
+
#
|
207
|
+
# Because the match is-strict, it will not match a Hash having additional keys
|
208
|
+
#
|
209
|
+
# HashOf(name: String) === {name: 'John Doe', age: 21} #=> false
|
210
|
+
def HashOf(**keys_to_values)
|
211
|
+
HashOfOnlyT.new(**keys_to_values)
|
212
|
+
end
|
213
|
+
|
214
|
+
extend self
|
215
|
+
end
|
data/mal.gemspec
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: mal 0.0.1 ruby lib
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "mal"
|
9
|
+
s.version = "0.0.1"
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.authors = ["Julik Tarkhanov"]
|
14
|
+
s.date = "2016-09-16"
|
15
|
+
s.description = "Minuscule algebraic types for Ruby"
|
16
|
+
s.email = "me@julik.nl"
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE.txt",
|
19
|
+
"README.md"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".rspec",
|
24
|
+
"Gemfile",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.md",
|
27
|
+
"Rakefile",
|
28
|
+
"lib/mal.rb",
|
29
|
+
"mal.gemspec",
|
30
|
+
"spec/mal_spec.rb",
|
31
|
+
"spec/spec_helper.rb"
|
32
|
+
]
|
33
|
+
s.homepage = "http://github.com/julik/mal"
|
34
|
+
s.licenses = ["MIT"]
|
35
|
+
s.rubygems_version = "2.5.1"
|
36
|
+
s.summary = "Minuscule algebraic types for Ruby"
|
37
|
+
|
38
|
+
if s.respond_to? :specification_version then
|
39
|
+
s.specification_version = 4
|
40
|
+
|
41
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
42
|
+
s.add_development_dependency(%q<rack>, ["~> 1"])
|
43
|
+
s.add_development_dependency(%q<rspec>, ["~> 3.2"])
|
44
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
45
|
+
s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
|
46
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
47
|
+
else
|
48
|
+
s.add_dependency(%q<rack>, ["~> 1"])
|
49
|
+
s.add_dependency(%q<rspec>, ["~> 3.2"])
|
50
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
51
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
52
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
53
|
+
end
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<rack>, ["~> 1"])
|
56
|
+
s.add_dependency(%q<rspec>, ["~> 3.2"])
|
57
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
58
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
59
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
data/spec/mal_spec.rb
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Mal' do
|
4
|
+
include Mal
|
5
|
+
|
6
|
+
def expect_match_of(typespec, value)
|
7
|
+
match_v = typespec === value
|
8
|
+
expect(match_v).to eq(true), "Expected #{value.inspect} to match #{typespec.inspect}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def expect_no_match_of(typespec, value)
|
12
|
+
match_v = typespec === value
|
13
|
+
expect(match_v).to eq(false), "Expected #{value.inspect} not to match #{typespec.inspect}"
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'Nil()' do
|
17
|
+
it 'provides a good inspect' do
|
18
|
+
expect(Nil().inspect).to eq('Nil()')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'matches nil only' do
|
22
|
+
expect_match_of(Nil(), nil)
|
23
|
+
expect_no_match_of(Nil(), 0)
|
24
|
+
expect_no_match_of(Nil(), Object.new)
|
25
|
+
expect_no_match_of(Nil(), self)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'Bool()' do
|
30
|
+
it 'provides a good inspect' do
|
31
|
+
expect(Bool().inspect).to eq('Bool()')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'matches only true and false' do
|
35
|
+
expect_match_of(Bool(), true)
|
36
|
+
expect_match_of(Bool(), false)
|
37
|
+
expect_no_match_of(Bool(), nil)
|
38
|
+
expect_no_match_of(Bool(), self)
|
39
|
+
expect_no_match_of(Bool(), 'hello world')
|
40
|
+
expect_no_match_of(Bool(), 123)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'Either()' do
|
45
|
+
it 'provides a good inspect' do
|
46
|
+
expect(Either(String, Hash).inspect).to eq('Either(String, Hash)')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'matches either of given' do
|
50
|
+
m = Either(String, TrueClass)
|
51
|
+
expect_match_of(m, 'hello world')
|
52
|
+
expect_match_of(m, true)
|
53
|
+
expect_no_match_of(m, false)
|
54
|
+
expect_no_match_of(m, nil)
|
55
|
+
expect_no_match_of(m, self)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'Maybe()' do
|
60
|
+
it 'provides a good inspect' do
|
61
|
+
expect(Maybe(String).inspect).to eq('Maybe(String)')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'matches a nil or a given type' do
|
65
|
+
m = Maybe(String)
|
66
|
+
expect_match_of(m, nil)
|
67
|
+
expect_match_of(m, 'foo')
|
68
|
+
expect_no_match_of(m, false)
|
69
|
+
expect_no_match_of(m, self)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'ArrayOf()' do
|
74
|
+
it 'provides a good inspect' do
|
75
|
+
expect(ArrayOf(Maybe(Fixnum)).inspect).to eq('ArrayOf(Maybe(Fixnum))')
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'does not match objects of other types' do
|
79
|
+
expect_no_match_of(ArrayOf(TrueClass), nil)
|
80
|
+
expect_no_match_of(ArrayOf(TrueClass), false)
|
81
|
+
expect_no_match_of(ArrayOf(TrueClass), self)
|
82
|
+
expect_no_match_of(ArrayOf(TrueClass), 123)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'does not match an empty Array' do
|
86
|
+
expect_no_match_of(ArrayOf(TrueClass), [])
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'matches an Array containing objects of matching types' do
|
90
|
+
m = ArrayOf(Fixnum)
|
91
|
+
expect_no_match_of(m, [true, false, 1, 'foo'])
|
92
|
+
expect_match_of(m, [1, 78451, 546])
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'matches an Array of elements parametrized via union types' do
|
96
|
+
m = ArrayOf(Either(Fixnum, Bool()))
|
97
|
+
expect_no_match_of(m, [true, false, 1, 'foo'])
|
98
|
+
expect_match_of(m, [true, false, 1])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'Both()' do
|
103
|
+
it 'provides a good inspect' do
|
104
|
+
m = Both(String, /abc/)
|
105
|
+
expect(m.inspect).to eq("Both(String, /abc/)")
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'provides matching against all the elements' do
|
109
|
+
m = Both(String, /abc/)
|
110
|
+
expect_match_of(m, 'this is an abc string')
|
111
|
+
expect_no_match_of(m, 'this is a string that does match the second parameter')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe 'HashWith()' do
|
116
|
+
it 'provides a good inspect' do
|
117
|
+
m = HashWith(foo: Maybe(Fixnum))
|
118
|
+
expect(m.inspect).to eq("HashWith(:foo=>Maybe(Fixnum))")
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'does not match other types' do
|
122
|
+
m = HashWith(foo: Symbol)
|
123
|
+
expect_no_match_of(m, nil)
|
124
|
+
expect_no_match_of(m, [])
|
125
|
+
expect_no_match_of(m, self)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'does match an empty Hash when asked to' do
|
129
|
+
m = HashWith({})
|
130
|
+
expect_match_of(m, {})
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'does not match a Hash whose value does not satisfy the matcher' do
|
134
|
+
m = HashWith(some_key: Nil())
|
135
|
+
expect_no_match_of(m, {some_key: true})
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'does match a Hash whose keys/values do satisfy the matcher' do
|
139
|
+
m = HashWith(some_key: Maybe(String))
|
140
|
+
expect_match_of(m, {some_key: nil})
|
141
|
+
expect_match_of(m, {some_key: 'hello world'})
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'does match a Hash that has more keys than requested' do
|
145
|
+
m = HashWith(name: String)
|
146
|
+
expect_match_of(m, {name: 'John Doe', age: 21})
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe 'HashOf()' do
|
151
|
+
it 'provides a good inspect' do
|
152
|
+
m = HashOf(foo: Maybe(Fixnum))
|
153
|
+
expect(m.inspect).to eq("HashOf(:foo=>Maybe(Fixnum))")
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'does not match other types' do
|
157
|
+
m = HashOf(foo: Symbol)
|
158
|
+
expect_no_match_of(m, nil)
|
159
|
+
expect_no_match_of(m, [])
|
160
|
+
expect_no_match_of(m, self)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'does match an empty Hash when asked to' do
|
164
|
+
m = HashOf({})
|
165
|
+
expect_match_of(m, {})
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'does not match a Hash whose value does not satisfy the matcher' do
|
169
|
+
m = HashOf(some_key: Nil())
|
170
|
+
expect_no_match_of(m, {some_key: true})
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'does match a Hash whose keys/values do satisfy the matcher' do
|
174
|
+
m = HashOf(some_key: Maybe(String))
|
175
|
+
expect_match_of(m, {some_key: nil})
|
176
|
+
expect_match_of(m, {some_key: 'hello world'})
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'does not match a Hash that has more keys than requested' do
|
180
|
+
m = HashOf(name: String)
|
181
|
+
expect_no_match_of(m, {name: 'John Doe', age: 21})
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe 'HashOf with complex nested matchers' do
|
186
|
+
it 'matches the User response' do
|
187
|
+
m = Mal::HashWith(
|
188
|
+
user: Mal::HashWith(
|
189
|
+
active_subscription: Mal::Bool(),
|
190
|
+
profile_picture_url: Mal::Maybe(String),
|
191
|
+
full_name: String,
|
192
|
+
id: Integer
|
193
|
+
)
|
194
|
+
)
|
195
|
+
|
196
|
+
expect_match_of(m, {user: {
|
197
|
+
id: 123,
|
198
|
+
active_subscription: true,
|
199
|
+
profile_picture_url: 'http://url.com',
|
200
|
+
full_name: 'John Doe',
|
201
|
+
}})
|
202
|
+
|
203
|
+
expect_match_of(m, {user: {
|
204
|
+
id: 123,
|
205
|
+
interests: ['Fishing', 'Hiking'],
|
206
|
+
active_subscription: true,
|
207
|
+
profile_picture_url: 'http://url.com',
|
208
|
+
full_name: 'John Doe',
|
209
|
+
}})
|
210
|
+
|
211
|
+
expect_no_match_of(m, {user: {
|
212
|
+
# skip the "id" field and let the matcher fail
|
213
|
+
interests: ['Fishing', 'Hiking'],
|
214
|
+
active_subscription: true,
|
215
|
+
profile_picture_url: 'http://url.com',
|
216
|
+
full_name: 'John Doe',
|
217
|
+
}})
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Julik Tarkhanov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: jeweler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.0.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.0.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Minuscule algebraic types for Ruby
|
84
|
+
email: me@julik.nl
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files:
|
88
|
+
- LICENSE.txt
|
89
|
+
- README.md
|
90
|
+
files:
|
91
|
+
- ".document"
|
92
|
+
- ".rspec"
|
93
|
+
- Gemfile
|
94
|
+
- LICENSE.txt
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- lib/mal.rb
|
98
|
+
- mal.gemspec
|
99
|
+
- spec/mal_spec.rb
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
homepage: http://github.com/julik/mal
|
102
|
+
licenses:
|
103
|
+
- MIT
|
104
|
+
metadata: {}
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 2.5.1
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: Minuscule algebraic types for Ruby
|
125
|
+
test_files: []
|