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
@@ -2,13 +2,15 @@
2
2
 
3
3
  module Kind
4
4
  module Set
5
- extend self, TypeChecker
5
+ extend self, ::Kind::Object
6
6
 
7
7
  def kind; ::Set; end
8
8
 
9
9
  def value_or_empty(arg)
10
10
  KIND.value(self, arg, Empty::SET)
11
11
  end
12
+
13
+ alias empty_or value_or_empty
12
14
  end
13
15
 
14
16
  def self.Set?(*values)
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Nil
5
+ def self.|(another_kind)
6
+ UnionType[self] | another_kind
7
+ end
8
+
9
+ def self.name
10
+ 'nil'
11
+ end
12
+
13
+ def self.===(value)
14
+ value.nil?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module NotNil
5
+ def self.[](value, label: nil)
6
+ return value unless value.nil?
7
+
8
+ label_text = label ? "#{label}: " : ''
9
+
10
+ raise Error.new("#{label_text}expected to not be nil")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/maybe'
4
+
5
+ module Kind
6
+ module Object
7
+ include Kind::BasicObject
8
+ include Maybe::Buildable
9
+ include UnionType::Buildable
10
+
11
+ def name
12
+ kind.name
13
+ end
14
+
15
+ def ===(value)
16
+ kind === value
17
+ end
18
+
19
+ def inspect
20
+ "Kind::Object<#{name}>"
21
+ end
22
+ end
23
+
24
+ class Object::Instance # :nodoc: all
25
+ include ::Kind::Object
26
+
27
+ ResolveKindName = ->(kind, opt) do
28
+ name = Try.call!(opt, :[], :name)
29
+ name || Try.call!(kind, :name)
30
+ end
31
+
32
+ attr_reader :kind, :name
33
+
34
+ def initialize(kind, opt)
35
+ name = ResolveKindName.(kind, opt)
36
+
37
+ @name = KIND.of!(::String, name)
38
+ @kind = KIND.respond_to!(:===, kind)
39
+ end
40
+
41
+ private_constant :ResolveKindName
42
+ end
43
+
44
+ # Kind[]
45
+ def self.[](kind, opt = Empty::HASH)
46
+ Object::Instance.new(kind, opt)
47
+ end
48
+
49
+ # Kind::Of()
50
+ def self.Of(*args)
51
+ warn '[DEPRECATION] Kind::Of() is deprecated; use Kind[] instead. ' \
52
+ 'It will be removed on next major release.'
53
+
54
+ self[*args]
55
+ end
56
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class RespondTo
5
+ include Kind::BasicObject
6
+
7
+ def self.[](*args)
8
+ args.each { |arg| KIND.of!(::Symbol, arg) }
9
+
10
+ new(args)
11
+ end
12
+
13
+ private_class_method :new
14
+
15
+ attr_reader :inspect
16
+
17
+ def initialize(method_names)
18
+ @method_names = method_names
19
+ @inspect = "Kind::RespondTo#{@method_names}"
20
+ end
21
+
22
+ def ===(value)
23
+ KIND.interface?(@method_names, value)
24
+ end
25
+
26
+ alias_method :call, :===
27
+
28
+ alias_method :name, :inspect
29
+ end
30
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class UnionType
5
+ include Kind::BasicObject
6
+
7
+ Interface = Kind::RespondTo[:name, :===]
8
+
9
+ singleton_class.send(:alias_method, :[], :new)
10
+
11
+ attr_reader :inspect
12
+
13
+ def initialize(kind)
14
+ @kinds = Array(kind)
15
+ @inspect = "(#{@kinds.map(&:name).join(' | ')})"
16
+ end
17
+
18
+ def |(kind)
19
+ self.class.new(@kinds + [Interface[kind]])
20
+ end
21
+
22
+ def ===(value)
23
+ @kinds.any? { |kind| kind === value }
24
+ end
25
+
26
+ alias_method :name, :inspect
27
+
28
+ module Buildable
29
+ def |(another_kind)
30
+ __union_type | another_kind
31
+ end
32
+
33
+ private
34
+
35
+ def __union_type
36
+ @__union_type ||= UnionType[self]
37
+ end
38
+ end
39
+
40
+ private_constant :Interface
41
+ end
42
+
43
+ RespondTo.send(:include, UnionType::Buildable)
44
+ end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind/basic'
4
+
3
5
  module Kind
4
6
  module Presence
5
7
  extend self
6
8
 
7
9
  def call(object)
8
- return object.blank? ? nil : object if object.respond_to?(:blank?)
10
+ return if KIND.null?(object)
9
11
 
10
- return object if TrueClass === object
12
+ return object.blank? ? nil : object if object.respond_to?(:blank?)
11
13
 
12
14
  return blank_str?(object) ? nil : object if String === object
