kind 2.3.0 → 4.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 +37 -12
  4. data/.travis.yml +6 -3
  5. data/CHANGELOG.md +1267 -0
  6. data/Gemfile +10 -2
  7. data/README.md +1005 -489
  8. data/lib/kind.rb +53 -281
  9. data/lib/kind/active_model/kind_validator.rb +20 -13
  10. data/lib/kind/core.rb +10 -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/core/wrong_number_of_args.rb +9 -0
  15. data/lib/kind/deprecations/built_in_type_checkers.rb +23 -0
  16. data/lib/kind/{checker.rb → deprecations/checker.rb} +3 -2
  17. data/lib/kind/{checker → deprecations/checker}/factory.rb +1 -5
  18. data/lib/kind/{checker → deprecations/checker}/protocol.rb +3 -3
  19. data/lib/kind/deprecations/is.rb +35 -0
  20. data/lib/kind/deprecations/of.rb +258 -0
  21. data/lib/kind/{types.rb → deprecations/types.rb} +14 -8
  22. data/lib/kind/dig.rb +40 -0
  23. data/lib/kind/empty.rb +5 -11
  24. data/lib/kind/error.rb +2 -6
  25. data/lib/kind/maybe.rb +14 -130
  26. data/lib/kind/maybe/none.rb +57 -0
  27. data/lib/kind/maybe/result.rb +51 -0
  28. data/lib/kind/maybe/some.rb +90 -0
  29. data/lib/kind/maybe/typed.rb +29 -0
  30. data/lib/kind/maybe/wrappable.rb +33 -0
  31. data/lib/kind/presence.rb +33 -0
  32. data/lib/kind/try.rb +34 -0
  33. data/lib/kind/type_checker.rb +87 -0
  34. data/lib/kind/type_checkers.rb +30 -0
  35. data/lib/kind/type_checkers/core/array.rb +17 -0
  36. data/lib/kind/type_checkers/core/class.rb +13 -0
  37. data/lib/kind/type_checkers/core/comparable.rb +13 -0
  38. data/lib/kind/type_checkers/core/enumerable.rb +13 -0
  39. data/lib/kind/type_checkers/core/enumerator.rb +13 -0
  40. data/lib/kind/type_checkers/core/file.rb +13 -0
  41. data/lib/kind/type_checkers/core/float.rb +13 -0
  42. data/lib/kind/type_checkers/core/hash.rb +17 -0
  43. data/lib/kind/type_checkers/core/integer.rb +13 -0
  44. data/lib/kind/type_checkers/core/io.rb +13 -0
  45. data/lib/kind/type_checkers/core/method.rb +13 -0
  46. data/lib/kind/type_checkers/core/module.rb +17 -0
  47. data/lib/kind/type_checkers/core/numeric.rb +13 -0
  48. data/lib/kind/type_checkers/core/proc.rb +13 -0
  49. data/lib/kind/type_checkers/core/queue.rb +14 -0
  50. data/lib/kind/type_checkers/core/range.rb +13 -0
  51. data/lib/kind/type_checkers/core/regexp.rb +13 -0
  52. data/lib/kind/type_checkers/core/string.rb +17 -0
  53. data/lib/kind/type_checkers/core/struct.rb +13 -0
  54. data/lib/kind/type_checkers/core/symbol.rb +13 -0
  55. data/lib/kind/type_checkers/core/time.rb +13 -0
  56. data/lib/kind/type_checkers/custom/boolean.rb +19 -0
  57. data/lib/kind/type_checkers/custom/callable.rb +19 -0
  58. data/lib/kind/type_checkers/custom/lambda.rb +19 -0
  59. data/lib/kind/type_checkers/stdlib/open_struct.rb +13 -0
  60. data/lib/kind/type_checkers/stdlib/set.rb +17 -0
  61. data/lib/kind/undefined.rb +4 -2
  62. data/lib/kind/validator.rb +1 -1
  63. data/lib/kind/version.rb +1 -1
  64. data/test.sh +4 -4
  65. metadata +53 -9
  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)
@@ -99,7 +105,7 @@ module Kind
99
105
  params[:kind_name_to_check] ||= kind_name
100
106
 
101
107
  kind_checker = ::Module.new { extend Checker::Protocol }
