kind 2.2.0 → 4.0.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 +37 -12
  4. data/.travis.yml +10 -4
  5. data/CHANGELOG.md +1230 -0
  6. data/Gemfile +10 -2
  7. data/README.md +938 -460
  8. data/lib/kind.rb +52 -298
  9. data/lib/kind/active_model/kind_validator.rb +7 -7
  10. data/lib/kind/core.rb +9 -0
  11. data/lib/kind/core/deprecation.rb +29 -0
  12. data/lib/kind/core/kind.rb +61 -0
  13. data/lib/kind/core/undefined.rb +7 -0
  14. data/lib/kind/deprecations/built_in_type_checkers.rb +23 -0
  15. data/lib/kind/deprecations/checker.rb +16 -0
  16. data/lib/kind/deprecations/checker/factory.rb +31 -0
  17. data/lib/kind/deprecations/checker/protocol.rb +73 -0
  18. data/lib/kind/deprecations/is.rb +35 -0
  19. data/lib/kind/deprecations/of.rb +258 -0
  20. data/lib/kind/{types.rb → deprecations/types.rb} +15 -9
  21. data/lib/kind/dig.rb +40 -0
  22. data/lib/kind/empty.rb +5 -11
  23. data/lib/kind/error.rb +2 -6
  24. data/lib/kind/maybe.rb +22 -113
  25. data/lib/kind/maybe/none.rb +57 -0
  26. data/lib/kind/maybe/result.rb +51 -0
  27. data/lib/kind/maybe/some.rb +90 -0
  28. data/lib/kind/maybe/typed.rb +29 -0
  29. data/lib/kind/maybe/wrappable.rb +35 -0
  30. data/lib/kind/presence.rb +33 -0
  31. data/lib/kind/try.rb +36 -0
  32. data/lib/kind/type_checker.rb +73 -0
  33. data/lib/kind/type_checkers.rb +30 -0
  34. data/lib/kind/type_checkers/core/array.rb +17 -0
  35. data/lib/kind/type_checkers/core/class.rb +13 -0
  36. data/lib/kind/type_checkers/core/comparable.rb +13 -0
  37. data/lib/kind/type_checkers/core/enumerable.rb +13 -0
  38. data/lib/kind/type_checkers/core/enumerator.rb +13 -0
  39. data/lib/kind/type_checkers/core/file.rb +13 -0
  40. data/lib/kind/type_checkers/core/float.rb +13 -0
  41. data/lib/kind/type_checkers/core/hash.rb +17 -0
  42. data/lib/kind/type_checkers/core/integer.rb +13 -0
  43. data/lib/kind/type_checkers/core/io.rb +13 -0
  44. data/lib/kind/type_checkers/core/method.rb +13 -0
  45. data/lib/kind/type_checkers/core/module.rb +17 -0
  46. data/lib/kind/type_checkers/core/numeric.rb +13 -0
  47. data/lib/kind/type_checkers/core/proc.rb +13 -0
  48. data/lib/kind/type_checkers/core/queue.rb +14 -0
  49. data/lib/kind/type_checkers/core/range.rb +13 -0
  50. data/lib/kind/type_checkers/core/regexp.rb +13 -0
  51. data/lib/kind/type_checkers/core/string.rb +17 -0
  52. data/lib/kind/type_checkers/core/struct.rb +13 -0
  53. data/lib/kind/type_checkers/core/symbol.rb +13 -0
  54. data/lib/kind/type_checkers/core/time.rb +13 -0
  55. data/lib/kind/type_checkers/custom/boolean.rb +19 -0
  56. data/lib/kind/type_checkers/custom/callable.rb +19 -0
  57. data/lib/kind/type_checkers/custom/lambda.rb +19 -0
  58. data/lib/kind/type_checkers/stdlib/open_struct.rb +13 -0
  59. data/lib/kind/type_checkers/stdlib/set.rb +17 -0
  60. data/lib/kind/undefined.rb +4 -2
  61. data/lib/kind/validator.rb +1 -1
  62. data/lib/kind/version.rb +1 -1
  63. data/test.sh +4 -4
  64. metadata +52 -7
  65. data/lib/kind/checker.rb +0 -83
  66. data/lib/kind/is.rb +0 -19
  67. data/lib/kind/of.rb +0 -11
@@ -8,6 +8,8 @@ module Kind
8
8
 
9
9
  KIND_OF = <<-RUBY
10
10
  def self.%{method_name}(object = Undefined, options = Empty::HASH)
11
+ DEPRECATION.warn_method_replacement('Kind::Of::%{method_name}', 'Kind::%{method_name}')
12
+
11
13
  default = options[:or]
12
14
 
13
15
  return Kind::Of::%{kind_name} if Undefined == object && default.nil?
@@ -16,29 +18,33 @@ module Kind
16
18
 
17
19
  return object if is_instance
18
20
 
