kind 3.1.0 → 5.2.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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -0
  3. data/.travis.sh +67 -12
  4. data/.travis.yml +7 -5
  5. data/CHANGELOG.md +1647 -0
  6. data/Gemfile +22 -7
  7. data/README.md +920 -486
  8. data/kind.gemspec +1 -1
  9. data/lib/kind.rb +2 -314
  10. data/lib/kind/__lib__/attributes.rb +66 -0
  11. data/lib/kind/__lib__/kind.rb +71 -0
  12. data/lib/kind/__lib__/undefined.rb +14 -0
  13. data/lib/kind/action.rb +92 -0
  14. data/lib/kind/active_model/validation.rb +3 -4
  15. data/lib/kind/basic.rb +73 -0
  16. data/lib/kind/basic/error.rb +29 -0
  17. data/lib/kind/{undefined.rb → basic/undefined.rb} +8 -1
  18. data/lib/kind/dig.rb +31 -11
  19. data/lib/kind/either.rb +30 -0
  20. data/lib/kind/either/left.rb +29 -0
  21. data/lib/kind/either/methods.rb +17 -0
  22. data/lib/kind/either/monad.rb +65 -0
  23. data/lib/kind/either/monad/wrapper.rb +19 -0
  24. data/lib/kind/either/right.rb +38 -0
  25. data/lib/kind/empty.rb +4 -10
  26. data/lib/kind/empty/constant.rb +7 -0
  27. data/lib/kind/enum.rb +63 -0
  28. data/lib/kind/enum/item.rb +40 -0
  29. data/lib/kind/enum/methods.rb +72 -0
  30. data/lib/kind/function.rb +47 -0
  31. data/lib/kind/functional.rb +89 -0
  32. data/lib/kind/functional/action.rb +89 -0
  33. data/lib/kind/immutable_attributes.rb +34 -0
  34. data/lib/kind/immutable_attributes/initializer.rb +70 -0
  35. data/lib/kind/immutable_attributes/reader.rb +38 -0
  36. data/lib/kind/maybe.rb +35 -159
  37. data/lib/kind/maybe/methods.rb +21 -0
  38. data/lib/kind/maybe/monad.rb +82 -0
  39. data/lib/kind/maybe/monad/wrapper.rb +19 -0
  40. data/lib/kind/maybe/none.rb +50 -0
  41. data/lib/kind/maybe/some.rb +132 -0
  42. data/lib/kind/maybe/typed.rb +35 -0
  43. data/lib/kind/maybe/wrapper.rb +37 -0
  44. data/lib/kind/monad.rb +22 -0
  45. data/lib/kind/monads.rb +5 -0
  46. data/lib/kind/objects.rb +17 -0
  47. data/lib/kind/objects/basic_object.rb +45 -0
  48. data/lib/kind/objects/modules.rb +32 -0
  49. data/lib/kind/objects/modules/core/array.rb +19 -0
  50. data/lib/kind/objects/modules/core/class.rb +13 -0
  51. data/lib/kind/objects/modules/core/comparable.rb +13 -0
  52. data/lib/kind/objects/modules/core/enumerable.rb +13 -0
  53. data/lib/kind/objects/modules/core/enumerator.rb +13 -0
  54. data/lib/kind/objects/modules/core/file.rb +13 -0
  55. data/lib/kind/objects/modules/core/float.rb +13 -0
  56. data/lib/kind/objects/modules/core/hash.rb +19 -0
  57. data/lib/kind/objects/modules/core/integer.rb +13 -0
  58. data/lib/kind/objects/modules/core/io.rb +13 -0
  59. data/lib/kind/objects/modules/core/method.rb +13 -0
  60. data/lib/kind/objects/modules/core/module.rb +17 -0
  61. data/lib/kind/objects/modules/core/numeric.rb +13 -0
  62. data/lib/kind/objects/modules/core/proc.rb +13 -0
  63. data/lib/kind/objects/modules/core/queue.rb +14 -0
  64. data/lib/kind/objects/modules/core/range.rb +13 -0
  65. data/lib/kind/objects/modules/core/regexp.rb +13 -0
  66. data/lib/kind/objects/modules/core/string.rb +19 -0
  67. data/lib/kind/objects/modules/core/struct.rb +13 -0
  68. data/lib/kind/objects/modules/core/symbol.rb +13 -0
  69. data/lib/kind/objects/modules/core/time.rb +13 -0
  70. data/lib/kind/objects/modules/custom/boolean.rb +19 -0
  71. data/lib/kind/objects/modules/custom/callable.rb +19 -0
  72. data/lib/kind/objects/modules/custom/lambda.rb +19 -0
  73. data/lib/kind/objects/modules/stdlib/open_struct.rb +15 -0
  74. data/lib/kind/objects/modules/stdlib/set.rb +19 -0
  75. data/lib/kind/objects/nil.rb +17 -0
  76. data/lib/kind/objects/not_nil.rb +13 -0
  77. data/lib/kind/objects/object.rb +56 -0
  78. data/lib/kind/objects/respond_to.rb +30 -0
  79. data/lib/kind/objects/union_type.rb +44 -0
  80. data/lib/kind/presence.rb +35 -0
  81. data/lib/kind/result.rb +31 -0
  82. data/lib/kind/result/abstract.rb +53 -0
  83. data/lib/kind/result/failure.rb +31 -0
  84. data/lib/kind/result/methods.rb +17 -0
  85. data/lib/kind/result/monad.rb +69 -0
  86. data/lib/kind/result/monad/wrapper.rb +19 -0
  87. data/lib/kind/result/success.rb +40 -0
  88. data/lib/kind/try.rb +46 -0
  89. data/lib/kind/validator.rb +112 -1
  90. data/lib/kind/version.rb +1 -1
  91. data/test.sh +4 -4
  92. metadata +81 -13
  93. data/lib/kind/active_model/kind_validator.rb +0 -96
  94. data/lib/kind/checker.rb +0 -15
  95. data/lib/kind/checker/factory.rb +0 -35
  96. data/lib/kind/checker/protocol.rb +0 -73
  97. data/lib/kind/error.rb +0 -19
  98. data/lib/kind/is.rb +0 -19
  99. data/lib/kind/of.rb +0 -11
  100. data/lib/kind/types.rb +0 -115
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+ require 'kind/__lib__/attributes'
5
+
6
+ module Kind
7
+ module ImmutableAttributes
8
+ require 'kind/immutable_attributes/initializer'
9
+ require 'kind/immutable_attributes/reader'
10
+
11
+ module ClassMethods
12
+ def __attributes__ # :nodoc:
13
+ @__attributes__ ||= {}
14
+ end
15
+
16
+ def attribute(name, kind = nil, default: UNDEFINED, visibility: :public)
17
+ __attributes__[ATTRIBUTES.name!(name)] = ATTRIBUTES.value(kind, default, visibility)
18
+
19
+ attr_reader(name)
20
+
21
+ private(name) if visibility == :private
22
+ protected(name) if visibility == :protected
23
+
24
+ name
25
+ end
26
+ end
27
+
28
+ def self.included(base)
29
+ base.extend(ClassMethods)
30
+ base.send(:include, Reader)
31
+ base.send(:include, Initializer)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/__lib__/attributes'
4
+ require 'kind/basic'
5
+ require 'kind/empty'
6
+
7
+ module Kind
8
+ module ImmutableAttributes
9
+ module Initializer
10
+ def initialize(arg)
11
+ input = __resolve_attribute_input(arg)
12
+
13
+ hash = call_before_initialize_to_prepare_the_input(input)
14
+
15
+ @_nil_attrs = ::Set.new
16
+ @_____attrs = {}
17
+ @attributes = {}
18
+
19
+ self.class.__attributes__.each do |name, (kind, default, visibility)|
20
+ value_to_assign = __resolve_attribute_value_to_assign(kind, default, hash, name)
21
+
22
+ @_nil_attrs << name if value_to_assign.nil?
23
+ @_____attrs[name] = value_to_assign
24
+ @attributes[name] = value_to_assign if visibility == :public
25
+
26
+ instance_variable_set("@#{name}", value_to_assign)
27
+ end
28
+
29
+ @_nil_attrs.freeze
30
+ @attributes.freeze
31
+
32
+ call_after_initialize_and_assign_the_attributes
33
+ end
34
+
35
+ def nil_attributes
36
+ @_nil_attrs.to_a
37
+ end
38
+
39
+ def nil_attributes?(*names)
40
+ names.empty? ? !@_nil_attrs.empty? : names.all? { |name| @_nil_attrs.include?(name) }
41
+ end
42
+
43
+ private
44
+
45
+ def call_before_initialize_to_prepare_the_input(input)
46
+ input
47
+ end
48
+
49
+ def call_after_initialize_and_assign_the_attributes
50
+ end
51
+
52
+ def __resolve_attribute_input(arg)
53
+ return arg.attributes if arg.kind_of?(ImmutableAttributes)
54
+
55
+ arg.kind_of?(::Hash) ? arg : Empty::HASH
56
+ end
57
+
58
+ def __resolve_attribute_value_to_assign(kind, default, hash, name)
59
+ if kind.kind_of?(::Class) && kind < ImmutableAttributes
60
+ kind.new(hash[name])
61
+ elsif kind.kind_of?(::Array) && (nkind = kind[0]).kind_of?(::Class) && nkind < ImmutableAttributes
62
+ Array(hash[name]).map { |item| nkind.new(item) }
63
+ else
64
+ ATTRIBUTES.value_to_assign(kind, default, hash, name)
65
+ end
66
+ end
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+ require 'kind/__lib__/attributes'
5
+
6
+ module Kind
7
+ module ImmutableAttributes
8
+
9
+ module Reader
10
+ def self.included(base)
11
+ base.send(:attr_reader, :attributes)
12
+ end
13
+
14
+ def attribute?(name)
15
+ self.class.__attributes__.key?(name.to_sym)
16
+ end
17
+
18
+ def attribute(name)
19
+ @attributes[name.to_sym]
20
+ end
21
+
22
+ def attribute!(name)
23
+ @attributes.fetch(name.to_sym)
24
+ end
25
+
26
+ def with_attribute(name, value)
27
+ self.class.new(@_____attrs.merge(name.to_sym => value))
28
+ end
29
+
30
+ def with_attributes(arg)
31
+ hash = KIND.of!(::Hash, arg)
32
+
33
+ self.class.new(@_____attrs.merge(hash))
34
+ end
35
+ end
36
+
37
+ end
38
+ end
data/lib/kind/maybe.rb CHANGED
@@ -1,193 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'kind/dig'
3
+ require 'kind/basic'
4
4
 
