kind 5.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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -1
  3. data/.travis.sh +35 -10
  4. data/CHANGELOG.md +364 -26
  5. data/README.md +37 -36
  6. data/lib/kind.rb +2 -49
  7. data/lib/kind/__lib__/attributes.rb +66 -0
  8. data/lib/kind/__lib__/kind.rb +71 -0
  9. data/lib/kind/__lib__/undefined.rb +14 -0
  10. data/lib/kind/action.rb +92 -0
  11. data/lib/kind/active_model/validation.rb +1 -1
  12. data/lib/kind/basic.rb +73 -0
  13. data/lib/kind/basic/error.rb +29 -0
  14. data/lib/kind/{core → basic}/undefined.rb +6 -1
  15. data/lib/kind/{core/dig.rb → dig.rb} +20 -4
  16. data/lib/kind/either.rb +30 -0
  17. data/lib/kind/either/left.rb +29 -0
  18. data/lib/kind/either/methods.rb +17 -0
  19. data/lib/kind/either/monad.rb +65 -0
  20. data/lib/kind/either/monad/wrapper.rb +19 -0
  21. data/lib/kind/either/right.rb +38 -0
  22. data/lib/kind/{core/empty.rb → empty.rb} +2 -0
  23. data/lib/kind/{core/empty → empty}/constant.rb +0 -0
  24. data/lib/kind/enum.rb +63 -0
  25. data/lib/kind/enum/item.rb +40 -0
  26. data/lib/kind/enum/methods.rb +72 -0
  27. data/lib/kind/function.rb +47 -0
  28. data/lib/kind/functional.rb +89 -0
  29. data/lib/kind/functional/action.rb +89 -0
  30. data/lib/kind/immutable_attributes.rb +34 -0
  31. data/lib/kind/immutable_attributes/initializer.rb +70 -0
  32. data/lib/kind/immutable_attributes/reader.rb +38 -0
  33. data/lib/kind/maybe.rb +69 -0
  34. data/lib/kind/maybe/methods.rb +21 -0
  35. data/lib/kind/maybe/monad.rb +82 -0
  36. data/lib/kind/maybe/monad/wrapper.rb +19 -0
  37. data/lib/kind/{core/maybe → maybe}/none.rb +11 -18
  38. data/lib/kind/maybe/some.rb +132 -0
  39. data/lib/kind/maybe/typed.rb +35 -0
  40. data/lib/kind/{core/maybe/wrappable.rb → maybe/wrapper.rb} +8 -4
  41. data/lib/kind/monad.rb +22 -0
  42. data/lib/kind/monads.rb +5 -0
  43. data/lib/kind/objects.rb +17 -0
  44. data/lib/kind/objects/basic_object.rb +45 -0
  45. data/lib/kind/objects/modules.rb +32 -0
  46. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/array.rb +3 -1
  47. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/class.rb +1 -1
  48. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/comparable.rb +1 -1
  49. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/enumerable.rb +1 -1
  50. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/enumerator.rb +1 -1
  51. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/file.rb +1 -1
  52. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/float.rb +1 -1
  53. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/hash.rb +3 -1
  54. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/integer.rb +1 -1
  55. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/io.rb +1 -1
  56. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/method.rb +1 -1
  57. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/module.rb +1 -1
  58. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/numeric.rb +1 -1
  59. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/proc.rb +1 -1
  60. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/queue.rb +1 -1
  61. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/range.rb +1 -1
  62. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/regexp.rb +1 -1
  63. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/string.rb +3 -1
  64. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/struct.rb +1 -1
  65. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/symbol.rb +1 -1
  66. data/lib/kind/{core/type_checkers/ruby → objects/modules/core}/time.rb +1 -1
  67. data/lib/kind/{core/type_checkers → objects/modules}/custom/boolean.rb +2 -2
  68. data/lib/kind/{core/type_checkers → objects/modules}/custom/callable.rb +1 -1
  69. data/lib/kind/{core/type_checkers → objects/modules}/custom/lambda.rb +1 -1
  70. data/lib/kind/{core/type_checkers → objects/modules}/stdlib/open_struct.rb +3 -1
  71. data/lib/kind/{core/type_checkers → objects/modules}/stdlib/set.rb +3 -1
  72. data/lib/kind/objects/nil.rb +17 -0
  73. data/lib/kind/objects/not_nil.rb +13 -0
  74. data/lib/kind/objects/object.rb +56 -0
  75. data/lib/kind/objects/respond_to.rb +30 -0
  76. data/lib/kind/objects/union_type.rb +44 -0
  77. data/lib/kind/{core/presence.rb → presence.rb} +4 -2
  78. data/lib/kind/result.rb +31 -0
  79. data/lib/kind/result/abstract.rb +53 -0
  80. data/lib/kind/result/failure.rb +31 -0
  81. data/lib/kind/result/methods.rb +17 -0
  82. data/lib/kind/result/monad.rb +69 -0
  83. data/lib/kind/result/monad/wrapper.rb +19 -0
  84. data/lib/kind/result/success.rb +40 -0
  85. data/lib/kind/try.rb +46 -0
  86. data/lib/kind/validator.rb +14 -10
  87. data/lib/kind/version.rb +1 -1
  88. metadata +81 -47
  89. data/lib/kind/core.rb +0 -15
  90. data/lib/kind/core/error.rb +0 -15
  91. data/lib/kind/core/maybe.rb +0 -42
  92. data/lib/kind/core/maybe/result.rb +0 -51
  93. data/lib/kind/core/maybe/some.rb +0 -90
  94. data/lib/kind/core/maybe/typed.rb +0 -29
  95. data/lib/kind/core/try.rb +0 -34
  96. data/lib/kind/core/type_checker.rb +0 -87
  97. data/lib/kind/core/type_checkers.rb +0 -30
  98. data/lib/kind/core/utils/kind.rb +0 -61
  99. data/lib/kind/core/utils/undefined.rb +0 -7