13
15
 
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+
5
+ module Kind
6
+ module Result
7
+ require 'kind/result/abstract'
8
+ require 'kind/result/monad'
9
+ require 'kind/result/failure'
10
+ require 'kind/result/success'
11
+ require 'kind/result/methods'
12
+
13
+ extend self
14
+
15
+ def new(value)
16
+ Success[value]
17
+ end
18
+
19
+ alias_method :[], :new
20
+
21
+ def self.from
22
+ result = yield
23
+
24
+ Result::Monad === result ? result : Result::Success[result]
25
+ rescue StandardError => e
26
+ Result::Failure[:exception, e]
27
+ end
28
+ end
29
+
30
+ extend Result::Methods
31
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Result::Abstract
5
+ def failure?
6
+ false
7
+ end
8
+
9
+ def failed?
10
+ failure?
11
+ end
12
+
13
+ def success?
14
+ false
15
+ end
16
+
17
+ def succeeded?
18
+ success?
19
+ end
20
+
21
+ def on(&block)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def on_success(types = Undefined, matcher = Undefined)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def on_failure(types = Undefined, matcher = Undefined)
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def result?(types, matcher)
34
+ undef_t = Undefined == (t = types)
35
+ undef_m = Undefined == (m = matcher)
36
+
37
+ return true if undef_t && undef_m
38
+
39
+ if !undef_t && undef_m && !(Array === types || Symbol === types)
40
+ m, t = types, matcher
41
+
42
+ undef_m, undef_t = false, true
43
+ end
44
+
45
+ is_type = undef_t || (::Array === t ? t.empty? || t.include?(type) : t == type)
46
+ is_type && (undef_m || m === value)
47
+ end
48
+
49
+ def to_ary
50
+ [type, value]
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Result::Failure < Result::Monad
5
+ DEFAULT_TYPE = :error
6
+
7
+ def failure?
8
+ true
9
+ end
10
+
11
+ def value_or(default = UNDEFINED, &block)
12
+ Error.invalid_default_arg! if UNDEFINED == default && !block
13
+
14
+ UNDEFINED != default ? default : block.call
15
+ end
16
+
17
+ def map(&_)
18
+ self
19
+ end
20
+
21
+ alias_method :map!, :map
22
+ alias_method :then, :map
23
+ alias_method :then!, :map
24
+ alias_method :and_then, :map
25
+ alias_method :and_then!, :map
26
+
27
+ def inspect
28
+ '#<%s type=%p value=%p>' % ['Kind::Failure', type, value]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Result::Methods
5
+ def Failure(arg1 = UNDEFINED, arg2 = UNDEFINED)
6
+ Result::Failure[arg1, arg2]
7
+ end
8
+
9
+ def Success(arg1 = UNDEFINED, arg2 = UNDEFINED)
10
+ Result::Success[arg1, arg2]
11
+ end
12
+
13
+ def self.included(base)
14
+ base.send(:private, :Success, :Failure)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Result::Monad
5
+ include Result::Abstract
6
+
7
+ require 'kind/result/monad/wrapper'
8
+
9
+ attr_reader :type, :value
10
+
11
+ def self.[](arg1 = UNDEFINED, arg2 = UNDEFINED, value_must_be_a: nil) # :nodoc:
12
+ type = UNDEFINED == arg2 ? self::DEFAULT_TYPE : KIND.of!(::Symbol, arg1)
13
+
14
+ Error.wrong_number_of_args!(given: 0, expected: '1 or 2') if UNDEFINED == arg1
15
+
16
+ value = UNDEFINED == arg2 ? arg1 : arg2
17
+
18
+ new(type, (value_must_be_a ? KIND.of!(value_must_be_a, value) : value))
19
+ end
20
+
21
+ private_class_method :new
22
+
23
+ def initialize(type, value)
24
+ @type = type
25
+ @value = value
26
+ end
27
+
28
+ def value_or(_method_name = UNDEFINED, &block)
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def map(&_)
33
+ raise NotImplementedError
34
+ end
35
+
36
+ alias_method :map!, :map
37
+ alias_method :then, :map
38
+ alias_method :then!, :map
39
+ alias_method :and_then, :map
40
+ alias_method :and_then!, :map
41
+
42
+ def on
43
+ monad = Wrapper.new(self)
44
+
45
+ yield(monad)
46
+
47
+ monad.output
48
+ end
49
+
50
+ def on_success(types = Undefined, matcher = Undefined)
51
+ yield(value) if success? && result?(types, matcher)
52
+
53
+ self
54
+ end
55
+
56
+ def on_failure(types = Undefined, matcher = Undefined)
57
+ yield(value) if failure? && result?(types, matcher)
58
+
59
+ self
60
+ end
61
+
62
+ def ===(m)
63
+ return false unless Result::Abstract === m
64
+ return false unless (self.success? && m.success?) || (self.failure? && m.failure?)
65
+
66
+ self.type == m.type && self.value === m.value
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ require 'kind/monad'
5
+
6
+ class Result::Monad::Wrapper < Kind::Monad::Wrapper
7
+ def failure(types = Undefined, matcher = Undefined)
8
+ return if @monad.success? || output?
9
+
10
+ @output = yield(@monad.value) if @monad.result?(types, matcher)
11
+ end
12
+
13
+ def success(types = Undefined, matcher = Undefined)
14
+ return if @monad.failure? || output?
15
+
16
+ @output = yield(@monad.value) if @monad.result?(types, matcher)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Result::Success < Result::Monad
5
+ DEFAULT_TYPE = :ok
6
+
7
+ def success?
8
+ true
9
+ end
10
+
11
+ def value_or(_default = UNDEFINED, &block)
12
+ @value
13
+ end
14
+
15
+ def map(&fn)
16
+ map!(&fn)
17
+ rescue Kind::Monad::Error => e
18
+ raise e
19
+ rescue StandardError => e
20
+ Result::Failure[:exception, e]
21
+ end
22
+
23
+ def map!(&fn)
24
+ monad = fn.call(@value)
25
+
26
+ return monad if Result::Monad === monad
27
+
28
+ raise Kind::Monad::Error.new('Kind::Success | Kind::Failure', monad)
29
+ end
30
+
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 inspect
37
+ '#<%s type=%p value=%p>' % ['Kind::Success', type, value]
38
+ end
39
+ end
40
+ end