5
5
  module Kind
6
6
  module Maybe
7
- class Typed
8
- def initialize(kind)
9
- @kind_checker = Kind::Checker::Factory.create(kind)
10
- end
11
-
12
- def wrap(value)
13
- @kind_checker.as_maybe(value)
14
- end
15
-
16
- alias_method :new, :wrap
17
- alias_method :[], :wrap
18
- end
19
-
20
- module Value
21
- def self.none?(value)
22
- value.nil? || Undefined == value
23
- end
24
-
25
- def self.some?(value)
26
- !none?(value)
27
- end
7
+ require 'kind/maybe/monad'
8
+ require 'kind/maybe/none'
9
+ require 'kind/maybe/some'
10
+ require 'kind/maybe/wrapper'
11
+ require 'kind/maybe/typed'
12
+ require 'kind/maybe/methods'
13
+
14
+ extend self
15
+
16
+ def new(value)
17
+ (::Exception === value || KIND.null?(value) ? None : Some)
18
+ .new(value)
28
19
  end
29
20
 
30
- class Result
31
- attr_reader :value
21
+ alias_method :[], :new
32
22
 
33
- def initialize(value)
34
- @value = value.kind_of?(Result) ? value.value : value
35
- end
36
-
37
- def value_or(method_name = Undefined, &block)
38
- raise NotImplementedError
39
- end
23
+ module Buildable
24
+ def maybe(value = UNDEFINED, &block)
25
+ return __maybe[value] if UNDEFINED != value && !block
26
+ return __maybe.wrap(&block) if UNDEFINED == value && block
27
+ return __maybe.wrap(value, &block) if UNDEFINED != value && block
40
28
 
