kind 4.1.0 → 5.4.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -1
  3. data/.travis.sh +39 -7
  4. data/.travis.yml +1 -2
  5. data/CHANGELOG.md +524 -29
  6. data/Gemfile +13 -6
  7. data/README.md +57 -43
  8. data/kind.gemspec +3 -3
  9. data/lib/kind.rb +2 -84
  10. data/lib/kind/__lib__/action_steps.rb +57 -0
  11. data/lib/kind/__lib__/attributes.rb +66 -0
  12. data/lib/kind/__lib__/kind.rb +51 -0
  13. data/lib/kind/__lib__/of.rb +17 -0
  14. data/lib/kind/__lib__/strict.rb +49 -0
  15. data/lib/kind/__lib__/undefined.rb +14 -0
  16. data/lib/kind/action.rb +127 -0
  17. data/lib/kind/active_model/validation.rb +3 -4
  18. data/lib/kind/basic.rb +79 -0
  19. data/lib/kind/basic/error.rb +29 -0
  20. data/lib/kind/{undefined.rb → basic/undefined.rb} +6 -1
  21. data/lib/kind/dig.rb +21 -5
  22. data/lib/kind/either.rb +30 -0
  23. data/lib/kind/either/left.rb +29 -0
  24. data/lib/kind/either/methods.rb +17 -0
  25. data/lib/kind/either/monad.rb +65 -0
  26. data/lib/kind/either/monad/wrapper.rb +19 -0
  27. data/lib/kind/either/right.rb +38 -0
  28. data/lib/kind/empty.rb +2 -2
  29. data/lib/kind/empty/constant.rb +7 -0
  30. data/lib/kind/enum.rb +63 -0
  31. data/lib/kind/enum/item.rb +40 -0
  32. data/lib/kind/enum/methods.rb +72 -0
  33. data/lib/kind/function.rb +45 -0
  34. data/lib/kind/functional.rb +89 -0
  35. data/lib/kind/functional/action.rb +87 -0
  36. data/lib/kind/functional/steps.rb +22 -0
  37. data/lib/kind/immutable_attributes.rb +34 -0
  38. data/lib/kind/immutable_attributes/initializer.rb +70 -0
  39. data/lib/kind/immutable_attributes/reader.rb +38 -0
  40. data/lib/kind/maybe.rb +37 -12
  41. data/lib/kind/maybe/methods.rb +21 -0
  42. data/lib/kind/maybe/monad.rb +82 -0
  43. data/lib/kind/maybe/monad/wrapper.rb +19 -0
  44. data/lib/kind/maybe/none.rb +12 -19
  45. data/lib/kind/maybe/some.rb +68 -26
  46. data/lib/kind/maybe/typed.rb +11 -5
  47. data/lib/kind/maybe/{wrappable.rb → wrapper.rb} +8 -4
  48. data/lib/kind/monad.rb +22 -0
  49. data/lib/kind/monads.rb +5 -0
  50. data/lib/kind/objects.rb +17 -0
  51. data/lib/kind/objects/basic_object.rb +43 -0
  52. data/lib/kind/objects/modules.rb +32 -0
  53. data/lib/kind/{type_checkers → objects/modules}/core/array.rb +3 -1
  54. data/lib/kind/{type_checkers → objects/modules}/core/class.rb +1 -1
  55. data/lib/kind/{type_checkers → objects/modules}/core/comparable.rb +1 -1
  56. data/lib/kind/{type_checkers → objects/modules}/core/enumerable.rb +1 -1
  57. data/lib/kind/{type_checkers → objects/modules}/core/enumerator.rb +1 -1
  58. data/lib/kind/{type_checkers → objects/modules}/core/file.rb +1 -1
  59. data/lib/kind/{type_checkers → objects/modules}/core/float.rb +1 -1
  60. data/lib/kind/{type_checkers → objects/modules}/core/hash.rb +3 -1
  61. data/lib/kind/{type_checkers → objects/modules}/core/integer.rb +1 -1
  62. data/lib/kind/{type_checkers → objects/modules}/core/io.rb +1 -1
  63. data/lib/kind/{type_checkers → objects/modules}/core/method.rb +1 -1
  64. data/lib/kind/{type_checkers → objects/modules}/core/module.rb +1 -1
  65. data/lib/kind/{type_checkers → objects/modules}/core/numeric.rb +1 -1
  66. data/lib/kind/{type_checkers → objects/modules}/core/proc.rb +1 -1
  67. data/lib/kind/{type_checkers → objects/modules}/core/queue.rb +1 -1
  68. data/lib/kind/{type_checkers → objects/modules}/core/range.rb +1 -1
  69. data/lib/kind/{type_checkers → objects/modules}/core/regexp.rb +1 -1
  70. data/lib/kind/{type_checkers → objects/modules}/core/string.rb +3 -1
  71. data/lib/kind/{type_checkers → objects/modules}/core/struct.rb +1 -1
  72. data/lib/kind/{type_checkers → objects/modules}/core/symbol.rb +1 -1
  73. data/lib/kind/{type_checkers → objects/modules}/core/time.rb +1 -1
  74. data/lib/kind/{type_checkers → objects/modules}/custom/boolean.rb +2 -2
  75. data/lib/kind/{type_checkers → objects/modules}/custom/callable.rb +1 -1
  76. data/lib/kind/{type_checkers → objects/modules}/custom/lambda.rb +1 -1
  77. data/lib/kind/{type_checkers → objects/modules}/stdlib/open_struct.rb +3 -1
  78. data/lib/kind/{type_checkers → objects/modules}/stdlib/set.rb +3 -1
  79. data/lib/kind/objects/nil.rb +17 -0
  80. data/lib/kind/objects/not_nil.rb +9 -0
  81. data/lib/kind/objects/object.rb +56 -0
  82. data/lib/kind/objects/respond_to.rb +30 -0
  83. data/lib/kind/objects/union_type.rb +44 -0
  84. data/lib/kind/presence.rb +4 -2
  85. data/lib/kind/result.rb +31 -0
  86. data/lib/kind/result/abstract.rb +53 -0
  87. data/lib/kind/result/failure.rb +33 -0
  88. data/lib/kind/result/methods.rb +17 -0
  89. data/lib/kind/result/monad.rb +74 -0
  90. data/lib/kind/result/monad/wrapper.rb +19 -0
  91. data/lib/kind/result/success.rb +53 -0
  92. data/lib/kind/strict/disabled.rb +34 -0
  93. data/lib/kind/try.rb +22 -10
  94. data/lib/kind/validator.rb +111 -0
  95. data/lib/kind/version.rb +1 -1
  96. metadata +84 -51
  97. data/lib/kind/active_model/kind_validator.rb +0 -103
  98. data/lib/kind/core.rb +0 -10
  99. data/lib/kind/core/deprecation.rb +0 -29
  100. data/lib/kind/core/kind.rb +0 -61
  101. data/lib/kind/core/undefined.rb +0 -7
  102. data/lib/kind/core/wrong_number_of_args.rb +0 -9
  103. data/lib/kind/deprecations/built_in_type_checkers.rb +0 -23
  104. data/lib/kind/deprecations/checker.rb +0 -16
  105. data/lib/kind/deprecations/checker/factory.rb +0 -31
  106. data/lib/kind/deprecations/checker/protocol.rb +0 -73
  107. data/lib/kind/deprecations/is.rb +0 -35
  108. data/lib/kind/deprecations/of.rb +0 -258
  109. data/lib/kind/deprecations/types.rb +0 -121
  110. data/lib/kind/error.rb +0 -15
  111. data/lib/kind/maybe/result.rb +0 -51
  112. data/lib/kind/type_checker.rb +0 -87
  113. data/lib/kind/type_checkers.rb +0 -30
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+
5
+ module Kind
6
+ module Function
7
+ module Behavior
8
+ def self.extended(base)
9
+ base.send(:alias_method, :[], :call)
10
+ base.send(:alias_method, :===, :call)
11
+ base.send(:alias_method, :yield, :call)
12
+ end
13
+ end
14
+
15
+ def self.included(_)
16
+ raise RuntimeError, "The Kind::Function can't be included, it can be only extended."
17
+ end
18
+
19
+ def self.extended(base)
20
+ base.extend(Kind.of_module(base))
21
+ end
22
+
23
+ def kind_function!
24
+ return self if Kind.is?(Behavior, self)
25
+
26
+ KIND.respond_to!(:call, self).extend(Behavior)
27
+
28
+ if method(:call).parameters.empty?
29
+ raise ArgumentError, "#{self.name}.call must receive at least one argument"
30
+ end
31
+
32
+ self.instance_eval(
33
+ 'def to_proc; @to_proc ||= method(:call).to_proc; end' \
34
+ "\n" \
35
+ 'def curry; @curry ||= to_proc.curry; end'
36
+ )
37
+
38
+ self.to_proc
39
+ self.curry
40
+ self
41
+ end
42
+
43
+ private_constant :Behavior
44
+ end
45
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+ require 'kind/empty'
5
+ require 'kind/__lib__/attributes'
6
+
7
+ module Kind
8
+ module Functional
9
+ def self.extended(_)
10
+ raise RuntimeError, "The Kind::Functional can't be extended, it can be only included."
11
+ end
12
+
13
+ def self.included(base)
14
+ Kind.of_class(base).send(:extend, ClassMethods)
15
+ end
16
+
17
+ module Behavior
18
+ def self.included(base)
19
+ base.send(:alias_method, :[], :call)
20
+ base.send(:alias_method, :===, :call)
21
+ base.send(:alias_method, :yield, :call)
22
+ end
23
+
24
+ def initialize(arg = Empty::HASH)
25
+ hash = STRICT.kind_of(::Hash, arg)
26
+
27
+ self.class.__dependencies__.each do |name, (kind, default, _visibility)|
28
+ value_to_assign = ATTRIBUTES.value_to_assign!(kind, default, hash, name)
29
+
30
+ instance_variable_set("@#{name}", value_to_assign)
31
+ end
32
+ end
33
+ end
34
+
35
+ module DependencyInjection
36
+ def __dependencies__ # :nodoc:
37
+ @__dependencies__ ||= {}
38
+ end
39
+
40
+ def dependency(name, kind, default: UNDEFINED)
41
+ __dependencies__[ATTRIBUTES.name!(name)] = ATTRIBUTES.value!(kind, default)
42
+
43
+ attr_reader(name)
44
+
45
+ private(name)
46
+
47
+ name
48
+ end
49
+ end
50
+
51
+ module ClassMethods
52
+ include DependencyInjection
53
+
54
+ def kind_functional!
55
+ return self if Kind.is?(Behavior, self)
56
+
57
+ public_methods = self.public_instance_methods - ::Object.new.methods
58
+
59
+ unless public_methods.include?(:call)
60
+ raise Kind::Error.new("expected #{self} to implement `#call`")
61
+ end
62
+
63
+ if public_methods.size > 1
64
+ raise Kind::Error.new("#{self} can only have `#call` as its public method")
65
+ end
66
+
67
+ if public_instance_method(:call).parameters.empty?
68
+ raise ArgumentError, "#{self.name}#call must receive at least one argument"
69
+ end
70
+
71
+ self.send(:include, Behavior)
72
+
73
+ def self.inherited(_)
74
+ raise RuntimeError, "#{self.name} is a Kind::Functional and it can't be inherited by anyone"
75
+ end
76
+
77
+ self.class_eval(
78
+ 'def to_proc; @to_proc ||= method(:call).to_proc; end' \
79
+ "\n" \
80
+ 'def curry; @curry ||= to_proc.curry; end'
81
+ )
82
+
83
+ __dependencies__.freeze
84
+
85
+ self
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/functional'
4
+ require 'kind/functional/steps'
5
+
6
+ module Kind
7
+ module Functional::Action
8
+ CALL_TMPL = [
9
+ 'def call(%s)',
10
+ ' result = call!(%s)',
11
+ '',
12
+ ' return result if Kind::Result::Monad === result',
13
+ '',
14
+ ' raise Kind::Error, "#{self.class.name}#call! must return a Kind::Success or Kind::Failure"',
15
+ 'end'
16
+ ].join("\n").freeze
17
+
18
+ module Macros
19
+ def kind_functional_action!
20
+ return self if Kind.is?(Result::Methods, self)
21
+
22
+ public_methods = self.public_instance_methods - ::Object.new.methods
23
+
24
+ unless public_methods.include?(:call!)
25
+ raise Kind::Error.new("expected #{self} to implement `#call!`")
26
+ end
27
+
28
+ if public_methods.size > 1
29
+ raise Kind::Error.new("#{self} can only have `#call!` as its public method")
30
+ end
31
+
32
+ call_parameters = public_instance_method(:call!).parameters
33
+
34
+ if call_parameters.empty?
35
+ raise ArgumentError, "#{self.name}#call! must receive at least one argument"
36
+ end
37
+
38
+ def self.inherited(_)
39
+ raise RuntimeError, "#{self.name} is a Kind::Functional::Action and it can't be inherited"
40
+ end
41
+
42
+ call_parameters.flatten!
43
+
44
+ call_with_args = call_parameters.include?(:req) || call_parameters.include?(:rest)
45
+ call_with_kargs = call_parameters.include?(:keyreq) || call_parameters.include?(:keyrest)
46
+
47
+ call_tmpl_args = '*args, **kargs' if call_with_args && call_with_kargs
48
+ call_tmpl_args = '*args' if call_with_args && !call_with_kargs
49
+ call_tmpl_args = '**kargs' if !call_with_args && call_with_kargs
50
+
51
+ self.class_eval(
52
+ "def to_proc; @to_proc ||= method(:call!).to_proc; end" \
53
+ "\n" \
54
+ "def curry; @curry ||= to_proc.curry; end" \
55
+ "\n" \
56
+ "#{CALL_TMPL % [call_tmpl_args, call_tmpl_args]}"
57
+ )
58
+
59
+ if Kind.of_module?(self)
60
+ self.send(:extend, Functional::Steps)
61
+ else
62
+ self.send(:include, Functional::Steps)
63
+ self.send(:include, Functional::Behavior)
64
+
65
+ __dependencies__.freeze
66
+ end
67
+
68
+ self.send(:protected, :call!)
69
+
70
+ self
71
+ end
72
+ end
73
+
74
+ def self.included(base)
75
+ Kind.of_class(base).send(:extend, Functional::DependencyInjection)
76
+
77
+ base.send(:extend, Macros)
78
+ end
79
+
80
+ def self.extended(base)
81
+ base.send(:extend, Kind.of_module(base))
82
+ base.send(:extend, Macros)
83
+ end
84
+
85
+ private_constant :Macros, :CALL_TMPL
86
+ end
87
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+ require 'kind/empty'
5
+ require 'kind/result'
6
+ require 'kind/__lib__/action_steps'
7
+
8
+ module Kind
9
+ module Functional
10
+ module Steps
11
+ def self.extended(base)
12
+ base.extend(Result::Methods)
13
+ base.extend(ACTION_STEPS)
14
+ end
15
+
16
+ def self.included(base)
17
+ base.send(:include, Result::Methods)
18
+ base.send(:include, ACTION_STEPS)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -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 = STRICT.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,44 +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
- extend self
8
-
9
- require 'kind/maybe/result'
7
+ require 'kind/maybe/monad'
10
8
  require 'kind/maybe/none'
11
9
  require 'kind/maybe/some'
12
- require 'kind/maybe/wrappable'
10
+ require 'kind/maybe/wrapper'
13
11
  require 'kind/maybe/typed'
12
+ require 'kind/maybe/methods'
13
+
14
+ extend self
14
15
 
15
16
  def new(value)
16
- (KIND.null?(value) ? None : Some)
17
+ (::Exception === value || KIND.nil_or_undefined?(value) ? None : Some)
17
18
  .new(value)
18
19
  end
19
20
 
20
21
  alias_method :[], :new
21
22
 
22
- extend Wrappable
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
23
42
  end
24
43
 
25
44
  Optional = Maybe
26
45
 
27
- None = Maybe.none
46
+ None = Maybe::NONE_INSTANCE
28
47
 
29
48
  def self.None
30
- None
49
+ Maybe::NONE_INSTANCE
31
50
  end
32
51
 
33
52
  def self.Some(value)
34
- Maybe.some(value)
53
+ Maybe::Some[value]
35
54
  end
36
55
 
37
56
  def self.Maybe(kind)
38
- 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]
39
61
  end
40
62
 
41
63
  def self.Optional(kind)
42
- 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]
43
68
  end
44
69
  end