102
- kind_checker.module_eval("def self.__kind; #{params[:kind_name_to_check]}; end")
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.kind_of?(::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,141 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind/dig'
4
+
3
5
  module Kind
4
6
  module Maybe
5
- class Typed
6
- def initialize(kind)
7
- @kind_checker = Kind::Checker::Factory.create(kind)
8
- end
9
-
10
- def wrap(value)
11
- @kind_checker.as_maybe(value)
12
- end
13
-
14
- alias_method :new, :wrap
15
- alias_method :[], :wrap
16
- end
17
-
18
- module Value
19
- def self.none?(value)
20
- value.nil? || Undefined == value
21
- end
22
-
23
- def self.some?(value)
24
- !none?(value)
25
- end
26
- end
27
-
28
- class Result
29
- attr_reader :value
30
-
31
- def initialize(value)
32
- @value = value.kind_of?(Result) ? value.value : value
33
- end
34
-
35
- def value_or(method_name = Undefined, &block)
36
- raise NotImplementedError
37
- end
38
-
39
- def none?
40
- raise NotImplementedError
41
- end
42
-
43
- def some?; !none?; end
7
+ extend self
44
8
 
45
- def map(&fn)
46
- raise NotImplementedError
47
- end
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'
48
14
 
49
- def try(method_name = Undefined, &block)
50
- raise NotImplementedError
51
- end
15
+ def new(value)
16
+ (KIND.null?(value) ? None : Some)
17
+ .new(value)
52
18
  end
53
19
 
54
- class None < Result
55
- INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
56
-
57
- def value_or(default = Undefined, &block)
58
- raise ArgumentError, INVALID_DEFAULT_ARG if Undefined == default && !block
59
-
60
- Undefined != default ? default : block.call
61
- end
62
-
63
- def none?; true; end
64
-
65
- def map(&fn)
66
- self
67
- end
68
-
69
- alias_method :then, :map
70
-
71
- def try(method_name = Undefined, &block)
72
- Kind.of.Symbol(method_name) if Undefined != method_name
73
-
74
- nil
75
- end
76
-
77
- private_constant :INVALID_DEFAULT_ARG
78
- end
79
-
80
- NONE_WITH_NIL_VALUE = None.new(nil)
81
- NONE_WITH_UNDEFINED_VALUE = None.new(Undefined)
82
-
83
- private_constant :NONE_WITH_NIL_VALUE, :NONE_WITH_UNDEFINED_VALUE
20
+ alias_method :[], :new
84
21
 
85
- class Some < Result
86
- def value_or(default = Undefined, &block)
87
- @value
88
- end
89
-
90
- def none?; false; end
91
-
92
- def map(&fn)
93
- result = fn.call(@value)
94
-
95
- return result if Maybe::None === result
96
- return NONE_WITH_NIL_VALUE if result.nil?
97
- return NONE_WITH_UNDEFINED_VALUE if Undefined == result
98
-
99
- Some.new(result)
100
- end
101
-
102
- alias_method :then, :map
103
-
104
- def try(method_name = Undefined, *args, &block)
105
- fn = Undefined == method_name ? block : Kind.of.Symbol(method_name).to_proc
106
-
107
- result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
108
-
109
- return result if Maybe::Value.some?(result)
110
- end
111
- end
112
-
113
- def self.new(value)
114
- result_type = Maybe::Value.none?(value) ? None : Some
115
- result_type.new(value)
116
- end
117
-
118
- def self.[](value)
119
- new(value)
120
- end
121
-
122
- def self.wrap(value)
123
- new(value)
124
- end
125
-
126
- def self.none
127
- NONE_WITH_NIL_VALUE
128
- end
129
-
130
- VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
131
-
132
- private_constant :VALUE_CANT_BE_NONE
133
-
134
- def self.some(value)
135
- return Maybe::Some.new(value) if Value.some?(value)
136
-
137
- raise ArgumentError, VALUE_CANT_BE_NONE
138
- end
22
+ extend Wrappable
139
23
  end
140
24
 
141
25
  Optional = Maybe
@@ -143,7 +27,7 @@ module Kind
143
27
  None = Maybe.none
144
28
 
145
29
  def self.None
146
- Kind::None
30
+ None
147
31
  end
148
32
 
149
33
  def self.Some(value)
@@ -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 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?(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 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