19
- Kind::Of.(::%{kind_name_to_check}, object && default ? default : object || default)
21
+ KIND.of!(::%{kind_name_to_check}, object && default ? default : object || default)
20
22
  end
21
23
  RUBY
22
24
 
23
25
  KIND_OF_IS = <<-RUBY
24
26
  def self.%{method_name}?(*args)
27
+ DEPRECATION.warn_method_replacement('Kind::Of::%{method_name}?', 'Kind::%{method_name}?')
28
+
25
29
  Kind::Of::%{kind_name}.instance?(*args)
26
30
  end
27
31
  RUBY
28
32
 
29
33
  KIND_IS = <<-RUBY
30
- def self.%{method_name}(value = Undefined)
31
- return Kind::Is::%{kind_name} if Undefined == value
34
+ def self.%{method_name}(value)
35
+ DEPRECATION.warn_removal('Kind::Is::%{method_name}')
32
36
 
33
- Kind::Is.__call__(::%{kind_name_to_check}, value)
37
+ KIND.is!(::%{kind_name_to_check}, value)
34
38
  end
35
39
  RUBY
36
40
 
37
41
  private_constant :KIND_OF, :KIND_IS, :COLONS
38
42
 
39
43
  def add(kind, name: nil)
40
- kind_name = Kind.of.Module(kind).name
41
- checker = name ? Kind::Of.(String, name) : kind_name
44
+ DEPRECATION.warn_removal('Kind::Types')
45
+
46
+ kind_name = KIND.of_module_or_class!(kind).name
47
+ checker = name ? Kind::String[name] : kind_name
42
48
 
43
49
  if checker.include?(COLONS)
44
50
  add_kind_with_namespace(checker, method_name: name)
@@ -98,8 +104,8 @@ module Kind
98
104
  kind_name = params[:kind_name]
99
105
  params[:kind_name_to_check] ||= kind_name
100
106
 
101
- kind_checker = ::Module.new { extend Checkable }
102
- kind_checker.module_eval("def self.__kind; #{params[:kind_name_to_check]}; end")
107
+ kind_checker = ::Module.new { extend Checker::Protocol }
108
+ kind_checker.module_eval("def self.__kind; ::#{params[:kind_name_to_check]}; end")
103
109
 
104
110
  kind_of_mod.instance_eval(KIND_OF % params)
105
111
  kind_of_mod.const_set(method_name, kind_checker)
@@ -108,7 +114,7 @@ module Kind
108
114
 
109
115
  unless kind_is_mod.respond_to?(method_name)
110
116
  kind_is_mod.instance_eval(KIND_IS % params)
111
- kind_is_mod.const_set(method_name, Module.new) if create_kind_is_mod
117
+ kind_is_mod.const_set(method_name, ::Module.new) if create_kind_is_mod
112
118
  end
113
119
  end
114
120
  end
data/lib/kind/dig.rb ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Dig
5
+ extend self
6
+
7
+ def call(data, keys)
8
+ return unless keys.is_a?(::Array)
9
+
10
+ keys.reduce(data) do |memo, key|
11
+ value = get(memo, key)
12
+
13
+ break if KIND.null?(value)
14
+
15
+ value
16
+ end
17
+ end
18
+
19
+ def [](*keys)
20
+ ->(data) { call(data, keys) }
21
+ end
22
+
23
+ private
24
+
25
+ def get(data, key)
26
+ return data[key] if ::Hash === data
27
+
28
+ case data
29
+ when ::Array
30
+ data[key] if key.respond_to?(:to_int)
31
+ when ::OpenStruct
32
+ data[key] if key.respond_to?(:to_sym)
33
+ when ::Struct
34
+ data[key] rescue nil if key.respond_to?(:to_int) || key.respond_to?(:to_sym)
35
+ else
36
+ data.public_send(key) if key.respond_to?(:to_sym) && data.respond_to?(key)
37
+ end
38
+ end
39
+ end
40
+ end
data/lib/kind/empty.rb CHANGED
@@ -1,21 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
-
5
3
  module Kind
6
4
  module Empty
7
5
  SET = ::Set.new.freeze
8
-
9
6
  HASH = {}.freeze
7
+ ARRAY = [].freeze
8
+ STRING = ''.freeze
10
9
 
11
- ARY = [].freeze
12
- ARRAY = ARY
13
-
14
- STR = ''.freeze
15
- STRING = STR
10
+ ARY = ARRAY
11
+ STR = STRING
16
12
  end
17
13
  end
18
14
 
19
- unless defined?(Empty)
20
- Empty = Kind::Empty
21
- end
15
+ Empty = Kind::Empty unless defined?(Empty)
data/lib/kind/error.rb CHANGED
@@ -2,12 +2,8 @@
2
2
 
3
3
  module Kind
4
4
  class Error < TypeError
