kind 2.2.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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