kind 4.1.0 → 5.4.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -1
  3. data/.travis.sh +39 -7
  4. data/.travis.yml +1 -2
  5. data/CHANGELOG.md +524 -29
  6. data/Gemfile +13 -6
  7. data/README.md +57 -43
  8. data/kind.gemspec +3 -3
  9. data/lib/kind.rb +2 -84
  10. data/lib/kind/__lib__/action_steps.rb +57 -0
  11. data/lib/kind/__lib__/attributes.rb +66 -0
  12. data/lib/kind/__lib__/kind.rb +51 -0
  13. data/lib/kind/__lib__/of.rb +17 -0
  14. data/lib/kind/__lib__/strict.rb +49 -0
  15. data/lib/kind/__lib__/undefined.rb +14 -0
  16. data/lib/kind/action.rb +127 -0
  17. data/lib/kind/active_model/validation.rb +3 -4
  18. data/lib/kind/basic.rb +79 -0
  19. data/lib/kind/basic/error.rb +29 -0
  20. data/lib/kind/{undefined.rb → basic/undefined.rb} +6 -1
  21. data/lib/kind/dig.rb +21 -5
  22. data/lib/kind/either.rb +30 -0
  23. data/lib/kind/either/left.rb +29 -0
  24. data/lib/kind/either/methods.rb +17 -0
  25. data/lib/kind/either/monad.rb +65 -0
  26. data/lib/kind/either/monad/wrapper.rb +19 -0
  27. data/lib/kind/either/right.rb +38 -0
  28. data/lib/kind/empty.rb +2 -2
  29. data/lib/kind/empty/constant.rb +7 -0
  30. data/lib/kind/enum.rb +63 -0
  31. data/lib/kind/enum/item.rb +40 -0
  32. data/lib/kind/enum/methods.rb +72 -0
  33. data/lib/kind/function.rb +45 -0
  34. data/lib/kind/functional.rb +89 -0
  35. data/lib/kind/functional/action.rb +87 -0
  36. data/lib/kind/functional/steps.rb +22 -0
  37. data/lib/kind/immutable_attributes.rb +34 -0
  38. data/lib/kind/immutable_attributes/initializer.rb +70 -0
  39. data/lib/kind/immutable_attributes/reader.rb +38 -0
  40. data/lib/kind/maybe.rb +37 -12
  41. data/lib/kind/maybe/methods.rb +21 -0
  42. data/lib/kind/maybe/monad.rb +82 -0
  43. data/lib/kind/maybe/monad/wrapper.rb +19 -0
  44. data/lib/kind/maybe/none.rb +12 -19
  45. data/lib/kind/maybe/some.rb +68 -26
  46. data/lib/kind/maybe/typed.rb +11 -5
  47. data/lib/kind/maybe/{wrappable.rb → wrapper.rb} +8 -4
  48. data/lib/kind/monad.rb +22 -0
  49. data/lib/kind/monads.rb +5 -0
  50. data/lib/kind/objects.rb +17 -0
  51. data/lib/kind/objects/basic_object.rb +43 -0
  52. data/lib/kind/objects/modules.rb +32 -0
  53. data/lib/kind/{type_checkers → objects/modules}/core/array.rb +3 -1
  54. data/lib/kind/{type_checkers → objects/modules}/core/class.rb +1 -1
  55. data/lib/kind/{type_checkers → objects/modules}/core/comparable.rb +1 -1
  56. data/lib/kind/{type_checkers → objects/modules}/core/enumerable.rb +1 -1
  57. data/lib/kind/{type_checkers → objects/modules}/core/enumerator.rb +1 -1
  58. data/lib/kind/{type_checkers → objects/modules}/core/file.rb +1 -1
  59. data/lib/kind/{type_checkers → objects/modules}/core/float.rb +1 -1
  60. data/lib/kind/{type_checkers → objects/modules}/core/hash.rb +3 -1
  61. data/lib/kind/{type_checkers → objects/modules}/core/integer.rb +1 -1
  62. data/lib/kind/{type_checkers → objects/modules}/core/io.rb +1 -1
  63. data/lib/kind/{type_checkers → objects/modules}/core/method.rb +1 -1
  64. data/lib/kind/{type_checkers → objects/modules}/core/module.rb +1 -1
  65. data/lib/kind/{type_checkers → objects/modules}/core/numeric.rb +1 -1
  66. data/lib/kind/{type_checkers → objects/modules}/core/proc.rb +1 -1
  67. data/lib/kind/{type_checkers → objects/modules}/core/queue.rb +1 -1
  68. data/lib/kind/{type_checkers → objects/modules}/core/range.rb +1 -1
  69. data/lib/kind/{type_checkers → objects/modules}/core/regexp.rb +1 -1
  70. data/lib/kind/{type_checkers → objects/modules}/core/string.rb +3 -1
  71. data/lib/kind/{type_checkers → objects/modules}/core/struct.rb +1 -1
  72. data/lib/kind/{type_checkers → objects/modules}/core/symbol.rb +1 -1
  73. data/lib/kind/{type_checkers → objects/modules}/core/time.rb +1 -1
  74. data/lib/kind/{type_checkers → objects/modules}/custom/boolean.rb +2 -2
  75. data/lib/kind/{type_checkers → objects/modules}/custom/callable.rb +1 -1
  76. data/lib/kind/{type_checkers → objects/modules}/custom/lambda.rb +1 -1
  77. data/lib/kind/{type_checkers → objects/modules}/stdlib/open_struct.rb +3 -1
  78. data/lib/kind/{type_checkers → objects/modules}/stdlib/set.rb +3 -1
  79. data/lib/kind/objects/nil.rb +17 -0
  80. data/lib/kind/objects/not_nil.rb +9 -0
  81. data/lib/kind/objects/object.rb +56 -0
  82. data/lib/kind/objects/respond_to.rb +30 -0
  83. data/lib/kind/objects/union_type.rb +44 -0
  84. data/lib/kind/presence.rb +4 -2
  85. data/lib/kind/result.rb +31 -0
  86. data/lib/kind/result/abstract.rb +53 -0
  87. data/lib/kind/result/failure.rb +33 -0
  88. data/lib/kind/result/methods.rb +17 -0
  89. data/lib/kind/result/monad.rb +74 -0
  90. data/lib/kind/result/monad/wrapper.rb +19 -0
  91. data/lib/kind/result/success.rb +53 -0
  92. data/lib/kind/strict/disabled.rb +34 -0
  93. data/lib/kind/try.rb +22 -10
  94. data/lib/kind/validator.rb +111 -0
  95. data/lib/kind/version.rb +1 -1
  96. metadata +84 -51
  97. data/lib/kind/active_model/kind_validator.rb +0 -103
  98. data/lib/kind/core.rb +0 -10
  99. data/lib/kind/core/deprecation.rb +0 -29
  100. data/lib/kind/core/kind.rb +0 -61
  101. data/lib/kind/core/undefined.rb +0 -7
  102. data/lib/kind/core/wrong_number_of_args.rb +0 -9
  103. data/lib/kind/deprecations/built_in_type_checkers.rb +0 -23
  104. data/lib/kind/deprecations/checker.rb +0 -16
  105. data/lib/kind/deprecations/checker/factory.rb +0 -31
  106. data/lib/kind/deprecations/checker/protocol.rb +0 -73
  107. data/lib/kind/deprecations/is.rb +0 -35
  108. data/lib/kind/deprecations/of.rb +0 -258
  109. data/lib/kind/deprecations/types.rb +0 -121
  110. data/lib/kind/error.rb +0 -15
  111. data/lib/kind/maybe/result.rb +0 -51
  112. data/lib/kind/type_checker.rb +0 -87
  113. data/lib/kind/type_checkers.rb +0 -30
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Kind
3
4
  Undefined = Object.new.tap do |undefined|