5
- UNDEFINED_OBJECT = Object.new
6
-
7
- private_constant :UNDEFINED_OBJECT
8
-
9
- def initialize(arg, object = UNDEFINED_OBJECT)
10
- if UNDEFINED_OBJECT == object
5
+ def initialize(arg, object = UNDEFINED)
6
+ if UNDEFINED == object
11
7
  # Will be used when the exception was raised with a message. e.g:
12
8
  # raise Kind::Error, "some message"
13
9
  super(arg)
data/lib/kind/maybe.rb CHANGED
@@ -1,124 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind/dig'
4
+
3
5
  module Kind
4
6
  module Maybe
5
- module Value
6
- def self.none?(value)
7
- value.nil? || Undefined == value
8
- end
9
-
10
- def self.some?(value)
11
- !none?(value)
12
- end
13
- end
14
-
15
- class Result
16
- attr_reader :value
17
-
18
- def initialize(value)
19
- @value = value.kind_of?(Result) ? value.value : value
20
- end
21
-
22
- def value_or(method_name = Undefined, &block)
23
- raise NotImplementedError
24
- end
25
-
26
- def none?
27
- raise NotImplementedError
28
- end
29
-
30
- def some?; !none?; end
31
-
32
- def map(&fn)
33
- raise NotImplementedError
34
- end
35
-
36
- def try(method_name = Undefined, &block)
37
- raise NotImplementedError
38
- end
39
- end
7
+ extend self
40
8
 
41
- class None < Result
42
- INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
9
+ require 'kind/maybe/result'
10
+ require 'kind/maybe/none'
11
+ require 'kind/maybe/some'
12
+ require 'kind/maybe/wrappable'
13
+ require 'kind/maybe/typed'
43
14
 
44
- def value_or(default = Undefined, &block)
45
- raise ArgumentError, INVALID_DEFAULT_ARG if Undefined == default && !block
46
-
47
- Undefined != default ? default : block.call
48
- end
49
-
50
- def none?; true; end
51
-
52
- def map(&fn)
53
- self
54
- end
55
-
56
- alias_method :then, :map
57
-
58
- def try(method_name = Undefined, &block)
59
- Kind.of.Symbol(method_name) if Undefined != method_name
60
-
61
- nil
62
- end
63
-
64
- private_constant :INVALID_DEFAULT_ARG
15
+ def new(value)
16
+ (KIND.null?(value) ? None : Some)
17
+ .new(value)
65
18
  end
66
19
 
67
- NONE_WITH_NIL_VALUE = None.new(nil)
68
- NONE_WITH_UNDEFINED_VALUE = None.new(Undefined)
69
-
70
- private_constant :NONE_WITH_NIL_VALUE, :NONE_WITH_UNDEFINED_VALUE
71
-
72
- class Some < Result
73
- def value_or(default = Undefined, &block)
74
- @value
75
- end
76
-
77
- def none?; false; end
78
-
79
- def map(&fn)
80
- result = fn.call(@value)
20
+ alias_method :[], :new
81
21
 
82
- return result if Maybe::None === result
83
- return NONE_WITH_NIL_VALUE if result.nil?
84
- return NONE_WITH_UNDEFINED_VALUE if Undefined == result
85
-
86
- Some.new(result)
87
- end
88
-
89
- alias_method :then, :map
90
-
91
- def try(method_name = Undefined, *args, &block)
92
- fn = Undefined == method_name ? block : Kind.of.Symbol(method_name).to_proc
93
-
94
- result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
95
-
96
- return result if Maybe::Value.some?(result)
97
- end
98
- end
99
-
100
- def self.new(value)
101
- result_type = Maybe::Value.none?(value) ? None : Some
102
- result_type.new(value)
103
- end
104
-
105
- def self.[](value);
106
- new(value)
107
- end
108
-
109
- def self.none
110
- NONE_WITH_NIL_VALUE
111
- end
112
-
113
- VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
114
-
115
- private_constant :VALUE_CANT_BE_NONE
116
-
117
- def self.some(value)
118
- return Maybe::Some.new(value) if Value.some?(value)
119
-
120
- raise ArgumentError, VALUE_CANT_BE_NONE
121
- end
22
+ extend Wrappable
122
23
  end
123
24
 
124
25
  Optional = Maybe
@@ -126,10 +27,18 @@ module Kind
126
27
  None = Maybe.none
127
28
 
128
29
  def self.None
129
- Kind::None
30
+ None
130
31
  end
131
32
 
132
33
  def self.Some(value)
133
34
  Maybe.some(value)
134
35
  end
36
+
37
+ def self.Maybe(kind)
38
+ Maybe::Typed.new(kind)
39
+ end
40
+
41
+ def self.Optional(kind)
42
+ Maybe::Typed.new(kind)
43
+ end
135
44
  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::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 none
48
+ NONE_WITH_NIL_VALUE
49
+ end
50
+
51
+ def __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?(::Kind::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::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 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