kind 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +149 -4
- data/lib/kind.rb +9 -9
- data/lib/kind/checker.rb +32 -2
- data/lib/kind/maybe.rb +2 -2
- data/lib/kind/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0a88ddbce52eca41466b1c600f745b3c212e7833bbb178cabd86514e1011601
|
4
|
+
data.tar.gz: f96ab5843a3596a19863389dacda8e5865eff611163112f5092a3387ea6898a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06af14f778ea084ae699a0c00e67d6808cbdace68a838cff42782fb7150f51e3524eba060784fd0cb540d08c2ecaa4b8135f75813e7d4223b87c9d1326831c05
|
7
|
+
data.tar.gz: 16e1e11bce9f67237a3f306b526f1b1f6564fe10eafcd22c2c90f2f18fa22077155a4372a9149d24dac02d266895e36c256a147a625f3592e8686bca974ab94b
|
data/README.md
CHANGED
@@ -12,7 +12,7 @@ Basic type system for Ruby.
|
|
12
12
|
|
13
13
|
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.
|
14
14
|
|
15
|
-
One of the goals of this project is to do simple type checking like `"some string".is_a?(String)`, but
|
15
|
+
One of the goals of this project is to do simple type checking like `"some string".is_a?(String)`, but, exposing useful abstractions to do it.
|
16
16
|
|
17
17
|
## Table of Contents <!-- omit in toc -->
|
18
18
|
- [Required Ruby version](#required-ruby-version)
|
@@ -35,7 +35,9 @@ One of the goals of this project is to do simple type checking like `"some strin
|
|
35
35
|
- [Kind::Maybe#try](#kindmaybetry)
|
36
36
|
- [Kind.of.Maybe()](#kindofmaybe)
|
37
37
|
- [Kind::Optional](#kindoptional)
|
38
|
+
- [Kind.of.<Type>.as_optional](#kindoftypeas_optional)
|
38
39
|
- [Kind::Empty](#kindempty)
|
40
|
+
- [Similar Projects](#similar-projects)
|
39
41
|
- [Development](#development)
|
40
42
|
- [Contributing](#contributing)
|
41
43
|
- [License](#license)
|
@@ -131,6 +133,21 @@ Kind.of.Boolean.instance?(true) # true
|
|
131
133
|
Kind.of.Boolean.instance?(false) # true
|
132
134
|
```
|
133
135
|
|
136
|
+
**Note:** When `.instance?` is called without an argument, it will return a lambda which will perform the kind verification.
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
collection = [
|
140
|
+
{number: 1},
|
141
|
+
'number 0',
|
142
|
+
{number: 2},
|
143
|
+
[0],
|
144
|
+
]
|
145
|
+
|
146
|
+
collection
|
147
|
+
.select(&Kind.of.Hash.instance?)
|
148
|
+
.reduce(0) { |total, item| total + item.fetch(:number, 0) } # 3
|
149
|
+
```
|
150
|
+
|
134
151
|
Also, there are aliases to perform the strict type verification. e.g:
|
135
152
|
|
136
153
|
```ruby
|
@@ -205,6 +222,15 @@ kind_of_user.instance?(User) # true
|
|
205
222
|
kind_of_user.class?(Hash) # false
|
206
223
|
kind_of_user.class?(User) # true
|
207
224
|
|
225
|
+
# ------------------------------------ #
|
226
|
+
# Using methods which returns a lambda #
|
227
|
+
# ------------------------------------ #
|
228
|
+
collection = [User.new, User.new, 0, {} nil, User.new]
|
229
|
+
|
230
|
+
collection.select(&Kind.of(User).instance?).size == 3 # true
|
231
|
+
|
232
|
+
collection.map(&Kind.of(User).as_optional).select(&:some?).size == 3 # true
|
233
|
+
|
208
234
|
# Creating type checkers dynamically is cheap
|
209
235
|
# because a singleton object is created to be available for use.
|
210
236
|
|
@@ -343,7 +369,7 @@ The list of types (classes and modules) available to use with `Kind.of.*` or `Ki
|
|
343
369
|
- `Kind.of.Callable()`: verifies if the given value `respond_to?(:call)` or if it's a class/module and if its `public_instance_methods.include?(:call)`.
|
344
370
|
- `Kind.of.Maybe()` or its alias `Kind.of.Optional()`
|
345
371
|
|
346
|
-
|
372
|
+
**Note:** Remember, you can use the `Kind.is.*` method to check if some given value is a class/module with all type checkers above.
|
347
373
|
|
348
374
|
[⬆️ Back to Top](#table-of-contents-)
|
349
375
|
|
@@ -434,6 +460,10 @@ object = 'foo'
|
|
434
460
|
|
435
461
|
p Kind::Maybe[object].try(:upcase) # "FOO"
|
436
462
|
|
463
|
+
p Kind::Maybe[{}].try(:fetch, :number, 0) # 0
|
464
|
+
|
465
|
+
p Kind::Maybe[{number: 1}].try(:fetch, :number) # 1
|
466
|
+
|
437
467
|
p Kind::Maybe[object].try { |value| value.upcase } # "FOO"
|
438
468
|
|
439
469
|
#############
|
@@ -514,7 +544,119 @@ result2 =
|
|
514
544
|
puts result2 # 35
|
515
545
|
```
|
516
546
|
|
517
|
-
|
547
|
+
**Note:** The `Kind.of.Optional` is available to check if some value is a `Kind::Optional`.
|
548
|
+
|
549
|
+
[⬆️ Back to Top](#table-of-contents-)
|
550
|
+
|
551
|
+
### Kind.of.<Type>.as_optional
|
552
|
+
|
553
|
+
It is very common the need to avoid some computing when a method receives a wrong input.
|
554
|
+
In these scenarios, you could check the given input type as optional and avoid unexpected behavior. e.g:
|
555
|
+
|
556
|
+
```ruby
|
557
|
+
def person_name(params)
|
558
|
+
Kind::Of::Hash.as_optional(params)
|
559
|
+
.map { |data| data if data.values_at(:first_name, :last_name).compact.size == 2 }
|
560
|
+
.map { |data| "#{data[:first_name]} #{data[:last_name]}" }
|
561
|
+
.value_or { 'John Doe' }
|
562
|
+
end
|
563
|
+
|
564
|
+
person_name('') # "John Doe"
|
565
|
+
person_name(nil) # "John Doe"
|
566
|
+
|
567
|
+
person_name(first_name: 'Rodrigo') # "John Doe"
|
568
|
+
person_name(last_name: 'Serradura') # "John Doe"
|
569
|
+
|
570
|
+
person_name(first_name: 'Rodrigo', last_name: 'Serradura') # "Rodrigo Serradura"
|
571
|
+
|
572
|
+
#
|
573
|
+
# See below the previous implementation without using an optional.
|
574
|
+
#
|
575
|
+
def person_name(params)
|
576
|
+
if params.is_a?(Hash) && params.values_at(:first_name, :last_name).compact.size == 2
|
577
|
+
"#{params[:first_name]} #{params[:last_name]}"
|
578
|
+
else
|
579
|
+
'John Doe'
|
580
|
+
end
|
581
|
+
end
|
582
|
+
```
|
583
|
+
|
584
|
+
> Note: You could use the `.as_optional` method (or it alias `as_maybe`) with any [type checker](https://github.com/serradura/kind/blob/b177fed9cc2b3347d63963a2a2fd99f989c51a9a/README.md#type-checkers).
|
585
|
+
|
586
|
+
Let's see another example using a collection and how the method `.as_optional` works when it receives no argument.
|
587
|
+
|
588
|
+
```ruby
|
589
|
+
collection = [
|
590
|
+
{number: 1},
|
591
|
+
'number 0',
|
592
|
+
{number: 2},
|
593
|
+
[0],
|
594
|
+
]
|
595
|
+
|
596
|
+
collection
|
597
|
+
.select(&Kind.of.Hash.as_optional)
|
598
|
+
.reduce(0) do |total, item|
|
599
|
+
item.try { |data| data[:number] + total } || total
|
600
|
+
end
|
601
|
+
|
602
|
+
collection
|
603
|
+
.map(&Kind.of.Hash.as_optional).select(&:some?)
|
604
|
+
.reduce(0) { |total, item| total + item.value[:number] }
|
605
|
+
|
606
|
+
# Note: All the examples above return 3 as the sum of all hashes with numbers.
|
607
|
+
```
|
608
|
+
|
609
|
+
To finish follows an example of how to use optionals to handle arguments in coupled methods.
|
610
|
+
|
611
|
+
```ruby
|
612
|
+
module PersonIntroduction
|
613
|
+
extend self
|
614
|
+
|
615
|
+
def call(params)
|
616
|
+
optional_params = Kind::Of::Hash.as_optional(params)
|
617
|
+
|
618
|
+
"Hi my name is #{full_name(optional_params)}, I'm #{age(optional_params)} years old."
|
619
|
+
end
|
620
|
+
|
621
|
+
private
|
622
|
+
|
623
|
+
def full_name(optional)
|
624
|
+
optional.map { |data| "#{data[:first_name]} #{data[:last_name]}" }
|
625
|
+
.value_or { 'John Doe' }
|
626
|
+
end
|
627
|
+
|
628
|
+
def age(optional)
|
629
|
+
optional.map { |data| data[:age] }.value_or(0)
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
#
|
634
|
+
# See below the previous implementation without using an optional.
|
635
|
+
#
|
636
|
+
module PersonIntroduction
|
637
|
+
extend self
|
638
|
+
|
639
|
+
def call(params)
|
640
|
+
"Hi my name is #{full_name(params)}, I'm #{age(params)}"
|
641
|
+
end
|
642
|
+
|
643
|
+
private
|
644
|
+
|
645
|
+
def full_name(params)
|
646
|
+
case params
|
647
|
+
when Hash then "#{params[:first_name]} #{params[:last_name]}"
|
648
|
+
else 'John Doe'
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
def age(params)
|
653
|
+
case params
|
654
|
+
when Hash then params.fetch(:age, 0)
|
655
|
+
else 0
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|
659
|
+
```
|
518
660
|
|
519
661
|
[⬆️ Back to Top](#table-of-contents-)
|
520
662
|
|
@@ -565,6 +707,10 @@ Follows the list of constants, if the alias is available to be created:
|
|
565
707
|
|
566
708
|
[⬆️ Back to Top](#table-of-contents-)
|
567
709
|
|
710
|
+
## Similar Projects
|
711
|
+
|
712
|
+
- [dry-types](https://dry-rb.org/gems/dry-types)
|
713
|
+
|
568
714
|
## Development
|
569
715
|
|
570
716
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -575,7 +721,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
575
721
|
|
576
722
|
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).
|
577
723
|
|
578
|
-
|
579
724
|
## License
|
580
725
|
|
581
726
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/kind.rb
CHANGED
@@ -88,7 +88,7 @@ module Kind
|
|
88
88
|
|
89
89
|
def self.class?(value); Kind::Is.Class(value); end
|
90
90
|
|
91
|
-
def self.
|
91
|
+
def self.__is_instance__(value); class?(value); end
|
92
92
|
end)
|
93
93
|
|
94
94
|
# -- Module
|
@@ -128,13 +128,13 @@ module Kind
|
|
128
128
|
if ::Kind::Maybe::Value.none?(default)
|
129
129
|
__kind_undefined(value) { __kind_of(value) }
|
130
130
|
else
|
131
|
-
return value if instance?(value)
|
131
|
+
return value if value != Kind::Undefined && instance?(value)
|
132
132
|
|
133
133
|
__kind_undefined(default) { __kind_of(default) }
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
-
def self.
|
137
|
+
def self.__is_instance__(value); class?(value); end
|
138
138
|
end)
|
139
139
|
|
140
140
|
# -- Boolean
|
@@ -164,7 +164,7 @@ module Kind
|
|
164
164
|
if ::Kind::Maybe::Value.none?(default)
|
165
165
|
__kind_undefined(value) { Kind::Of::Boolean(value) }
|
166
166
|
else
|
167
|
-
return value if instance?(value)
|
167
|
+
return value if value != Kind::Undefined && instance?(value)
|
168
168
|
|
169
169
|
__kind_undefined(default) { Kind::Of::Boolean(default) }
|
170
170
|
end
|
@@ -178,7 +178,7 @@ module Kind
|
|
178
178
|
end
|
179
179
|
end
|
180
180
|
|
181
|
-
def self.
|
181
|
+
def self.__is_instance__(value);
|
182
182
|
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
183
183
|
end
|
184
184
|
|
@@ -213,7 +213,7 @@ module Kind
|
|
213
213
|
if ::Kind::Maybe::Value.none?(default)
|
214
214
|
__kind_undefined(value) { Kind::Of::Lambda(value) }
|
215
215
|
else
|
216
|
-
return value if instance?(value)
|
216
|
+
return value if value != Kind::Undefined && instance?(value)
|
217
217
|
|
218
218
|
__kind_undefined(default) { Kind::Of::Lambda(default) }
|
219
219
|
end
|
@@ -227,7 +227,7 @@ module Kind
|
|
227
227
|
end
|
228
228
|
end
|
229
229
|
|
230
|
-
def self.
|
230
|
+
def self.__is_instance__(value)
|
231
231
|
value.is_a?(__kind) && value.lambda?
|
232
232
|
end
|
233
233
|
end)
|
@@ -261,7 +261,7 @@ module Kind
|
|
261
261
|
if ::Kind::Maybe::Value.none?(default)
|
262
262
|
__kind_undefined(value) { Kind::Of::Callable(value) }
|
263
263
|
else
|
264
|
-
return value if instance?(value)
|
264
|
+
return value if value != Kind::Undefined && instance?(value)
|
265
265
|
|
266
266
|
__kind_undefined(default) { Kind::Of::Callable(default) }
|
267
267
|
end
|
@@ -275,7 +275,7 @@ module Kind
|
|
275
275
|
end
|
276
276
|
end
|
277
277
|
|
278
|
-
def self.
|
278
|
+
def self.__is_instance__(value);
|
279
279
|
value.respond_to?(:call)
|
280
280
|
end
|
281
281
|
end)
|
data/lib/kind/checker.rb
CHANGED
@@ -11,17 +11,28 @@ module Kind
|
|
11
11
|
|
12
12
|
return Kind::Of.(__kind, value) if ::Kind::Maybe::Value.none?(default)
|
13
13
|
|
14
|
-
instance?(value) ? value : Kind::Of.(__kind, default)
|
14
|
+
value != Kind::Undefined && instance?(value) ? value : Kind::Of.(__kind, default)
|
15
15
|
end
|
16
16
|
|
17
17
|
def [](value, options = options = Empty::HASH)
|
18
18
|
instance(value, options)
|
19
19
|
end
|
20
20
|
|
21
|
-
def instance?(value)
|
21
|
+
def instance?(value = Kind::Undefined)
|
22
|
+
return __is_instance__(value) if value != Kind::Undefined
|
23
|
+
|
24
|
+
is_instance_to_proc
|
25
|
+
end
|
26
|
+
|
27
|
+
def __is_instance__(value)
|
22
28
|
value.is_a?(__kind)
|
23
29
|
end
|
24
30
|
|
31
|
+
def is_instance_to_proc
|
32
|
+
@is_instance_to_proc ||=
|
33
|
+
-> checker { -> value { checker.__is_instance__(value) } }.call(self)
|
34
|
+
end
|
35
|
+
|
25
36
|
def or_nil(value)
|
26
37
|
return value if instance?(value)
|
27
38
|
end
|
@@ -29,6 +40,25 @@ module Kind
|
|
29
40
|
def or_undefined(value)
|
30
41
|
or_nil(value) || Kind::Undefined
|
31
42
|
end
|
43
|
+
|
44
|
+
def as_maybe(value = Kind::Undefined)
|
45
|
+
return __as_maybe__(value) if value != Kind::Undefined
|
46
|
+
|
47
|
+
as_maybe_to_proc
|
48
|
+
end
|
49
|
+
|
50
|
+
def as_optional(value = Kind::Undefined)
|
51
|
+
as_maybe(value)
|
52
|
+
end
|
53
|
+
|
54
|
+
def __as_maybe__(value)
|
55
|
+
Kind::Maybe.new(or_nil(value))
|
56
|
+
end
|
57
|
+
|
58
|
+
def as_maybe_to_proc
|
59
|
+
@as_maybe_to_proc ||=
|
60
|
+
-> checker { -> value { checker.__as_maybe__(value) } }.call(self)
|
61
|
+
end
|
32
62
|
end
|
33
63
|
|
34
64
|
private_constant :Checkable
|
data/lib/kind/maybe.rb
CHANGED
@@ -87,10 +87,10 @@ module Kind
|
|
87
87
|
|
88
88
|
alias_method :then, :map
|
89
89
|
|
90
|
-
def try(method_name = Undefined, &block)
|
90
|
+
def try(method_name = Undefined, *args, &block)
|
91
91
|
fn = method_name == Undefined ? block : Kind.of.Symbol(method_name).to_proc
|
92
92
|
|
93
|
-
result = fn.call(value)
|
93
|
+
result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
|
94
94
|
|
95
95
|
return result if Maybe::Value.some?(result)
|
96
96
|
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: 1.
|
4
|
+
version: 1.8.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: 2020-05-
|
11
|
+
date: 2020-05-04 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Basic type system for Ruby (free of dependencies).
|
14
14
|
email:
|