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 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: