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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 273fcdcef40d8b61e552bb440f93a7b83adfd29621e1809201a66117babf9974
4
- data.tar.gz: 84dac931b67ccc4ee895a8c2a4c3326ccfe0faf0540df09ba9fbcbfbe5034edc
3
+ metadata.gz: a0a88ddbce52eca41466b1c600f745b3c212e7833bbb178cabd86514e1011601
4
+ data.tar.gz: f96ab5843a3596a19863389dacda8e5865eff611163112f5092a3387ea6898a4
5
5
  SHA512:
6
- metadata.gz: b3d702df70066307c7dac3058c41dab688de46c606d57455194d77570823d051dab5fd43b5c87c870dee58b9cef41a9e93a7d9d4639fe39489cc4b0e2d93d4bf
7
- data.tar.gz: 4c02cd4e43a0407167f4284c6972feed925380a0d71a8c5dedcb474a24f458954175a929dac9d5b4509520cfea663e45692dba67269712bc6e8d83e53b80a8aa
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 using a bunch of basic abstractions. So, after reading this README and realizing that you need something more robust, I recommend you check out the [dry-types gem](https://dry-rb.org/gems/dry-types).
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
- PS: Remember, you can use the `Kind.is.*` method to check if some given value is a class/module with all type checkers above.
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
- PS: The `Kind.of.Optional` is available to check if some value is a `Kind::Optional`.
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.instance?(value); class?(value); end
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.instance?(value); class?(value); end
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.instance?(value);
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.instance?(value)
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.instance?(value);
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kind
4
- VERSION = '1.7.0'
4
+ VERSION = '1.8.0'
5
5
  end
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.7.0
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-03 00:00:00.000000000 Z
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: