kind 3.0.0 → 5.0.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -0
  3. data/.travis.sh +37 -12
  4. data/.travis.yml +6 -5
  5. data/CHANGELOG.md +1295 -0
  6. data/Gemfile +10 -2
  7. data/README.md +985 -490
  8. data/lib/kind.rb +35 -287
  9. data/lib/kind/active_model/kind_validator.rb +20 -13
  10. data/lib/kind/core.rb +8 -0
  11. data/lib/kind/core/kind.rb +61 -0
  12. data/lib/kind/core/undefined.rb +7 -0
  13. data/lib/kind/dig.rb +40 -0
  14. data/lib/kind/empty.rb +4 -12
  15. data/lib/kind/empty/constant.rb +7 -0
  16. data/lib/kind/error.rb +2 -6
  17. data/lib/kind/maybe.rb +14 -153
  18. data/lib/kind/maybe/none.rb +57 -0
  19. data/lib/kind/maybe/result.rb +51 -0
  20. data/lib/kind/maybe/some.rb +90 -0
  21. data/lib/kind/maybe/typed.rb +29 -0
  22. data/lib/kind/maybe/wrappable.rb +33 -0
  23. data/lib/kind/presence.rb +33 -0
  24. data/lib/kind/try.rb +34 -0
  25. data/lib/kind/type_checker.rb +87 -0
  26. data/lib/kind/type_checkers.rb +30 -0
  27. data/lib/kind/type_checkers/core/array.rb +17 -0
  28. data/lib/kind/type_checkers/core/class.rb +13 -0
  29. data/lib/kind/type_checkers/core/comparable.rb +13 -0
  30. data/lib/kind/type_checkers/core/enumerable.rb +13 -0
  31. data/lib/kind/type_checkers/core/enumerator.rb +13 -0
  32. data/lib/kind/type_checkers/core/file.rb +13 -0
  33. data/lib/kind/type_checkers/core/float.rb +13 -0
  34. data/lib/kind/type_checkers/core/hash.rb +17 -0
  35. data/lib/kind/type_checkers/core/integer.rb +13 -0
  36. data/lib/kind/type_checkers/core/io.rb +13 -0
  37. data/lib/kind/type_checkers/core/method.rb +13 -0
  38. data/lib/kind/type_checkers/core/module.rb +17 -0
  39. data/lib/kind/type_checkers/core/numeric.rb +13 -0
  40. data/lib/kind/type_checkers/core/proc.rb +13 -0
  41. data/lib/kind/type_checkers/core/queue.rb +14 -0
  42. data/lib/kind/type_checkers/core/range.rb +13 -0
  43. data/lib/kind/type_checkers/core/regexp.rb +13 -0
  44. data/lib/kind/type_checkers/core/string.rb +17 -0
  45. data/lib/kind/type_checkers/core/struct.rb +13 -0
  46. data/lib/kind/type_checkers/core/symbol.rb +13 -0
  47. data/lib/kind/type_checkers/core/time.rb +13 -0
  48. data/lib/kind/type_checkers/custom/boolean.rb +19 -0
  49. data/lib/kind/type_checkers/custom/callable.rb +19 -0
  50. data/lib/kind/type_checkers/custom/lambda.rb +19 -0
  51. data/lib/kind/type_checkers/stdlib/open_struct.rb +13 -0
  52. data/lib/kind/type_checkers/stdlib/set.rb +17 -0
  53. data/lib/kind/undefined.rb +4 -2
  54. data/lib/kind/validator.rb +1 -1
  55. data/lib/kind/version.rb +1 -1
  56. data/test.sh +4 -4
  57. metadata +45 -9
  58. data/lib/kind/checker.rb +0 -15
  59. data/lib/kind/checker/factory.rb +0 -35
  60. data/lib/kind/checker/protocol.rb +0 -73
  61. data/lib/kind/is.rb +0 -19
  62. data/lib/kind/of.rb +0 -11
  63. data/lib/kind/types.rb +0 -115
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ UNDEFINED = Object.new.freeze
5
+
6
+ private_constant :UNDEFINED
7
+ end
data/lib/kind/dig.rb ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Dig
5
+ extend self
6
+
7
+ def call(data, keys)
8
+ return unless keys.kind_of?(::Array)
9
+
10
+ keys.reduce(data) do |memo, key|
11
+ value = get(memo, key)
12
+
13
+ break if KIND.null?(value)
14
+
15
+ value
16
+ end
17
+ end
18
+
19
+ def [](*keys)
20
+ ->(data) { call(data, keys) }
21
+ end
22
+
23
+ private
24
+
25
+ def get(data, key)
26
+ return data[key] if ::Hash === data
27
+
28
+ case data
29
+ when ::Array
30
+ data[key] if key.respond_to?(:to_int)
31
+ when ::OpenStruct
32
+ data[key] if key.respond_to?(:to_sym)
33
+ when ::Struct
34
+ data[key] rescue nil if key.respond_to?(:to_int) || key.respond_to?(:to_sym)
35
+ else
36
+ data.public_send(key) if key.respond_to?(:to_sym) && data.respond_to?(key)
37
+ end
38
+ end
39
+ end
40
+ end
data/lib/kind/empty.rb CHANGED
@@ -1,21 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
-
5
3
  module Kind
6
4
  module Empty
7
5
  SET = ::Set.new.freeze
8
-
9
6
  HASH = {}.freeze
7
+ ARRAY = [].freeze
8
+ STRING = ''.freeze
10
9
 
11
- ARY = [].freeze
12
- ARRAY = ARY
13
-
14
- STR = ''.freeze
15
- STRING = STR
10
+ ARY = ARRAY
11
+ STR = STRING
16
12
  end
17
13
  end
18
-
19
- unless defined?(Empty)
20
- Empty = Kind::Empty
21
- end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(Empty)
4
+ raise LoadError, "already initialized constant Empty"
5
+ else
6
+ Empty = Kind::Empty
7
+ end
data/lib/kind/error.rb CHANGED
@@ -2,12 +2,8 @@
2
2
 
3
3
  module Kind
4
4
  class Error < TypeError
5
- UNDEFINED_OBJECT = Object.new
6
-
7
- private_constant :UNDEFINED_OBJECT
8
-
9
- def initialize(arg, object = UNDEFINED_OBJECT)
10
- if UNDEFINED_OBJECT == object
5
+ def initialize(arg, object = UNDEFINED)
6
+ if UNDEFINED == object
11
7
  # Will be used when the exception was raised with a message. e.g:
12
8
  # raise Kind::Error, "some message"
13
9
  super(arg)
data/lib/kind/maybe.rb CHANGED
@@ -1,164 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind/dig'
4
+
3
5
  module Kind
4
6
  module Maybe
5
- class Typed
6
- def initialize(kind)
7
- @kind_checker = Kind::Checker::Factory.create(kind)
8
- end
9
-
10
- def wrap(value)
11
- @kind_checker.as_maybe(value)
12
- end
13
-
14
- alias_method :new, :wrap
15
- alias_method :[], :wrap
16
- end
17
-
18
- module Value
19
- def self.none?(value)
20
- value.nil? || Undefined == value
21
- end
22
-
23
- def self.some?(value)
24
- !none?(value)
25
- end
26
- end
27
-
28
- class Result
29
- attr_reader :value
30
-
31
- def initialize(value)
32
- @value = value.kind_of?(Result) ? value.value : value
33
- end
34
-
35
- def value_or(method_name = Undefined, &block)
36
- raise NotImplementedError
37
- end
38
-
39
- def none?
40
- raise NotImplementedError
41
- end
42
-
43
- def some?; !none?; end
44
-
45
- def map(&fn)
46
- raise NotImplementedError
47
- end
7
+ extend self
48
8
 
49
- def try(method_name = Undefined, &block)
50
- raise NotImplementedError
51
- end
52
- end
53
-
54
- class None < Result
55
- INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
56
-
57
- def value_or(default = Undefined, &block)
58
- raise ArgumentError, INVALID_DEFAULT_ARG if Undefined == default && !block
59
-
60
- Undefined != default ? default : block.call
61
- end
62
-
63
- def none?; true; end
64
-
65
- def map(&fn)
66
- self
67
- end
68
-
69
- alias_method :then, :map
9
+ require 'kind/maybe/result'
10
+ require 'kind/maybe/none'
11
+ require 'kind/maybe/some'
12
+ require 'kind/maybe/wrappable'
13
+ require 'kind/maybe/typed'
70
14
 
