kind 1.7.0 → 1.8.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/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:
|