41
- def none?
42
- raise NotImplementedError
29
+ __maybe
43
30
  end
44
31
 
45
- def some?; !none?; end
46
-
47
- def map(&fn)
48
- raise NotImplementedError
49
- end
50
-
51
- def try(method_name = Undefined, &block)
52
- raise NotImplementedError
53
- end
54
- end
55
-
56
- class None < Result
57
- INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
58
-
59
- def value_or(default = Undefined, &block)
60
- raise ArgumentError, INVALID_DEFAULT_ARG if Undefined == default && !block
61
-
62
- Undefined != default ? default : block.call
63
- end
64
-
65
- def none?; true; end
66
-
67
- def map(&fn)
68
- self
69
- end
70
-
71
- alias_method :then, :map
72
-
73
- def try!(method_name = Undefined, *args, &block)
74
- Kind.of.Symbol(method_name) if Undefined != method_name
75
-
76
- NONE_WITH_NIL_VALUE
77
- end
78
-
79
- alias_method :try, :try!
80
-
81
- def dig(*keys)
82
- NONE_WITH_NIL_VALUE
83
- end
84
-
85
- private_constant :INVALID_DEFAULT_ARG
86
- end
87
-
88
- NONE_WITH_NIL_VALUE = None.new(nil)
89
- NONE_WITH_UNDEFINED_VALUE = None.new(Undefined)
90
-
91
- private_constant :NONE_WITH_NIL_VALUE, :NONE_WITH_UNDEFINED_VALUE
92
-
93
- class Some < Result
94
- def value_or(default = Undefined, &block)
95
- @value
96
- end
97
-
98
- def none?; false; end
99
-
100
- def map(&fn)
101
- result = fn.call(@value)
102
-
103
- resolve(result)
104
- end
105
-
106
- alias_method :then, :map
107
-
108
- def try!(method_name = Undefined, *args, &block)
109
- Kind::Of::Symbol(method_name) if Undefined != method_name
110
-
111
- __try__(method_name, args, block)
112
- end
113
-
114
- def try(method_name = Undefined, *args, &block)
115
- if (Undefined != method_name && value.respond_to?(Kind::Of::Symbol(method_name))) ||
116
- (Undefined == method_name && block)
117
- __try__(method_name, args, block)
118
- else
119
- NONE_WITH_NIL_VALUE
120
- end
121
- end
122
-
123
- def dig(*keys)
124
- resolve(Kind::Dig.call(value, keys))
125
- end
32
+ alias_method :optional, :maybe
126
33
 
