kind 2.3.0 → 4.1.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 +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