kind 0.1.0 → 0.2.0
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/.gitignore +3 -0
- data/README.md +172 -5
- data/lib/kind.rb +81 -10
- data/lib/kind/version.rb +1 -1
- metadata +2 -3
- data/Gemfile.lock +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b572e02668d3a89864333fad8605a280f3bb09f5de9c0db9800bdc956632b78
|
4
|
+
data.tar.gz: 643847caaa8791c0bb5f4304f9e14135e853fb9763c53f92d7c3760c5d384be5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8d9057024bb9c8dd10f69946e5fcf0027f07a848dd16772bcb81dd2d9eeff12f026fe3600808c24bcf60126274d27a170ee32ab37f73f7fdaa26377e2aa1704
|
7
|
+
data.tar.gz: d4872142369aadb04b2679bc1bf8bb6b690ea19b9a7208dc9b0cb3f25c32f275981479d7847e0fe884bbb3c43c1da748c40ecd47e8833ff4052848514f3fcf1c
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,27 @@
|
|
1
|
+
- [Kind](#kind)
|
2
|
+
- [Installation](#installation)
|
3
|
+
- [Usage](#usage)
|
4
|
+
- [Verifying the kind of some object](#verifying-the-kind-of-some-object)
|
5
|
+
- [Verifying the kind of some class/module](#verifying-the-kind-of-some-classmodule)
|
6
|
+
- [Built-in type checkers](#built-in-type-checkers)
|
7
|
+
- [Special type checkers](#special-type-checkers)
|
8
|
+
- [Kind.of](#kindof)
|
9
|
+
- [Kind.is](#kindis)
|
10
|
+
- [How to create a new type checker?](#how-to-create-a-new-type-checker)
|
11
|
+
- [Development](#development)
|
12
|
+
- [Contributing](#contributing)
|
13
|
+
- [License](#license)
|
14
|
+
- [Code of Conduct](#code-of-conduct)
|
15
|
+
|
1
16
|
# Kind
|
2
17
|
|
3
|
-
|
18
|
+
Basic type system for Ruby.
|
19
|
+
|
20
|
+
**Motivation:**
|
4
21
|
|
5
|
-
|
22
|
+
As a creator of Ruby gems, I have a common need that I have to handle in many of my projects: type checking of method arguments.
|
23
|
+
|
24
|
+
One of the goals of this project is to do simple type checking like `"some string".is_a?(String)`, but using a bunch of basic abstractions. So, after reading this README and realizing that you need something more robust, I recommend to you check out the [dry-types gem](https://dry-rb.org/gems/dry-types).
|
6
25
|
|
7
26
|
## Installation
|
8
27
|
|
@@ -22,7 +41,155 @@ Or install it yourself as:
|
|
22
41
|
|
23
42
|
## Usage
|
24
43
|
|
25
|
-
|
44
|
+
### Verifying the kind of some object
|
45
|
+
|
46
|
+
By default, basic type verification is strict. So, when you perform `Kind.of.Hash(value)`, if the given value was a Hash it will be returned, but if it wasn't one, an error will be raised.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Kind.of.Hash('')
|
50
|
+
# raise Kind::Error, "'' expected to be a kind of Hash"
|
51
|
+
|
52
|
+
Kind.of.Hash({a: 1})
|
53
|
+
# {a: 1}
|
54
|
+
|
55
|
+
# ---
|
56
|
+
|
57
|
+
Kind.of.Boolean(nil)
|
58
|
+
# raise Kind::Error, "nil expected to be a kind of Boolean"
|
59
|
+
|
60
|
+
Kind.of.Boolean(true) # true
|
61
|
+
Kind.of.Boolean(false) # false
|
62
|
+
```
|
63
|
+
|
64
|
+
As an alternative syntax, you can use the `Kind::Of` instead of the method. e.g: `Kind::Of::Hash('')`
|
65
|
+
|
66
|
+
But if you don't need a strict type verification, use the `.or_nil`method
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
Kind.of.Hash.or_nil('')
|
70
|
+
# nil
|
71
|
+
|
72
|
+
Kind.of.Hash.or_nil({a: 1})
|
73
|
+
# {a: 1}
|
74
|
+
|
75
|
+
# ---
|
76
|
+
|
77
|
+
Kind.of.Boolean.or_nil('') # nil
|
78
|
+
Kind.of.Boolean.or_nil(true) # true
|
79
|
+
```
|
80
|
+
|
81
|
+
And just for convenience, you can use the method `.instance?` to verify if the given object has the expected type.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
Kind.of.Hash.instance?('')
|
85
|
+
# false
|
86
|
+
|
87
|
+
# ---
|
88
|
+
|
89
|
+
Kind.of.Boolean.instance?('') # false
|
90
|
+
Kind.of.Boolean.instance?(true) # true
|
91
|
+
Kind.of.Boolean.instance?(false) # true
|
92
|
+
```
|
93
|
+
|
94
|
+
### Verifying the kind of some class/module
|
95
|
+
|
96
|
+
You can use `Kind.is` to verify if some class has the expected type as its ancestor.
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
Kind.is.Hash(String)
|
100
|
+
# false
|
101
|
+
|
102
|
+
Kind.is.Hash(Hash)
|
103
|
+
# true
|
104
|
+
|
105
|
+
Kind.is.Hash(ActiveSupport::HashWithIndifferentAccess)
|
106
|
+
# true
|
107
|
+
```
|
108
|
+
|
109
|
+
And just for convenience, you can use the method `Kind.of.*.class?` to verify if the given class has the expected type as its ancestor.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
Kind.of.Hash.class?(Hash)
|
113
|
+
# true
|
114
|
+
|
115
|
+
Kind.of.Hash.class?(ActiveSupport::HashWithIndifferentAccess)
|
116
|
+
```
|
117
|
+
|
118
|
+
## Built-in type checkers
|
119
|
+
|
120
|
+
The list of types (classes and modules) available to use with `Kind.of.*` or `Kind.is.*` are:
|
121
|
+
|
122
|
+
| Classes | Modules |
|
123
|
+
| ---------- | ---------- |
|
124
|
+
| String | Enumerable |
|
125
|
+
| Symbol | Comparable |
|
126
|
+
| Numeric | |
|
127
|
+
| Integer | |
|
128
|
+
| Float | |
|
129
|
+
| Regexp | |
|
130
|
+
| Time | |
|
131
|
+
| Array | |
|
132
|
+
| Range | |
|
133
|
+
| Hash | |
|
134
|
+
| Struct | |
|
135
|
+
| Enumerator | |
|
136
|
+
| Method | |
|
137
|
+
| Proc | |
|
138
|
+
| IO | |
|
139
|
+
| File | |
|
140
|
+
|
141
|
+
### Special type checkers
|
142
|
+
|
143
|
+
#### Kind.of
|
144
|
+
|
145
|
+
- `Kind.of.Class()`
|
146
|
+
- `Kind.of.Module()`
|
147
|
+
- `Kind.of.Lambda()`
|
148
|
+
- `Kind.of.Boolean()`
|
149
|
+
|
150
|
+
#### Kind.is
|
151
|
+
|
152
|
+
- `Kind.of.Class()`
|
153
|
+
- `Kind.of.Module()`
|
154
|
+
- `Kind.of.Boolean()`
|
155
|
+
|
156
|
+
## How to create a new type checker?
|
157
|
+
|
158
|
+
Use `Kind::Types.add()`. e.g:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
class User
|
162
|
+
end
|
163
|
+
|
164
|
+
# You can define it at the end of the file which has the class/module.
|
165
|
+
|
166
|
+
Kind::Types.add(User)
|
167
|
+
|
168
|
+
# Or, you can add the type checker within the class definition.
|
169
|
+
|
170
|
+
class User
|
171
|
+
Kind::Types.add(self)
|
172
|
+
end
|
173
|
+
|
174
|
+
# --------------- #
|
175
|
+
# Usage examples: #
|
176
|
+
# --------------- #
|
177
|
+
|
178
|
+
Kind.of.User(User.new)
|
179
|
+
# #<User:0x0000...>
|
180
|
+
|
181
|
+
Kind.of.User({})
|
182
|
+
# Kind::Error ({} expected to be a kind of User)
|
183
|
+
|
184
|
+
Kind.of.User.or_nil({})
|
185
|
+
# nil
|
186
|
+
|
187
|
+
Kind.of.User.instance?({}) # false
|
188
|
+
Kind.of.User.class?(Hash) # false
|
189
|
+
|
190
|
+
Kind.of.User.instance?(User) # true
|
191
|
+
Kind.of.User.class?(User) # true
|
192
|
+
```
|
26
193
|
|
27
194
|
## Development
|
28
195
|
|
@@ -32,7 +199,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
199
|
|
33
200
|
## Contributing
|
34
201
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
202
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/serradura/kind. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/serradura/kind/blob/master/CODE_OF_CONDUCT.md).
|
36
203
|
|
37
204
|
|
38
205
|
## License
|
@@ -41,4 +208,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
41
208
|
|
42
209
|
## Code of Conduct
|
43
210
|
|
44
|
-
Everyone interacting in the Kind project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
211
|
+
Everyone interacting in the Kind project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/serradura/kind/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/kind.rb
CHANGED
@@ -5,7 +5,7 @@ require 'kind/version'
|
|
5
5
|
module Kind
|
6
6
|
class Error < TypeError
|
7
7
|
def initialize(klass, object)
|
8
|
-
super("#{object} expected to be a kind of #{klass}")
|
8
|
+
super("#{object.inspect} expected to be a kind of #{klass}")
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -19,25 +19,86 @@ module Kind
|
|
19
19
|
def self.Class(object)
|
20
20
|
self.call(::Class, object)
|
21
21
|
end
|
22
|
+
|
23
|
+
def self.Module(object)
|
24
|
+
self.call(::Module, object)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.Boolean(object = nil)
|
28
|
+
return Kind::Of::Boolean if object.nil?
|
29
|
+
|
30
|
+
return object if object.is_a?(::TrueClass) || object.is_a?(::FalseClass)
|
31
|
+
|
32
|
+
raise Kind::Error.new('Boolean'.freeze, object)
|
33
|
+
end
|
34
|
+
|
35
|
+
module Boolean
|
36
|
+
def self.class?(value)
|
37
|
+
Kind.is.Boolean(value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.instance?(value)
|
41
|
+
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.or_nil(value)
|
45
|
+
return value if instance?(value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.Lambda(object = nil)
|
50
|
+
return Kind::Of::Lambda if object.nil?
|
51
|
+
|
52
|
+
return object if object.is_a?(::Proc) && object.lambda?
|
53
|
+
|
54
|
+
raise Kind::Error.new('Lambda'.freeze, object)
|
55
|
+
end
|
56
|
+
|
57
|
+
module Lambda
|
58
|
+
def self.class?(value)
|
59
|
+
Kind.is.Proc(value)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.instance?(value)
|
63
|
+
value.is_a?(::Proc) && value.lambda?
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.or_nil(value)
|
67
|
+
return value if instance?(value)
|
68
|
+
end
|
69
|
+
end
|
22
70
|
end
|
23
71
|
|
24
72
|
module Is
|
25
73
|
def self.call(expected, value)
|
26
|
-
expected_klass, klass = Kind.of.
|
74
|
+
expected_klass, klass = Kind.of.Module(expected), Kind.of.Module(value)
|
75
|
+
|
76
|
+
klass <= expected_klass || false
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.Class(value)
|
80
|
+
value.is_a?(::Class)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.Module(value)
|
84
|
+
value == Module || (value.is_a?(::Module) && !self.Class(value))
|
85
|
+
end
|
27
86
|
|
28
|
-
|
87
|
+
def self.Boolean(value)
|
88
|
+
klass = Kind.of.Class(value)
|
89
|
+
klass <= TrueClass || klass <= FalseClass
|
29
90
|
end
|
30
91
|
end
|
31
92
|
|
32
93
|
def self.of; Of; end
|
33
94
|
def self.is; Is; end
|
34
95
|
|
35
|
-
singleton_class.send(:alias_method, :is_a, :is)
|
36
|
-
|
37
96
|
module Types
|
97
|
+
extend self
|
98
|
+
|
38
99
|
KIND_OF = <<-RUBY
|
39
100
|
def self.%{klass}(object = nil)
|
40
|
-
return Kind::Of::%{klass}
|
101
|
+
return Kind::Of::%{klass} if object.nil?
|
41
102
|
|
42
103
|
Kind::Of.call(::%{klass}, object)
|
43
104
|
end
|
@@ -65,8 +126,8 @@ module Kind
|
|
65
126
|
|
66
127
|
private_constant :KIND_OF, :KIND_IS, :KIND_OF_MODULE
|
67
128
|
|
68
|
-
def
|
69
|
-
klass_name = Kind.of.
|
129
|
+
def add(klass)
|
130
|
+
klass_name = Kind.of.Module(klass).name
|
70
131
|
|
71
132
|
return if Of.respond_to?(klass_name)
|
72
133
|
|
@@ -83,6 +144,16 @@ module Kind
|
|
83
144
|
end
|
84
145
|
end
|
85
146
|
|
86
|
-
|
87
|
-
|
147
|
+
# Classes
|
148
|
+
[
|
149
|
+
String, Symbol, Numeric, Integer, Float, Regexp, Time,
|
150
|
+
Array, Range, Hash, Struct, Enumerator,
|
151
|
+
Method, Proc,
|
152
|
+
IO, File
|
153
|
+
].each { |klass| Types.add(klass) }
|
154
|
+
|
155
|
+
# Modules
|
156
|
+
[
|
157
|
+
Enumerable, Comparable
|
158
|
+
].each { |klass| Types.add(klass) }
|
88
159
|
end
|
data/lib/kind/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kind
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Serradura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Basic type system for Ruby.
|
14
14
|
email:
|
@@ -21,7 +21,6 @@ files:
|
|
21
21
|
- ".travis.yml"
|
22
22
|
- CODE_OF_CONDUCT.md
|
23
23
|
- Gemfile
|
24
|
-
- Gemfile.lock
|
25
24
|
- LICENSE.txt
|
26
25
|
- README.md
|
27
26
|
- Rakefile
|
data/Gemfile.lock
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
kind (0.1.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
minitest (5.13.0)
|
10
|
-
rake (12.3.2)
|
11
|
-
|
12
|
-
PLATFORMS
|
13
|
-
ruby
|
14
|
-
|
15
|
-
DEPENDENCIES
|
16
|
-
kind!
|
17
|
-
minitest (~> 5.0)
|
18
|
-
rake (~> 12.0)
|
19
|
-
|
20
|
-
BUNDLED WITH
|
21
|
-
2.1.2
|