71
- def try!(method_name = Undefined, &block)
72
- Kind.of.Symbol(method_name) if Undefined != method_name
73
-
74
- NONE_WITH_NIL_VALUE
75
- end
76
-
77
- alias_method :try, :try!
78
-
79
- private_constant :INVALID_DEFAULT_ARG
15
+ def new(value)
16
+ (KIND.null?(value) ? None : Some)
17
+ .new(value)
80
18
  end
81
19
 
82
- NONE_WITH_NIL_VALUE = None.new(nil)
83
- NONE_WITH_UNDEFINED_VALUE = None.new(Undefined)
84
-
85
- private_constant :NONE_WITH_NIL_VALUE, :NONE_WITH_UNDEFINED_VALUE
86
-
87
- class Some < Result
88
- def value_or(default = Undefined, &block)
89
- @value
90
- end
91
-
92
- def none?; false; end
20
+ alias_method :[], :new
93
21
 
94
- def map(&fn)
95
- result = fn.call(@value)
96
-
97
- resolve(result)
98
- end
99
-
100
- alias_method :then, :map
101
-
102
- def try!(method_name = Undefined, *args, &block)
103
- Kind::Of::Symbol(method_name) if Undefined != method_name
104
-
105
- __try__(method_name, args, block)
106
- end
107
-
108
- def try(method_name = Undefined, *args, &block)
109
- if (Undefined != method_name && value.respond_to?(Kind::Of::Symbol(method_name))) ||
110
- (Undefined == method_name && block)
111
- __try__(method_name, args, block)
112
- else
113
- NONE_WITH_NIL_VALUE
114
- end
115
- end
116
-
117
- private
118
-
119
- def __try__(method_name = Undefined, args, block)
120
- fn = Undefined == method_name ? block : method_name.to_proc
121
-
122
- result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
123
-
124
- resolve(result)
125
- end
126
-
127
- def resolve(result)
128
- return result if Maybe::None === result
129
- return NONE_WITH_NIL_VALUE if result.nil?
130
- return NONE_WITH_UNDEFINED_VALUE if Undefined == result
131
-
132
- Some.new(result)
133
- end
134
- end
135
-
136
- def self.new(value)
137
- result_type = Maybe::Value.none?(value) ? None : Some
138
- result_type.new(value)
139
- end
140
-
141
- def self.[](value)
142
- new(value)
143
- end
144
-
145
- def self.wrap(value)
146
- new(value)
147
- end
148
-
149
- def self.none
150
- NONE_WITH_NIL_VALUE
151
- end
152
-
153
- VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
154
-
155
- private_constant :VALUE_CANT_BE_NONE
156
-
157
- def self.some(value)
158
- return Maybe::Some.new(value) if Value.some?(value)
159
-
160
- raise ArgumentError, VALUE_CANT_BE_NONE
161
- end
22
+ extend Wrappable
162
23
  end
163
24
 
164
25
  Optional = Maybe
@@ -166,7 +27,7 @@ module Kind
166
27
  None = Maybe.none
167
28
 
168
29
  def self.None
169
- Kind::None
30
+ None
170
31
  end
171
32
 
