kind 4.0.0 → 5.3.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 (111) 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 +486 -28
  6. data/Gemfile +13 -6
  7. data/README.md +153 -49
  8. data/kind.gemspec +1 -1
  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 +90 -0
  36. data/lib/kind/immutable_attributes.rb +34 -0
  37. data/lib/kind/immutable_attributes/initializer.rb +70 -0
  38. data/lib/kind/immutable_attributes/reader.rb +38 -0
  39. data/lib/kind/maybe.rb +37 -12
  40. data/lib/kind/maybe/methods.rb +21 -0
  41. data/lib/kind/maybe/monad.rb +82 -0
  42. data/lib/kind/maybe/monad/wrapper.rb +19 -0
  43. data/lib/kind/maybe/none.rb +12 -19
  44. data/lib/kind/maybe/some.rb +68 -26
  45. data/lib/kind/maybe/typed.rb +11 -5
  46. data/lib/kind/maybe/{wrappable.rb → wrapper.rb} +9 -7
  47. data/lib/kind/monad.rb +22 -0
  48. data/lib/kind/monads.rb +5 -0
  49. data/lib/kind/objects.rb +17 -0
  50. data/lib/kind/objects/basic_object.rb +43 -0
  51. data/lib/kind/objects/modules.rb +32 -0
  52. data/lib/kind/{type_checkers → objects/modules}/core/array.rb +3 -1
  53. data/lib/kind/{type_checkers → objects/modules}/core/class.rb +1 -1
  54. data/lib/kind/{type_checkers → objects/modules}/core/comparable.rb +1 -1
  55. data/lib/kind/{type_checkers → objects/modules}/core/enumerable.rb +1 -1
  56. data/lib/kind/{type_checkers → objects/modules}/core/enumerator.rb +1 -1
  57. data/lib/kind/{type_checkers → objects/modules}/core/file.rb +1 -1
  58. data/lib/kind/{type_checkers → objects/modules}/core/float.rb +1 -1
  59. data/lib/kind/{type_checkers → objects/modules}/core/hash.rb +3 -1
  60. data/lib/kind/{type_checkers → objects/modules}/core/integer.rb +1 -1
  61. data/lib/kind/{type_checkers → objects/modules}/core/io.rb +1 -1
  62. data/lib/kind/{type_checkers → objects/modules}/core/method.rb +1 -1
  63. data/lib/kind/{type_checkers → objects/modules}/core/module.rb +1 -1
  64. data/lib/kind/{type_checkers → objects/modules}/core/numeric.rb +1 -1
  65. data/lib/kind/{type_checkers → objects/modules}/core/proc.rb +1 -1
  66. data/lib/kind/{type_checkers → objects/modules}/core/queue.rb +1 -1
  67. data/lib/kind/{type_checkers → objects/modules}/core/range.rb +1 -1
  68. data/lib/kind/{type_checkers → objects/modules}/core/regexp.rb +1 -1
  69. data/lib/kind/{type_checkers → objects/modules}/core/string.rb +3 -1
  70. data/lib/kind/{type_checkers → objects/modules}/core/struct.rb +1 -1
  71. data/lib/kind/{type_checkers → objects/modules}/core/symbol.rb +1 -1
  72. data/lib/kind/{type_checkers → objects/modules}/core/time.rb +1 -1
  73. data/lib/kind/{type_checkers → objects/modules}/custom/boolean.rb +2 -2
  74. data/lib/kind/{type_checkers → objects/modules}/custom/callable.rb +1 -1
  75. data/lib/kind/{type_checkers → objects/modules}/custom/lambda.rb +1 -1
  76. data/lib/kind/{type_checkers → objects/modules}/stdlib/open_struct.rb +3 -1
  77. data/lib/kind/{type_checkers → objects/modules}/stdlib/set.rb +3 -1
  78. data/lib/kind/objects/nil.rb +17 -0
  79. data/lib/kind/objects/not_nil.rb +9 -0
  80. data/lib/kind/objects/object.rb +56 -0
  81. data/lib/kind/objects/respond_to.rb +30 -0
  82. data/lib/kind/objects/union_type.rb +44 -0
  83. data/lib/kind/presence.rb +4 -2
  84. data/lib/kind/result.rb +31 -0
  85. data/lib/kind/result/abstract.rb +53 -0
  86. data/lib/kind/result/failure.rb +33 -0
  87. data/lib/kind/result/methods.rb +17 -0
  88. data/lib/kind/result/monad.rb +74 -0
  89. data/lib/kind/result/monad/wrapper.rb +19 -0
  90. data/lib/kind/result/success.rb +53 -0
  91. data/lib/kind/strict/disabled.rb +34 -0
  92. data/lib/kind/try.rb +21 -11
  93. data/lib/kind/validator.rb +111 -0
  94. data/lib/kind/version.rb +1 -1
  95. metadata +78 -48
  96. data/lib/kind/active_model/kind_validator.rb +0 -96
  97. data/lib/kind/core.rb +0 -9
  98. data/lib/kind/core/deprecation.rb +0 -29
  99. data/lib/kind/core/kind.rb +0 -61
  100. data/lib/kind/core/undefined.rb +0 -7
  101. data/lib/kind/deprecations/built_in_type_checkers.rb +0 -23
  102. data/lib/kind/deprecations/checker.rb +0 -16
  103. data/lib/kind/deprecations/checker/factory.rb +0 -31
  104. data/lib/kind/deprecations/checker/protocol.rb +0 -73
  105. data/lib/kind/deprecations/is.rb +0 -35
  106. data/lib/kind/deprecations/of.rb +0 -258
  107. data/lib/kind/deprecations/types.rb +0 -121
  108. data/lib/kind/error.rb +0 -15
  109. data/lib/kind/maybe/result.rb +0 -51
  110. data/lib/kind/type_checker.rb +0 -73
  111. data/lib/kind/type_checkers.rb +0 -30
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Enum
5
+ class Item
6
+ Underscore = ->(arg) do
7
+ str = String(arg).strip
8
+ str.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
9
+ str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
10
+ str.tr!("-", "_")
11
+ str.downcase!
12
+ str
13
+ end
14
+
15
+ attr_reader :value, :to_s, :name, :to_sym, :inspect
16
+
17
+ alias_method :key, :to_s
18
+ alias_method :to_str, :to_s
19
+
20
+ def initialize(key, val)
21
+ @value = val.frozen? ? val : val.dup.freeze
22
+
23
+ @to_s = Kind.respond_to(key, :to_sym).to_s
24
+ @name = Underscore[key].upcase.freeze
25
+ @to_sym = key.to_sym
26
+ @inspect = ('#<Kind::Enum::Item name=%p to_s=%p value=%p>' % [@name, @to_s, @value]).freeze
27
+ end
28
+
29
+ def ==(arg)
30
+ arg == value || arg == to_s || arg == to_sym
31
+ end
32
+
33
+ def to_ary
34
+ [key, value]
35
+ end
36
+
37
+ alias_method :===, :==
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Enum
5
+ METHODS = \
6
+ <<-RUBY
7
+ def to_h
8
+ ENUM__HASH
9
+ end
10
+
11
+ def items
12
+ ENUM__ITEMS
13
+ end
14
+
15
+ def ===(arg)
16
+ ENUM__ITEMS.any? { |item| item === arg }
17
+ end
18
+
19
+ def refs
20
+ ENUM__REFS.to_a
21
+ end
22
+
23
+ def keys
24
+ ENUM__KEYS.to_a
25
+ end
26
+
27
+ def values
28
+ ENUM__VALS.to_a
29
+ end
30
+
31
+ def ref?(arg)
32
+ ENUM__REFS.include?(arg)
33
+ end
34
+
35
+ def key?(arg)
36
+ arg.respond_to?(:to_sym) ? ref?(arg) && !value?(arg) : false
37
+ end
38
+
39
+ def value?(arg)
40
+ ENUM__VALS.include?(arg)
41
+ end
42
+
43
+ def [](arg)
44
+ return arg if ref?(arg)
45
+
46
+ raise KeyError, "key or value not found: %p" % [arg]
47
+ end
48
+
49
+ def ref(arg)
50
+ arg if ref?(arg)
51
+ end
52
+
53
+ def item(arg)
54
+ ENUM__MAP[arg]
55
+ end
56
+
57
+ def key(arg)
58
+ item(arg).key if value?(arg)
59
+ end
60
+
61
+ def value_at(arg)
62
+ item(arg).value if key?(arg)
63
+ end
64
+
65
+ def self.included(base)
66
+ base.extend(base)
67
+ end
68
+ RUBY
69
+
70
+ private_constant :METHODS
71
+ end
72
+ end
@@ -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,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/result'
4
+ require 'kind/functional'
5
+ require 'kind/__lib__/action_steps'
6
+
7
+ module Kind
8
+ module Functional::Action
9
+ CALL_TMPL = [
10
+ 'def call(%s)',
11
+ ' result = call!(%s)',
12
+ '',
13
+ ' return result if Kind::Result::Monad === result',
14
+ '',
15
+ ' raise Kind::Error, "#{self.class.name}#call! must return a Kind::Success or Kind::Failure"',
16
+ 'end'
17
+ ].join("\n").freeze
18
+
19
+ module Macros
20
+ def kind_functional_action!
21
+ return self if Kind.is?(Result::Methods, self)
22
+
23
+ public_methods = self.public_instance_methods - ::Object.new.methods
24
+
25
+ unless public_methods.include?(:call!)
26
+ raise Kind::Error.new("expected #{self} to implement `#call!`")
27
+ end
28
+
29
+ if public_methods.size > 1
30
+ raise Kind::Error.new("#{self} can only have `#call!` as its public method")
31
+ end
32
+
33
+ call_parameters = public_instance_method(:call!).parameters
34
+
35
+ if call_parameters.empty?
36
+ raise ArgumentError, "#{self.name}#call! must receive at least one argument"
37
+ end
38
+
39
+ def self.inherited(_)
40
+ raise RuntimeError, "#{self.name} is a Kind::Functional::Action and it can't be inherited"
41
+ end
42
+
43
+ call_parameters.flatten!
44
+
45
+ call_with_args = call_parameters.include?(:req) || call_parameters.include?(:rest)
46
+ call_with_kargs = call_parameters.include?(:keyreq) || call_parameters.include?(:keyrest)
47
+
48
+ call_tmpl_args = '*args, **kargs' if call_with_args && call_with_kargs
49
+ call_tmpl_args = '*args' if call_with_args && !call_with_kargs
50
+ call_tmpl_args = '**kargs' if !call_with_args && call_with_kargs
51
+
52
+ self.class_eval(
53
+ "def to_proc; @to_proc ||= method(:call!).to_proc; end" \
54
+ "\n" \
55
+ "def curry; @curry ||= to_proc.curry; end" \
56
+ "\n" \
57
+ "#{CALL_TMPL % [call_tmpl_args, call_tmpl_args]}"
58
+ )
59
+
60
+ if Kind.of_module?(self)
61
+ self.send(:extend, Result::Methods)
62
+ self.send(:extend, ACTION_STEPS)
63
+ else
64
+ self.send(:include, Result::Methods)
65
+ self.send(:include, ACTION_STEPS)
66
+ self.send(:include, Functional::Behavior)
67
+
68
+ __dependencies__.freeze
69
+ end
70
+
71
+ self.send(:protected, :call!)
72
+
73
+ self
74
+ end
75
+ end
76
+
77
+ def self.included(base)
78
+ Kind.of_class(base).send(:extend, Functional::DependencyInjection)
79
+
80
+ base.send(:extend, Macros)
81
+ end
82
+
83
+ def self.extended(base)
84
+ base.send(:extend, Kind.of_module(base))
85
+ base.send(:extend, Macros)
86
+ end
87
+
88
+ private_constant :Macros, :CALL_TMPL
89
+ end
90
+ 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