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,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/__lib__/kind'
4
+ require 'kind/__lib__/undefined'
5
+
6
+ module Kind
7
+ module ATTRIBUTES
8
+ extend self
9
+
10
+ def name!(name)
11
+ STRICT.kind_of(::Symbol, name)
12
+ end
13
+
14
+ def value(kind, default, visibility = :private)
15
+ [kind, default, visibility]
16
+ end
17
+
18
+ def value_to_assign(kind, default, hash, name)
19
+ raw_value = hash[name]
20
+
21
+ return raw_value if kind.nil? && UNDEFINED == default
22
+
23
+ value = resolve_value_to_assign(kind, default, raw_value)
24
+
25
+ (kind.nil? || kind === value) ? value : nil
26
+ end
27
+
28
+ def value!(kind, default)
29
+ return value(kind, default) unless kind.nil?
30
+
31
+ raise Error.new("kind expected to not be nil")
32
+ end
33
+
34
+ def value_to_assign!(kind, default, hash, name)
35
+ value = resolve_value_to_assign(kind, default, hash[name])
36
+
37
+ Kind.of(kind, value, label: name)
38
+ end
39
+
40
+ private
41
+
42
+ def resolve_value_to_assign(kind, default, value)
43
+ if kind == ::Proc
44
+ UNDEFINED == default ? value : KIND.value(kind, value, default)
45
+ else
46
+ default_is_a_callable = default.respond_to?(:call)
47
+
48
+ default_value =
49
+ if default_is_a_callable
50
+ default_fn = Proc === default ? default : default.method(:call)
51
+
52
+ default_fn.arity != 0 ? default_fn.call(value) : default_fn.call
53
+ else
54
+ default
55
+ end
56
+
57
+ return value if UNDEFINED == default_value
58
+ return default_value || value if kind.nil?
59
+
60
+ default_is_a_callable ? KIND.value(kind, default_value, value) : KIND.value(kind, value, default_value)
61
+ end
62
+ end
63
+ end
64
+
65
+ private_constant :ATTRIBUTES
66
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/__lib__/strict'
4
+
5
+ module Kind
6
+ module KIND
7
+ extend self
8
+
9
+ def nil_or_undefined?(value) # :nodoc:
10
+ value.nil? || Undefined == value
11
+ end
12
+
13
+ def of?(kind, values) # :nodoc:
14
+ of_kind = -> value { kind === value }
15
+
16
+ values.empty? ? of_kind : values.all?(&of_kind)
17
+ end
18
+
19
+ def respond_to!(method_name, value) # :nodoc:
20
+ return value if value.respond_to?(method_name)
21
+
22
+ raise Error.new("expected #{value} to respond to :#{method_name}")
23
+ end
24
+
25
+ def interface?(method_names, value) # :nodoc:
26
+ method_names.all? { |method_name| value.respond_to?(method_name) }
27
+ end
28
+
29
+ def value(kind, arg, default) # :nodoc:
30
+ kind === arg ? arg : default
31
+ end
32
+
33
+ def is?(expected, value) # :nodoc:
34
+ is(STRICT.module_or_class(expected), value)
35
+ end
36
+
37
+ private
38
+
39
+ def is(expected_kind, value) # :nodoc:
40
+ kind = STRICT.module_or_class(value)
41
+
42
+ if OF.class?(kind)
43
+ kind <= expected_kind || expected_kind == ::Class
44
+ else
45
+ kind == expected_kind || kind.kind_of?(expected_kind)
46
+ end
47
+ end
48
+ end
49
+
50
+ private_constant :KIND
51
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module OF
5
+ extend self
6
+
7
+ def class?(value) # :nodoc:
8
+ value.kind_of?(::Class)
9
+ end
10
+
11
+ def module?(value) # :nodoc:
12
+ ::Module == value || (value.kind_of?(::Module) && !class?(value))
13
+ end
14
+ end
15
+
16
+ private_constant :OF
17
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/__lib__/of'
4
+
5
+ module Kind
6
+ module STRICT
7
+ extend self
8
+
9
+ def error(kind_name, value, label = nil) # :nodoc:
10
+ raise Error.new(kind_name, value, label: label)
11
+ end
12
+
13
+ def object_is_a(kind, value, label = nil) # :nodoc:
14
+ return value if kind === value
15
+
16
+ error(kind.name, value, label)
17
+ end
18
+
19
+ def kind_of(kind, value, kind_name = nil) # :nodoc:
20
+ return value if kind === value
21
+
22
+ error(kind_name || kind.name, value)
23
+ end
24
+
25
+ def module_or_class(value) # :nodoc:
26
+ kind_of(::Module, value, 'Module/Class')
27
+ end
28
+
29
+ def class!(value) # :nodoc:
30
+ kind_of(::Class, value)
31
+ end
32
+
33
+ def module!(value) # :nodoc:
34
+ return value if OF.module?(value)
35
+
36
+ error('Module', value)
37
+ end
38
+
39
+ def not_nil(value, label) # :nodoc:
40
+ return value unless value.nil?
41
+
42
+ label_text = label ? "#{label}: " : ''
43
+
44
+ raise Error.new("#{label_text}expected to not be nil")
45
+ end
46
+ end
47
+
48
+ private_constant :STRICT
49
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ UNDEFINED = Object.new.tap do |undefined|
5
+ def undefined.inspect
6
+ @inspect ||= 'UNDEFINED'.freeze
7
+ end
8
+
9
+ undefined.inspect
10
+ undefined.freeze
11
+ end
12
+
13
+ private_constant :UNDEFINED
14
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+ require 'kind/empty'
5
+ require 'kind/result'
6
+ require 'kind/immutable_attributes'
7
+ require 'kind/__lib__/action_steps'
8
+
9
+ module Kind
10
+ module Action
11
+ CALL_TMPL = [
12
+ 'def self.call(arg)',
13
+ ' new(Kind.of!(::Hash, arg)).call',
14
+ 'end',
15
+ '',
16
+ 'def call',
17
+ ' result = call!',
18
+ '',
19
+ ' return result if Kind::Result::Monad === result',
20
+ '',
21
+ ' raise Kind::Error, "#{self.class.name}#call! must return a Success() or Failure()"',
22
+ 'end'
23
+ ].join("\n").freeze
24
+
25
+ private_constant :CALL_TMPL
26
+
27
+ module ClassMethods
28
+ include ImmutableAttributes::ClassMethods
29
+
30
+ def to_proc
31
+ @to_proc ||= ->(arg) { call(arg) }
32
+ end
33
+
34
+ ATTRIBUTE_METHODS = [
35
+ :attributes, :attribute,
36
+ :attribute?, :attribute!,
37
+ :with_attribute, :with_attributes,
38
+ :nil_attributes, :nil_attributes?
39
+ ]
40
+
41
+ private_constant :ATTRIBUTE_METHODS
42
+
43
+ def kind_action!
44
+ return self if respond_to?(:call)
45
+
46
+ public_methods = self.public_instance_methods - ::Object.new.methods
47
+
48
+ remaining_methods = public_methods - (__attributes__.keys + ATTRIBUTE_METHODS)
49
+
50
+ unless remaining_methods.include?(:call!)
51
+ raise Kind::Error.new("expected #{self} to implement `#call!`")
52
+ end
53
+
54
+ if remaining_methods.size > 1
55
+ raise Kind::Error.new("#{self} can only have `#call!` as its public method")
56
+ end
57
+
58
+ call_parameters = public_instance_method(:call!).parameters
59
+
60
+ unless call_parameters.empty?
61
+ raise ArgumentError, "#{self.name}#call! must receive no arguments"
62
+ end
63
+
64
+ def self.inherited(_)
65
+ raise RuntimeError, "#{self.name} is a Kind::Action and it can't be inherited"
66
+ end
67
+
68
+ self.send(:private_class_method, :new)
69
+
70
+ self.class_eval(CALL_TMPL)
71
+
72
+ self.send(:alias_method, :[], :call)
73
+ self.send(:alias_method, :===, :call)
74
+ self.send(:alias_method, :yield, :call)
75
+ end
76
+ end
77
+
78
+ module StepAdapters
79
+ private
80
+
81
+ def Check!(mthod); __Check(mthod, Empty::HASH); end
82
+ def Step!(mthod); __Step(mthod, Empty::HASH); end
83
+ def Map!(mthod); __Map(mthod, Empty::HASH); end
84
+ def Tee!(_mthod); raise NotImplementedError; end
85
+ def Try!(mthod, opt = Empty::HASH); __Try(mthod, Empty::HASH, opt); end
86
+
87
+ def __resolve_step(method_name, value)
88
+ m = method(method_name)
89
+ m.arity > 0 ? m.call(value) : m.call
90
+ end
91
+
92
+ def __map_step_exception(value)
93
+ { exception: value }
94
+ end
95
+ end
96
+
97
+ private_constant :StepAdapters
98
+
99
+ def self.included(base)
100
+ Kind.of_class(base).extend(ClassMethods)
101
+
102
+ base.send(:include, ACTION_STEPS)
103
+ base.send(:include, StepAdapters)
104
+ base.send(:include, ImmutableAttributes::Reader)
105
+ end
106
+
107
+ include ImmutableAttributes::Initializer
108
+
109
+ def inspect
110
+ '#<%s attributes=%p nil_attributes=%p>' % [self.class.name, attributes, nil_attributes]
111
+ end
112
+
113
+ private
114
+
115
+ def Failure(arg1 = UNDEFINED, arg2 = UNDEFINED)
116
+ arg1 = Empty::HASH if UNDEFINED == arg1 && UNDEFINED == arg2
117
+
118
+ Result::Failure[arg1, arg2, value_must_be_a: ::Hash]
119
+ end
120
+
121
+ def Success(arg1 = UNDEFINED, arg2 = UNDEFINED)
122
+ arg1 = Empty::HASH if UNDEFINED == arg1 && UNDEFINED == arg2
123
+
124
+ Result::Success[arg1, arg2, value_must_be_a: ::Hash]
125
+ end
126
+ end
127
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'kind'
3
+ warn '[DEPRECATION] "kind/active_model/validation" is deprecated; use "kind/validator" instead. ' \
4
+ 'It will be removed on next major release.'
5
+
4
6
  require 'kind/validator'