@@ -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 ADDED
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+
5
+ module Kind
6
+ module Maybe
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)
19
+ end
20
+
21
+ alias_method :[], :new
22
+
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
28
+
29
+ __maybe
30
+ end
31
+
32
+ alias_method :optional, :maybe
33
+
34
+ private
35
+
36
+ def __maybe
37
+ @__maybe ||= Maybe::Typed[self]
38
+ end
39
+ end
40
+
41
+ extend Wrapper
42
+ end
43
+
44
+ Optional = Maybe
45
+
46
+ None = Maybe::NONE_INSTANCE
47
+
48
+ def self.None
49
+ Maybe::NONE_INSTANCE
50
+ end
51
+
52
+ def self.Some(value)
53
+ Maybe::Some[value]
54
+ end
55
+
56
+ def self.Maybe(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]
61
+ end
62
+
63
+ def self.Optional(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]
68
+ end
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
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ require 'kind/monad'
5
+
6
+ class Maybe::Monad::Wrapper < Kind::Monad::Wrapper
7
+ def none(matcher = UNDEFINED)
8
+ return if @monad.some? || output?
9
+
10
+ @output = yield(@monad.value) if @monad.maybe?(matcher)
11
+ end
12
+
13
+ def some(matcher = UNDEFINED)
14
+ return if @monad.none? || output?
15
+
16
+ @output = yield(@monad.value) if @monad.maybe?(matcher)
17
+ end
18
+ end
19
+ end
@@ -2,18 +2,16 @@
2
2
 
3
3
  module Kind
4
4
  module Maybe
5
- class None < Result
6
- INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
7
-
5
+ class None < Monad
8
6
  def value_or(default = UNDEFINED, &block)
9
- raise ArgumentError, INVALID_DEFAULT_ARG if UNDEFINED == default && !block
7
+ Error.invalid_default_arg! if UNDEFINED == default && !block
10
8
 
11
9
  UNDEFINED != default ? default : block.call
12
10
  end
13
11
 
14
12
  def none?; true; end
15
13
 
16
- def map(&fn)
14
+ def map(_method_name = UNDEFINED, &fn)
17
15
  self
18
16
  end
19
17
 
@@ -21,6 +19,10 @@ module Kind
21
19
  alias_method :then, :map
22
20
  alias_method :then!, :map
23
21
  alias_method :check, :map
22
+ alias_method :accept, :map
23
+ alias_method :reject, :map
24
+ alias_method :and_then, :map
25
+ alias_method :and_then!, :map!
24
26
 
25
27
  def try!(method_name = UNDEFINED, *args, &block)
26
28
  KIND.of!(::Symbol, method_name)if UNDEFINED != method_name
@@ -38,20 +40,11 @@ module Kind
38
40
  self
39
41
  end
40
42
 
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 self.none
48
- NONE_WITH_NIL_VALUE
49
- end
50
-
51
- def self.__none__(value) # :nodoc:
52
- None.new(value)
43
+ def inspect
44
+ '#<%s value=%s>' % ['Kind::None', value.inspect]
45
+ end
53
46
  end
54
47
 
55
- private_constant :NONE_WITH_NIL_VALUE, :NONE_WITH_UNDEFINED_VALUE
48
+ NONE_INSTANCE = None.new(nil)
56
49
  end
57
50
  end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/dig'