4
5
  def undefined.inspect
5
6
  @inspect ||= 'Kind::Undefined'.freeze
6
7
  end
8
+ undefined.inspect
9
+
10
+ def undefined.empty?
11
+ true
12
+ end
7
13
 
8
14
  def undefined.to_s
9
15
  inspect
@@ -23,7 +29,6 @@ module Kind
23
29
  default.respond_to?(:call) ? default.call : default
24
30
  end
25
31
 
26
- undefined.inspect
27
32
  undefined.freeze
28
33
  end
29
34
  end
data/lib/kind/dig.rb CHANGED
@@ -1,23 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind/basic'
4
+ require 'kind/empty'
5
+ require 'kind/presence'
6
+
3
7
  module Kind
4
8
  module Dig
5
9
  extend self
6
10
 
7
- def call(data, keys)
8
- return unless keys.kind_of?(::Array)
9
-
11
+ def call!(data, keys = Empty::ARRAY) # :nodoc
10
12
  keys.reduce(data) do |memo, key|
11
13
  value = get(memo, key)
12
14
 
13
- break if KIND.null?(value)
15
+ break if KIND.nil_or_undefined?(value)
14
16
 
15
17
  value
16
18
  end
17
19
  end
18
20
 
21
+ def call(data, *input)
22
+ args = input.size == 1 && input[0].kind_of?(::Array) ? input[0] : input
23
+
24
+ result = call!(data, args)
25
+
26
+ return result unless block_given?
27
+
28
+ yield(result) unless KIND.nil_or_undefined?(result)
29
+ end
30
+
31
+ def presence(*args, &block)
32
+ Presence.(call(*args, &block))
33
+ end
34
+
19
35
  def [](*keys)