127
34
  private
128
35
 
129
- def __try__(method_name = Undefined, args, block)
130
- fn = Undefined == method_name ? block : method_name.to_proc
131
-
132
- result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
133
-
134
- resolve(result)
36
+ def __maybe
37
+ @__maybe ||= Maybe::Typed[self]
135
38
  end
136
-
137
- def resolve(result)
138
- return result if Maybe::None === result
139
- return NONE_WITH_NIL_VALUE if result.nil?
140
- return NONE_WITH_UNDEFINED_VALUE if Undefined == result
141
-
142
- Some.new(result)
143
- end
144
- end
145
-
146
- def self.new(value)
147
- result_type = Maybe::Value.none?(value) ? None : Some
148
- result_type.new(value)
149
- end
150
-
151
- def self.[](value)
152
- new(value)
153
- end
154
-
155
- def self.wrap(value)
156
- new(value)
157
39
  end
158
40
 
159
- def self.none
160
- NONE_WITH_NIL_VALUE
161
- end
162
-
163
- VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
164
-
165
- private_constant :VALUE_CANT_BE_NONE
166
-
167
- def self.some(value)
168
- return Maybe::Some.new(value) if Value.some?(value)
169
-
170
- raise ArgumentError, VALUE_CANT_BE_NONE
171
- end
41
+ extend Wrapper
172
42
  end