172
33
  def self.Some(value)
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ class None < Result
6
+ INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
7
+
8
+ def value_or(default = UNDEFINED, &block)
9
+ raise ArgumentError, INVALID_DEFAULT_ARG if UNDEFINED == default && !block
10
+
11
+ UNDEFINED != default ? default : block.call
12
+ end
13
+
14
+ def none?; true; end
15
+
16
+ def map(&fn)
17
+ self
18
+ end
19
+
20
+ alias_method :map!, :map
21
+ alias_method :then, :map
22
+ alias_method :then!, :map
23
+ alias_method :check, :map
24
+
25
+ def try!(method_name = UNDEFINED, *args, &block)
26
+ KIND.of!(::Symbol, method_name)if UNDEFINED != method_name
27
+
28
+ self
29
+ end
30
+
31
+ alias_method :try, :try!
32
+
33
+ def dig(*keys)
34
+ self
35
+ end
36
+
37
+ def presence
38
+ self
39
+ end
40
+
41
+ private_constant :INVALID_DEFAULT_ARG
42
+ end
43
+
44
+ NONE_WITH_NIL_VALUE = None.new(nil)
45
+ NONE_WITH_UNDEFINED_VALUE = None.new(Undefined)
46
+
47
+ def none
48
+ NONE_WITH_NIL_VALUE
49
+ end
50
+
51
+ def __none__(value) # :nodoc:
52
+ None.new(value)
53
+ end
54
+
55
+ private_constant :NONE_WITH_NIL_VALUE, :NONE_WITH_UNDEFINED_VALUE
56
+ end
57
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ class Result
6
+ attr_reader :value
7
+
8
+ Value = ->(arg) { arg.kind_of?(Maybe::Result) ? arg.value : arg } # :nodoc:
9
+
10
+ def initialize(arg)
11
+ @value = Value.(arg)
12
+ end
13
+
14
+ def value_or(method_name = UNDEFINED, &block)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def none?
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def some?; !none?; end
23
+
24
+ def map(&fn)
25
+ raise NotImplementedError
26
+ end
27
+
28
+ alias_method :map!, :map
29
+ alias_method :then, :map
30
+ alias_method :then!, :map
31
+
32
+ def check(&fn)
33
+ raise NotImplementedError
34
+ end
35
+
36
+ def try(method_name = UNDEFINED, &block)
37
+ raise NotImplementedError
38
+ end
39
+
40
+ alias_method :try!, :try
41
+
42
+ def dig(*keys)
43
+ raise NotImplementedError
44
+ end
45
+
46
+ def presence
47
+ raise NotImplementedError
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ class Some < Result
6
+ def value_or(default = UNDEFINED, &block)
7
+ @value
8
+ end
9
+
10
+ def none?; false; end
11
+
12
+ def map(&fn)
13
+ map!(&fn)
14
+ rescue StandardError => exception
15
+ None.new(exception)
16
+ end
17
+
18
+ alias_method :then, :map
19
+
20
+ def check(&fn)
21
+ result = fn.call(@value)
22
+
23
+ !result || KIND.null?(result) ? NONE_WITH_NIL_VALUE : self
24
+ end
25
+
26
+ def map!(&fn)
27
+ result = fn.call(@value)
28
+
29
+ resolve(result)
30
+ end
31
+
32
+ alias_method :then!, :map!
33
+
34
+ def try!(method_name = UNDEFINED, *args, &block)
35
+ return __try_block__(block, args) if block
36
+
37
+ return __try_method__(method_name, args) if UNDEFINED != method_name
38
+
39
+ raise ArgumentError, 'method name or a block must be present'
40
+ end
41
+
42
+ def try(method_name = UNDEFINED, *args, &block)
43
+ return __try_block__(block, args) if block
44
+
45
+ return __try_method__(method_name, args) if value.respond_to?(method_name)
46
+
47
+ NONE_WITH_NIL_VALUE
48
+ end
49
+
50
+ def dig(*keys)
51
+ resolve(Dig.(value, keys))
52
+ end
53
+
54
+ def presence
55
+ resolve(Presence.(value))
56
+ end
57
+
58
+ private
59
+
60
+ def __try_method__(method_name, args)
61
+ __try_block__(KIND.of!(::Symbol, method_name).to_proc, args)
62
+ end
63
+
64
+ def __try_block__(block, args)
65
+ result = args.empty? ? block.call(value) : block.call(*args.unshift(value))
66
+
67
+ resolve(result)
68
+ end
69
+
70
+ def resolve(result)
71
+ return result if Maybe::None === result
72
+ return None.new(result) if Exception === result
73
+ return NONE_WITH_NIL_VALUE if result.nil?
74
+ return NONE_WITH_UNDEFINED_VALUE if Undefined == result
75
+
76
+ Some.new(result)
77
+ end
78
+ end
79
+
80
+ VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
81
+
82
+ def some(value)
83
+ return Maybe::Some.new(value) if !KIND.null?(value)
84
+
85
+ raise ArgumentError, VALUE_CANT_BE_NONE
86
+ end
87
+
88
+ private_constant :VALUE_CANT_BE_NONE
89
+ end
90
+ end