20
- ->(data) { call(data, keys) }
36
+ ->(data) { call!(data, keys) }
21
37
  end
22
38
 
23
39
  private
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+
5
+ module Kind
6
+ module Either
7
+ require 'kind/either/monad'
8
+ require 'kind/either/left'
9
+ require 'kind/either/right'
10
+ require 'kind/either/methods'
11
+
12
+ extend self
13
+
14
+ def new(value)
15
+ Right[value]
16
+ end
17
+
18
+ alias_method :[], :new
19
+
20
+ def self.from
21
+ result = yield
22
+
23
+ Either::Monad === result ? result : Either::Right[result]
24
+ rescue StandardError => e
25
+ Either::Left[e]
26
+ end
27
+ end
28
+
29
+ extend Either::Methods
30
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Either::Left < Either::Monad
5
+ def left?
6
+ true
7
+ end
8
+
9
+ def value_or(default = UNDEFINED, &block)
10
+ Error.invalid_default_arg! if UNDEFINED == default && !block
11
+
12
+ UNDEFINED != default ? default : block.call
13
+ end
14
+
15
+ def map(&_)
16
+ self
17
+ end
18
+
19
+ alias_method :map!, :map
20
+ alias_method :then, :map
21
+ alias_method :then!, :map
22
+ alias_method :and_then, :map
23
+ alias_method :and_then!, :map
24
+
25
+ def inspect
26
+ '#<%s value=%p>' % ['Kind::Left', value]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Either::Methods
5
+ def Left(value)
6
+ Either::Left[value]
7
+ end
8
+
9
+ def Right(value)
10
+ Either::Right[value]
11
+ end
12
+
13
+ def self.included(base)
14
+ base.send(:private, :Left, :Right)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Either::Monad
5
+ require 'kind/either/monad/wrapper'
6
+
7
+ attr_reader :value
8
+
9
+ singleton_class.send(:alias_method, :[], :new)
10
+
11
+ def initialize(value)
12
+ @value = value
13
+ end
14
+
15
+ def left?
16
+ false
17
+ end
18
+
19
+ def right?
20
+ false
21
+ end
22
+
23
+ def value_or(_method_name = UNDEFINED, &block)
24
+ raise NotImplementedError
25
+ end
26
+
27
+ def map(&_)
28
+ raise NotImplementedError
29
+ end
30
+
31
+ alias_method :map!, :map
32
+ alias_method :then, :map
33
+ alias_method :then!, :map
34
+ alias_method :and_then, :map
35
+ alias_method :and_then!, :map
36
+
37
+ def on
38
+ monad = Wrapper.new(self)
39
+
40
+ yield(monad)
41
+
42
+ monad.output
43
+ end
44
+
45
+ def on_right(matcher = UNDEFINED)
46
+ yield(value) if right? && either?(matcher)
47
+
48
+ self
49
+ end
50
+
51
+ def on_left(matcher = UNDEFINED)
52
+ yield(value) if left? && either?(matcher)
53
+
54
+ self
55
+ end
56
+
57
+ def either?(matcher)
58
+ UNDEFINED == matcher || matcher === value
59
+ end
60
+
61
+ def ===(monad)
62
+ self.class === monad && self.value === monad.value
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ require 'kind/monad'
5
+
6
+ class Either::Monad::Wrapper < Kind::Monad::Wrapper
7
+ def left(matcher = UNDEFINED)
8
+ return if @monad.right? || output?
9
+
10
+ @output = yield(@monad.value) if @monad.either?(matcher)
11
+ end
12
+
13
+ def right(matcher = UNDEFINED)
14
+ return if @monad.left? || output?
15
+
16
+ @output = yield(@monad.value) if @monad.either?(matcher)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Either::Right < Either::Monad
5
+ def right?
6
+ true
7
+ end
8
+
9
+ def value_or(_default = UNDEFINED, &block)
10
+ @value
11
+ end
12
+
13
+ def map(&fn)
14
+ map!(&fn)
15
+ rescue Kind::Monad::Error => e
16
+ raise e
17
+ rescue StandardError => e
18
+ Either::Left[e]
19
+ end
20
+
21
+ def map!(&fn)
22
+ monad = fn.call(@value)
23
+
24
+ return monad if Either::Monad === monad
25
+
26
+ raise Kind::Monad::Error.new('Kind::Right | Kind::Left', monad)
27
+ end
28
+
29
+ alias_method :then, :map
30
+ alias_method :then!, :map!
31
+ alias_method :and_then, :map
32
+ alias_method :and_then!, :map!
33
+
34
+ def inspect
35
+ '#<%s value=%p>' % ['Kind::Right', value]
36
+ end
37
+ end
38
+ end
data/lib/kind/empty.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  module Kind
4
6
  module Empty