173
43
 
174
44
  Optional = Maybe
175
45
 
176
- None = Maybe.none
46
+ None = Maybe::NONE_INSTANCE
177
47
 
178
48
  def self.None
179
- Kind::None
49
+ Maybe::NONE_INSTANCE
180
50
  end
181
51
 
182
52
  def self.Some(value)
183
- Maybe.some(value)
53
+ Maybe::Some[value]
184
54
  end
185
55
 
186
56
  def self.Maybe(kind)
187
- Maybe::Typed.new(kind)
57
+ warn '[DEPRECATION] Kind::Maybe() is deprecated; use Kind::Maybe::Typed[] instead. ' \
58
+ 'It will be removed on next major release.'
59
+
60
+ Maybe::Typed[kind]
188
61
  end
189
62
 
190
63
  def self.Optional(kind)
191
- Maybe::Typed.new(kind)
64
+ warn '[DEPRECATION] Kind::Optional() is deprecated; use Kind::Optional::Typed[] instead. ' \
65
+ 'It will be removed on next major release.'
66
+
67
+ Optional::Typed[kind]
192
68
  end
193
69
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe::Methods
5
+ def Maybe(&block)
6
+ Kind::Maybe.from(&block)
7
+ end
8
+
9
+ def None
10
+ Kind::Maybe::NONE_INSTANCE
11
+ end
12
+
13
+ def Some(value = UNDEFINED, &block)
14
+ UNDEFINED == value && block ? Maybe(&block) : Kind::Maybe[value]
15
+ end
16
+
17
+ def self.included(base)
18
+ base.send(:private, :Some, :None)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ class Monad
6
+ require 'kind/maybe/monad/wrapper'
7
+
8
+ attr_reader :value
9
+
10
+ Value = ->(arg) { arg.kind_of?(Maybe::Monad) ? arg.value : arg } # :nodoc:
11
+
12
+ def initialize(value)
13
+ @value = Value[value]
14
+ end
15
+
16
+ def value_or(_method_name = UNDEFINED, &block)
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def none?
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def some?; !none?; end
25
+
26
+ def map(&fn)
27
+ raise NotImplementedError
28
+ end
29
+
30
+ alias_method :map!, :map
31
+ alias_method :then, :map
32
+ alias_method :then!, :map
33
+ alias_method :and_then, :map
34
+ alias_method :and_then!, :map!
35
+
36
+ def check(&fn)
37
+ raise NotImplementedError
38
+ end
39
+
40
+ alias_method :accept, :check
41
+ alias_method :reject, :check
42
+
43
+ def try(_method_name = UNDEFINED, &block)
44
+ raise NotImplementedError
45
+ end
46
+
47
+ alias_method :try!, :try
48
+
49
+ def dig(*keys)
50
+ raise NotImplementedError
51
+ end
52
+
53
+ def presence
54
+ raise NotImplementedError
55
+ end
56
+
57
+ def on
58
+ monad = Wrapper.new(self)
59
+
60
+ yield(monad)
61
+
62
+ monad.output
63
+ end
64
+
65
+ def on_some(matcher = UNDEFINED)
66
+ yield(value) if some? && maybe?(matcher)
67
+
68
+ self
69
+ end
70
+
71
+ def on_none(matcher = UNDEFINED)
72
+ yield(value) if none? && maybe?(matcher)
73
+
74
+ self
75
+ end
76
+
77
+ def maybe?(matcher)
78
+ UNDEFINED == matcher || matcher === value
79
+ end
80
+ end
81
+ end
82
+ end