4
+ require 'kind/presence'
5
+
6
+ module Kind
7
+ module Maybe
8
+ class Some < Monad
9
+ KindSymbol = ->(value) { KIND.of!(::Symbol, value) }
10
+
11
+ VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
12
+
13
+ def self.[](value)
14
+ return new(value) if !KIND.null?(value)
15
+
16
+ raise ArgumentError, VALUE_CANT_BE_NONE
17
+ end
18
+
19
+ def value_or(_default = UNDEFINED, &block)
20
+ @value
21
+ end
22
+
23
+ def none?; false; end
24
+
25
+ def map(method_name = UNDEFINED, &fn)
26
+ map!(method_name, &fn)
27
+ rescue StandardError => exception
28
+ None.new(exception)
29
+ end
30
+
31
+ alias_method :then, :map
32
+ alias_method :and_then, :map
33
+
34
+ def map!(method_name = UNDEFINED, &fn)
35
+ result = if UNDEFINED != method_name
36
+ return NONE_INSTANCE unless @value.respond_to?(KindSymbol[method_name])
37
+
38
+ @value.public_send(method_name)
39
+ else
40
+ fn.call(@value)
41
+ end
42
+
43
+ resolve(result)
44
+ end
45
+
46
+ alias_method :then!, :map!
47
+ alias_method :and_then!, :map!
48
+
49
+ def check(method_name = UNDEFINED, &fn)
50
+ result = if UNDEFINED != method_name
51
+ if method_name.kind_of?(::Symbol)
52
+ @value.respond_to?(method_name) ? @value.public_send(method_name) : NONE_INSTANCE
53
+ else
54
+ method_name === @value
55
+ end
56
+ else
57
+ fn.call(@value)
58
+ end
59
+
60
+ !result || KIND.null?(result) ? NONE_INSTANCE : self
61
+ end
62
+
63
+ alias_method :accept, :check
64
+
65
+ def reject(method_name = UNDEFINED, &fn)
66
+ result = if UNDEFINED != method_name
67
+ if method_name.kind_of?(::Symbol)
68
+ @value.respond_to?(method_name) ? @value.public_send(method_name) : NONE_INSTANCE
69
+ else
70
+ method_name === @value
71
+ end
72
+ else
73
+ fn.call(@value)
74
+ end
75
+
76
+ result || KIND.null?(result) ? NONE_INSTANCE : self
77
+ end
78
+
79
+ def try!(method_name = UNDEFINED, *args, &block)
80
+ return __try_block__(block, args) if block
81
+
82
+ return __try_method__(method_name, args) if UNDEFINED != method_name
83
+
84
+ raise ArgumentError, 'method name or a block must be present'
85
+ end
86
+
87
+ def try(method_name = UNDEFINED, *args, &block)
88
+ return __try_block__(block, args) if block
89
+
90
+ return __try_method__(method_name, args) if value.respond_to?(method_name)
91
+
92
+ NONE_INSTANCE
93
+ rescue TypeError
94
+ NONE_INSTANCE
95
+ end
96
+
97
+ def dig(*keys)
98
+ resolve(Dig.call!(value, keys))
99
+ end
100
+
101
+ def presence
102
+ resolve(Presence.(value))
103
+ end
104
+
105
+ def inspect
106
+ '#<%s value=%s>' % ['Kind::Some', value.inspect]
107
+ end
108
+
109
+ private
110
+
111
+ def __try_method__(method_name, args)
112
+ __try_block__(KindSymbol[method_name].to_proc, args)
113
+ end
114
+
115
+ def __try_block__(block, args)
116
+ result = args.empty? ? block.call(value) : block.call(*args.unshift(value))
117
+
118
+ resolve(result)
119
+ end
120
+
121
+ def resolve(result)
122
+ return result if Maybe::None === result
123
+ return NONE_INSTANCE if KIND.null?(result)
124
+ return None.new(result) if ::Exception === result
125
+
126
+ Some.new(result)
127
+ end
128
+
129
+ private_constant :KindSymbol, :VALUE_CANT_BE_NONE
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ class Typed
6
+ include Wrapper
7
+
8
+ singleton_class.send(:alias_method, :[], :new)
9
+
10
+ def initialize(kind)
11
+ @kind = kind
12
+ end
13
+
14
+ def new(arg)
15
+ value = Monad::Value[arg]
16
+
17
+ @kind === value ? Maybe::Some[value] : Maybe::NONE_INSTANCE
18
+ end
19
+
20
+ alias_method :[], :new
21
+
22
+ def inspect
23
+ "Kind::Maybe<#{@kind}>"
24
+ end
25
+
26
+ private
27
+
28
+ def __call_before_expose_the_arg_in_a_block(arg)
29
+ value = Monad::Value[arg]
30
+
31
+ @kind === value ? value : Maybe::NONE_INSTANCE
32
+ end
33
+ end
34
+ end
35
+ end