5
- require 'active_model'
6
- require 'active_model/validations'
7
- require_relative 'kind_validator'
data/lib/kind/basic.rb ADDED
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/version'
4
+
5
+ require 'kind/__lib__/kind'
6
+ require 'kind/__lib__/undefined'
7
+
8
+ require 'kind/basic/undefined'
9
+ require 'kind/basic/error'
10
+
11
+ module Kind
12
+ extend self
13
+
14
+ def is?(kind, arg)
15
+ KIND.is?(kind, arg)
16
+ end
17
+
18
+ def is(*args)
19
+ warn '[DEPRECATION] Kind.is will behave like Kind.is! in the next major release; ' \
20
+ 'use Kind.is? instead.'
21
+
22
+ is?(*args)
23
+ end
24
+
25
+ def is!(kind, arg, label: nil)
26
+ return arg if KIND.is?(kind, arg)
27
+
28
+ label_text = label ? "#{label}: " : ''
29
+
30
+ raise Kind::Error.new("#{label_text}#{arg} expected to be a #{kind}")
31
+ end
32
+
33
+ def of?(kind, *args)
34
+ KIND.of?(kind, args)
35
+ end
36
+
37
+ def of_class?(value)
38
+ OF.class?(value)
39
+ end
40
+
41
+ def of_module?(value)
42
+ OF.module?(value)
43
+ end
44
+
45
+ def respond_to?(value, *method_names)
46
+ return super(value) if method_names.empty?
47
+
48
+ KIND.interface?(method_names, value)
49
+ end
50
+
51
+ def of(kind, value, label: nil)
52
+ STRICT.object_is_a(kind, value, label)
53
+ end
54
+
55
+ alias_method :of!, :of
56
+
57
+ def of_class(value)
58
+ STRICT.class!(value)
59
+ end
60
+
61
+ def of_module(value)
62
+ STRICT.module!(value)
63
+ end
64
+
65
+ def of_module_or_class(value)
66
+ STRICT.module_or_class(value)
67
+ end
68
+
69
+ def respond_to(value, *method_names)
70
+ method_names.each { |method_name| KIND.respond_to!(method_name, value) }
71
+
72
+ value
73
+ end
74
+ alias_method :respond_to!, :respond_to
75
+
76
+ def value(kind, value, default:)
77
+ KIND.value(kind, value, of(kind, default))
78
+ end
79
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Error < StandardError
5
+ INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
6
+
7
+ def self.wrong_number_of_args!(given:, expected:)
8
+ raise ArgumentError, "wrong number of arguments (given #{given}, expected #{expected})"
9
+ end
10
+
11
+ def self.invalid_default_arg!
12
+ raise ArgumentError, INVALID_DEFAULT_ARG
13
+ end
14
+
15
+ def initialize(arg, object = UNDEFINED, label: nil)
16
+ if UNDEFINED == object
17
+ # Will be used when the exception was raised with a message. e.g:
18
+ # raise Kind::Error, "some message"
19
+ super(arg)
20
+ else
21
+ label_text = label ? "#{label}: " : ''
22
+
23
+ super("#{label_text}#{object.inspect} expected to be a kind of #{arg}")
24
+ end
25
+ end
26
+
27
+ private_constant :INVALID_DEFAULT_ARG
28
+ end
29
+ end