kind 3.0.1 → 5.1.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -0
  3. data/.travis.sh +42 -12
  4. data/.travis.yml +7 -5
  5. data/CHANGELOG.md +1309 -0
  6. data/Gemfile +22 -7
  7. data/README.md +990 -490
  8. data/kind.gemspec +1 -1
  9. data/lib/kind.rb +29 -292
  10. data/lib/kind/active_model/validation.rb +3 -4
  11. data/lib/kind/core.rb +15 -0
  12. data/lib/kind/core/dig.rb +40 -0
  13. data/lib/kind/core/empty.rb +13 -0
  14. data/lib/kind/core/empty/constant.rb +7 -0
  15. data/lib/kind/{error.rb → core/error.rb} +2 -6
  16. data/lib/kind/core/maybe.rb +42 -0
  17. data/lib/kind/core/maybe/none.rb +57 -0
  18. data/lib/kind/core/maybe/result.rb +51 -0
  19. data/lib/kind/core/maybe/some.rb +90 -0
  20. data/lib/kind/core/maybe/typed.rb +29 -0
  21. data/lib/kind/core/maybe/wrappable.rb +33 -0
  22. data/lib/kind/core/presence.rb +33 -0
  23. data/lib/kind/core/try.rb +34 -0
  24. data/lib/kind/core/type_checker.rb +87 -0
  25. data/lib/kind/core/type_checkers.rb +30 -0
  26. data/lib/kind/core/type_checkers/custom/boolean.rb +19 -0
  27. data/lib/kind/core/type_checkers/custom/callable.rb +19 -0
  28. data/lib/kind/core/type_checkers/custom/lambda.rb +19 -0
  29. data/lib/kind/core/type_checkers/ruby/array.rb +17 -0
  30. data/lib/kind/core/type_checkers/ruby/class.rb +13 -0
  31. data/lib/kind/core/type_checkers/ruby/comparable.rb +13 -0
  32. data/lib/kind/core/type_checkers/ruby/enumerable.rb +13 -0
  33. data/lib/kind/core/type_checkers/ruby/enumerator.rb +13 -0
  34. data/lib/kind/core/type_checkers/ruby/file.rb +13 -0
  35. data/lib/kind/core/type_checkers/ruby/float.rb +13 -0
  36. data/lib/kind/core/type_checkers/ruby/hash.rb +17 -0
  37. data/lib/kind/core/type_checkers/ruby/integer.rb +13 -0
  38. data/lib/kind/core/type_checkers/ruby/io.rb +13 -0
  39. data/lib/kind/core/type_checkers/ruby/method.rb +13 -0
  40. data/lib/kind/core/type_checkers/ruby/module.rb +17 -0
  41. data/lib/kind/core/type_checkers/ruby/numeric.rb +13 -0
  42. data/lib/kind/core/type_checkers/ruby/proc.rb +13 -0
  43. data/lib/kind/core/type_checkers/ruby/queue.rb +14 -0
  44. data/lib/kind/core/type_checkers/ruby/range.rb +13 -0
  45. data/lib/kind/core/type_checkers/ruby/regexp.rb +13 -0
  46. data/lib/kind/core/type_checkers/ruby/string.rb +17 -0
  47. data/lib/kind/core/type_checkers/ruby/struct.rb +13 -0
  48. data/lib/kind/core/type_checkers/ruby/symbol.rb +13 -0
  49. data/lib/kind/core/type_checkers/ruby/time.rb +13 -0
  50. data/lib/kind/core/type_checkers/stdlib/open_struct.rb +13 -0
  51. data/lib/kind/core/type_checkers/stdlib/set.rb +17 -0
  52. data/lib/kind/{undefined.rb → core/undefined.rb} +4 -2
  53. data/lib/kind/core/utils/kind.rb +61 -0
  54. data/lib/kind/core/utils/undefined.rb +7 -0
  55. data/lib/kind/validator.rb +108 -1
  56. data/lib/kind/version.rb +1 -1
  57. data/test.sh +4 -4
  58. metadata +50 -15
  59. data/lib/kind/active_model/kind_validator.rb +0 -96
  60. data/lib/kind/checker.rb +0 -15
  61. data/lib/kind/checker/factory.rb +0 -35
  62. data/lib/kind/checker/protocol.rb +0 -73
  63. data/lib/kind/empty.rb +0 -21
  64. data/lib/kind/is.rb +0 -19
  65. data/lib/kind/maybe.rb +0 -183
  66. data/lib/kind/of.rb +0 -11
  67. data/lib/kind/types.rb +0 -115
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ require 'kind/core/maybe/result'
6
+ require 'kind/core/maybe/none'
7
+ require 'kind/core/maybe/some'
8
+ require 'kind/core/maybe/wrappable'
9
+ require 'kind/core/maybe/typed'
10
+
11
+ extend self
12
+
13
+ def new(value)
14
+ (KIND.null?(value) ? None : Some)
15
+ .new(value)
16
+ end
17
+
18
+ alias_method :[], :new
19
+
20
+ extend Wrappable
21
+ end
22
+
23
+ Optional = Maybe
24
+
25
+ None = Maybe.none
26
+
27
+ def self.None
28
+ None
29
+ end
30
+
31
+ def self.Some(value)
32
+ Maybe.some(value)
33
+ end
34
+
35
+ def self.Maybe(kind)
36
+ Maybe::Typed.new(kind)
37
+ end
38
+
39
+ def self.Optional(kind)
40
+ Maybe::Typed.new(kind)
41
+ end
42
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ class None < Result
6
+ INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
7
+
8
+ def value_or(default = UNDEFINED, &block)
9
+ raise ArgumentError, INVALID_DEFAULT_ARG if UNDEFINED == default && !block
10
+
11
+ UNDEFINED != default ? default : block.call
12
+ end
13
+
14
+ def none?; true; end
15
+
16
+ def map(&fn)
17
+ self
18
+ end
19
+
20
+ alias_method :map!, :map
21
+ alias_method :then, :map
22
+ alias_method :then!, :map
23
+ alias_method :check, :map
24
+
25
+ def try!(method_name = UNDEFINED, *args, &block)
26
+ KIND.of!(::Symbol, method_name)if UNDEFINED != method_name
27
+
28
+ self
29
+ end
30
+
31
+ alias_method :try, :try!
32
+
33
+ def dig(*keys)
34
+ self
35
+ end
36
+
37
+ def presence
38
+ self
39
+ end
40
+
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)
53
+ end
54
+
55
+ private_constant :NONE_WITH_NIL_VALUE, :NONE_WITH_UNDEFINED_VALUE
56
+ end
57
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ class Result
6
+ attr_reader :value
7
+
8
+ Value = ->(arg) { arg.kind_of?(Maybe::Result) ? arg.value : arg } # :nodoc:
9
+
10
+ def initialize(arg)
11
+ @value = Value.(arg)
12
+ end
13
+
14
+ def value_or(method_name = UNDEFINED, &block)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def none?
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def some?; !none?; end
23
+
24
+ def map(&fn)
25
+ raise NotImplementedError
26
+ end
27
+
28
+ alias_method :map!, :map
29
+ alias_method :then, :map
30
+ alias_method :then!, :map
31
+
32
+ def check(&fn)
33
+ raise NotImplementedError
34
+ end
35
+
36
+ def try(method_name = UNDEFINED, &block)
37
+ raise NotImplementedError
38
+ end
39
+
40
+ alias_method :try!, :try
41
+
42
+ def dig(*keys)
43
+ raise NotImplementedError
44
+ end
45
+
46
+ def presence
47
+ raise NotImplementedError
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ class Some < Result
6
+ def value_or(default = UNDEFINED, &block)
7
+ @value
8
+ end
9
+
10
+ def none?; false; end
11
+
12
+ def map(&fn)
13
+ map!(&fn)
14
+ rescue StandardError => exception
15
+ None.new(exception)
16
+ end
17
+
18
+ alias_method :then, :map
19
+
20
+ def check(&fn)
21
+ result = fn.call(@value)
22
+
23
+ !result || KIND.null?(result) ? NONE_WITH_NIL_VALUE : self
24
+ end
25
+
26
+ def map!(&fn)
27
+ result = fn.call(@value)
28
+
29
+ resolve(result)
30
+ end
31
+
32
+ alias_method :then!, :map!
33
+
34
+ def try!(method_name = UNDEFINED, *args, &block)
35
+ return __try_block__(block, args) if block
36
+
37
+ return __try_method__(method_name, args) if UNDEFINED != method_name
38
+
39
+ raise ArgumentError, 'method name or a block must be present'
40
+ end
41
+
42
+ def try(method_name = UNDEFINED, *args, &block)
43
+ return __try_block__(block, args) if block
44
+
45
+ return __try_method__(method_name, args) if value.respond_to?(method_name)
46
+
47
+ NONE_WITH_NIL_VALUE
48
+ end
49
+
50
+ def dig(*keys)
51
+ resolve(Dig.(value, keys))
52
+ end
53
+
54
+ def presence
55
+ resolve(Presence.(value))
56
+ end
57
+
58
+ private
59
+
60
+ def __try_method__(method_name, args)
61
+ __try_block__(KIND.of!(::Symbol, method_name).to_proc, args)
62
+ end
63
+
64
+ def __try_block__(block, args)
65
+ result = args.empty? ? block.call(value) : block.call(*args.unshift(value))
66
+
67
+ resolve(result)
68
+ end
69
+
70
+ def resolve(result)
71
+ return result if Maybe::None === result
72
+ return None.new(result) if Exception === result
73
+ return NONE_WITH_NIL_VALUE if result.nil?
74
+ return NONE_WITH_UNDEFINED_VALUE if Undefined == result
75
+
76
+ Some.new(result)
77
+ end
78
+ end
79
+
80
+ VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
81
+
82
+ def self.some(value)
83
+ return Maybe::Some.new(value) if !KIND.null?(value)
84
+
85
+ raise ArgumentError, VALUE_CANT_BE_NONE
86
+ end
87
+
88
+ private_constant :VALUE_CANT_BE_NONE
89
+ end
90
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ class Typed
6
+ include Wrappable
7
+
8
+ def initialize(kind)
9
+ @kind = kind
10
+ end
11
+
12
+ def new(arg)
13
+ value = Result::Value.(arg)
14
+
15
+ @kind === value ? Maybe.some(value) : Maybe.none
16
+ end
17
+
18
+ alias_method :[], :new
19
+
20
+ private
21
+
22
+ def __call_before_expose_the_arg_in_a_block(arg)
23
+ value = Result::Value.(arg)
24
+
25
+ @kind === value ? value : Maybe.none
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Maybe
5
+ module Wrappable
6
+ def wrap(arg = UNDEFINED)
7
+ if block_given?
8
+ begin
9
+ return new(yield) if UNDEFINED == arg
10
+
11
+ input = __call_before_expose_the_arg_in_a_block(arg)
12
+
13
+ input.kind_of?(Maybe::None) ? input : new(yield(input))
14
+ rescue StandardError => exception
15
+ Maybe.__none__(exception)
16
+ end
17
+ else
18
+ return new(arg) if UNDEFINED != arg
19
+
20
+ raise ArgumentError, 'wrong number of arguments (given 0, expected 1)'
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def __call_before_expose_the_arg_in_a_block(input)
27
+ input
28
+ end
29
+ end
30
+
31
+ private_constant :Wrappable
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Presence
5
+ extend self
6
+
7
+ def call(object)
8
+ return object.blank? ? nil : object if object.respond_to?(:blank?)
9
+
10
+ return object if TrueClass === object
11
+
12
+ return blank_str?(object) ? nil : object if String === object
13
+
14
+ return object.empty? ? nil : object if object.respond_to?(:empty?)
15
+
16
+ return object if object
17
+ end
18
+
19
+ def to_proc
20
+ -> object { call(object) }
21
+ end
22
+
23
+ private
24
+
25
+ BLANK_RE = /\A[[:space:]]*\z/
26
+
27
+ def blank_str?(object)
28
+ object.empty? || BLANK_RE === object
29
+ end
30
+
31
+ private_constant :BLANK_RE
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Try
5
+ extend self
6
+
7
+ def call(*args)
8
+ object = args.shift
9
+
10
+ call!(object, args.shift, args)
11
+ end
12
+
13
+ def self.[](*args)
14
+ method_name = args.shift
15
+
16
+ ->(object) { call!(object, method_name, args) }
17
+ end
18
+
19
+ def call!(object, method_name, args) # :nodoc
20
+ return if KIND.null?(object)
21
+
22
+ resolve(object, method_name, args)
23
+ end
24
+
25
+ private
26
+
27
+ def resolve(object, method_name, args = Empty::ARRAY)
28
+ return unless object.respond_to?(method_name)
29
+ return object.public_send(method_name) if args.empty?
30
+
31
+ object.public_send(method_name, *args)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module TypeChecker
5
+ def name
6
+ kind.name
7
+ end
8
+
9
+ def ===(value)
10
+ kind === value
11
+ end
12
+
13
+ def [](value)
14
+ return value if self === value
15
+
16
+ KIND.error!(name, value)
17
+ end
18
+
19
+ def or_nil(value)
20
+ return value if self === value
21
+ end
22
+
23
+ def or_undefined(value)
24
+ or_nil(value) || Undefined
25
+ end
26
+
27
+ def or(fallback, value = UNDEFINED)
28
+ return __or_func.(fallback) if UNDEFINED === value
29
+
30
+ self === value ? value : fallback
31
+ end
32
+
33
+ def value?(value = UNDEFINED)
34
+ return self === value if UNDEFINED != value
35
+
36
+ @__is_value ||= ->(tc) { ->(arg) { tc === arg } }.(self)
37
+ end
38
+
39
+ def value(arg, default:)
40
+ KIND.value(self, arg, self[default])
41
+ end
42
+
43
+ def or_null(value) # :nodoc:
44
+ KIND.null?(value) ? value : self[value]
45
+ end
46
+
47
+ def maybe(value = UNDEFINED, &block)
48
+ return __maybe[value] if UNDEFINED != value && !block
49
+ return __maybe.wrap(&block) if UNDEFINED == value && block
50
+ return __maybe.wrap(value, &block) if UNDEFINED != value && block
51
+
52
+ __maybe
53
+ end
54
+
55
+ alias optional maybe
56
+
57
+ private
58
+
59
+ def __maybe
60
+ @__maybe ||= Maybe::Typed.new(self)
61
+ end
62
+
63
+ def __or_func
64
+ @__or_func ||= ->(tc, fb, value) { tc === value ? value : tc.or_null(fb) }.curry[self]
65
+ end
66
+ end
67
+
68
+ class TypeChecker::Object # :nodoc: all
69
+ include TypeChecker
70
+
71
+ ResolveKindName = ->(kind, opt) do
72
+ name = Try.(opt, :[], :name)
73
+ name || Try.(kind, :name)
74
+ end
75
+
76
+ attr_reader :kind, :name
77
+
78
+ def initialize(kind, opt)
79
+ name = ResolveKindName.(kind, opt)
80
+
81
+ @name = KIND.of!(::String, name)
82
+ @kind = KIND.respond_to!(:===, kind)
83
+ end
84
+
85
+ private_constant :ResolveKindName
86
+ end
87
+ end