5
7
  SET = ::Set.new.freeze
@@ -11,5 +13,3 @@ module Kind
11
13
  STR = STRING
12
14
  end
13
15
  end
14
-
15
- Empty = Kind::Empty unless defined?(Empty)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(Empty)
4
+ raise LoadError, "already initialized constant Empty"
5
+ else
6
+ Empty = Kind::Empty
7
+ end
data/lib/kind/enum.rb ADDED
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'kind/basic'
5
+
6
+ module Kind
7
+ module Enum
8
+ require 'kind/enum/item'
9
+ require 'kind/enum/methods'
10
+
11
+ extend self
12
+
13
+ def values(input)
14
+ enum_module = ::Module.new
15
+
16
+ enum_items =
17
+ case input
18
+ when ::Hash then create_from_hash(input)
19
+ when ::Array then create_from_array(input)
20
+ else raise ArgumentError, 'use an array or hash to define a Kind::Enum'
21
+ end
22
+
23
+ enum_items.each { |item| enum_module.const_set(item.name, item) }
24
+
25
+ enum_map = enum_items.each_with_object({}) do |item, memo|
26
+ memo[item.to_s] = item
27
+ memo[item.value] = item
28
+ memo[item.to_sym] = item
29
+ end
30
+
31
+ enum_module.const_set(:ENUM__MAP, enum_map)
32
+ enum_module.const_set(:ENUM__HASH, enum_items.map(&:to_ary).to_h.freeze)
33
+ enum_module.const_set(:ENUM__KEYS, ::Set.new(enum_items.map(&:key)).freeze)
34
+ enum_module.const_set(:ENUM__VALS, ::Set.new(enum_items.map(&:value)).freeze)
35
+ enum_module.const_set(:ENUM__REFS, ::Set.new(enum_map.keys))
36
+ enum_module.const_set(:ENUM__ITEMS, enum_items.freeze)
37
+
38
+ enum_module.send(:private_constant, :ENUM__MAP, :ENUM__HASH, :ENUM__KEYS,
39
+ :ENUM__VALS, :ENUM__REFS, :ENUM__ITEMS)
40
+
41
+ enum_module.module_eval(METHODS)
42
+
43
+ enum_module.extend(enum_module)
44
+ enum_module
45
+ end
46
+
47
+ private
48
+
49
+ def create_from_hash(input)
50
+ input.map { |key, value| build_item(key, value) }
51
+ end
52
+
53
+ def create_from_array(input)
54
+ input.map.with_index { |key, index| build_item(key, index) }
55
+ end
56
+
57
+ def build_item(key, value)
58
+ return Item.new(key, value) if key.respond_to?(:to_sym)
59
+
60
+ raise ArgumentError, 'use a string or symbol to define a Kind::Enum item'
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Enum
5
+ class Item
6
+ Underscore = ->(arg) do
7
+ str = String(arg).strip
8
+ str.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
9
+ str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
10
+ str.tr!("-", "_")
11
+ str.downcase!
12
+ str
13
+ end
14
+
15
+ attr_reader :value, :to_s, :name, :to_sym, :inspect
16
+
17
+ alias_method :key, :to_s
18
+ alias_method :to_str, :to_s
19
+
20
+ def initialize(key, val)
21
+ @value = val.frozen? ? val : val.dup.freeze
22
+
23
+ @to_s = Kind.respond_to(key, :to_sym).to_s
24
+ @name = Underscore[key].upcase.freeze
25
+ @to_sym = key.to_sym
26
+ @inspect = ('#<Kind::Enum::Item name=%p to_s=%p value=%p>' % [@name, @to_s, @value]).freeze
27
+ end
28
+
29
+ def ==(arg)
30
+ arg == value || arg == to_s || arg == to_sym
31
+ end
32
+
33
+ def to_ary
34
+ [key, value]
35
+ end
36
+
37
+ alias_method :===, :==
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Enum
5
+ METHODS = \
6
+ <<-RUBY
7
+ def to_h
8
+ ENUM__HASH
9
+ end
10
+
11
+ def items
12
+ ENUM__ITEMS
13
+ end
14
+
15
+ def ===(arg)
16
+ ENUM__ITEMS.any? { |item| item === arg }
17
+ end
18
+
19
+ def refs
20
+ ENUM__REFS.to_a
21
+ end
22
+
23
+ def keys
24
+ ENUM__KEYS.to_a
25
+ end
26
+
27
+ def values
28
+ ENUM__VALS.to_a
29
+ end
30
+
31
+ def ref?(arg)
32
+ ENUM__REFS.include?(arg)
33
+ end
34
+
35
+ def key?(arg)
36
+ arg.respond_to?(:to_sym) ? ref?(arg) && !value?(arg) : false
37
+ end
38
+
39
+ def value?(arg)
40
+ ENUM__VALS.include?(arg)
41
+ end
42
+
43
+ def [](arg)
44
+ return arg if ref?(arg)
45
+
46
+ raise KeyError, "key or value not found: %p" % [arg]
47
+ end
48
+
49
+ def ref(arg)
50
+ arg if ref?(arg)
51
+ end
52
+
53
+ def item(arg)
54
+ ENUM__MAP[arg]
55
+ end
56
+
57
+ def key(arg)
58
+ item(arg).key if value?(arg)
59
+ end
60
+
61
+ def value_at(arg)
62
+ item(arg).value if key?(arg)
63
+ end
64
+
65
+ def self.included(base)
66
+ base.extend(base)
67
+ end
68
+ RUBY
69
+
70
+ private_constant :METHODS
71
+ end
72
+ end