kind 3.0.1 → 5.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 +42 -12
  4. data/.travis.yml +7 -5
  5. data/CHANGELOG.md +1309 -0
  6. data/Gemfile +22 -7
  7. data/README.md +990 -490
  8. data/kind.gemspec +1 -1
  9. data/lib/kind.rb +29 -292
  10. data/lib/kind/active_model/validation.rb +3 -4
  11. data/lib/kind/core.rb +15 -0
  12. data/lib/kind/core/dig.rb +40 -0
  13. data/lib/kind/core/empty.rb +13 -0
  14. data/lib/kind/core/empty/constant.rb +7 -0
  15. data/lib/kind/{error.rb → core/error.rb} +2 -6
  16. data/lib/kind/core/maybe.rb +42 -0
  17. data/lib/kind/core/maybe/none.rb +57 -0
  18. data/lib/kind/core/maybe/result.rb +51 -0
  19. data/lib/kind/core/maybe/some.rb +90 -0
  20. data/lib/kind/core/maybe/typed.rb +29 -0
  21. data/lib/kind/core/maybe/wrappable.rb +33 -0
  22. data/lib/kind/core/presence.rb +33 -0
  23. data/lib/kind/core/try.rb +34 -0
  24. data/lib/kind/core/type_checker.rb +87 -0
  25. data/lib/kind/core/type_checkers.rb +30 -0
  26. data/lib/kind/core/type_checkers/custom/boolean.rb +19 -0
  27. data/lib/kind/core/type_checkers/custom/callable.rb +19 -0
  28. data/lib/kind/core/type_checkers/custom/lambda.rb +19 -0
  29. data/lib/kind/core/type_checkers/ruby/array.rb +17 -0
  30. data/lib/kind/core/type_checkers/ruby/class.rb +13 -0
  31. data/lib/kind/core/type_checkers/ruby/comparable.rb +13 -0
  32. data/lib/kind/core/type_checkers/ruby/enumerable.rb +13 -0
  33. data/lib/kind/core/type_checkers/ruby/enumerator.rb +13 -0
  34. data/lib/kind/core/type_checkers/ruby/file.rb +13 -0
  35. data/lib/kind/core/type_checkers/ruby/float.rb +13 -0
  36. data/lib/kind/core/type_checkers/ruby/hash.rb +17 -0
  37. data/lib/kind/core/type_checkers/ruby/integer.rb +13 -0
  38. data/lib/kind/core/type_checkers/ruby/io.rb +13 -0
  39. data/lib/kind/core/type_checkers/ruby/method.rb +13 -0
  40. data/lib/kind/core/type_checkers/ruby/module.rb +17 -0
  41. data/lib/kind/core/type_checkers/ruby/numeric.rb +13 -0
  42. data/lib/kind/core/type_checkers/ruby/proc.rb +13 -0
  43. data/lib/kind/core/type_checkers/ruby/queue.rb +14 -0
  44. data/lib/kind/core/type_checkers/ruby/range.rb +13 -0
  45. data/lib/kind/core/type_checkers/ruby/regexp.rb +13 -0
  46. data/lib/kind/core/type_checkers/ruby/string.rb +17 -0
  47. data/lib/kind/core/type_checkers/ruby/struct.rb +13 -0
  48. data/lib/kind/core/type_checkers/ruby/symbol.rb +13 -0
  49. data/lib/kind/core/type_checkers/ruby/time.rb +13 -0
  50. data/lib/kind/core/type_checkers/stdlib/open_struct.rb +13 -0
  51. data/lib/kind/core/type_checkers/stdlib/set.rb +17 -0
  52. data/lib/kind/{undefined.rb → core/undefined.rb} +4 -2
  53. data/lib/kind/core/utils/kind.rb +61 -0
  54. data/lib/kind/core/utils/undefined.rb +7 -0
  55. data/lib/kind/validator.rb +108 -1
  56. data/lib/kind/version.rb +1 -1
  57. data/test.sh +4 -4
  58. metadata +50 -15
  59. data/lib/kind/active_model/kind_validator.rb +0 -96
  60. data/lib/kind/checker.rb +0 -15
  61. data/lib/kind/checker/factory.rb +0 -35
  62. data/lib/kind/checker/protocol.rb +0 -73
  63. data/lib/kind/empty.rb +0 -21
  64. data/lib/kind/is.rb +0 -19
  65. data/lib/kind/maybe.rb +0 -183
  66. data/lib/kind/of.rb +0 -11
  67. data/lib/kind/types.rb +0 -115
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  module Kind
4
3
  Undefined = Object.new.tap do |undefined|
5
4
  def undefined.inspect
@@ -19,9 +18,12 @@ module Kind
19
18
  end
20
19
 
21
20
  def undefined.default(value, default)
22
- return self if self != value
21
+ return value if self != value
23
22
 
24
23
  default.respond_to?(:call) ? default.call : default
25
24
  end
25
+
26
+ undefined.inspect
27
+ undefined.freeze
26
28
  end
27
29
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module KIND
5
+ def self.null?(value) # :nodoc:
6
+ value.nil? || Undefined == value
7
+ end
8
+
9
+ def self.of?(kind, values) # :nodoc:
10
+ of_kind = -> value { kind === value }
11
+
12
+ values.empty? ? of_kind : values.all?(&of_kind)
13
+ end
14
+
15
+ def self.of!(kind, value, kind_name = nil) # :nodoc:
16
+ return value if kind === value
17
+
18
+ error!(kind_name || kind.name, value)
19
+ end
20
+
21
+ def self.error!(kind_name, value) # :nodoc:
22
+ raise Error.new(kind_name, value)
23
+ end
24
+
25
+ def self.of_class?(value) # :nodoc:
26
+ value.kind_of?(::Class)
27
+ end
28
+
29
+ def self.of_module?(value) # :nodoc:
30
+ ::Module == value || (value.kind_of?(::Module) && !of_class?(value))
31
+ end
32
+
33
+ def self.of_module_or_class!(value) # :nodoc:
34
+ of!(::Module, value, 'Module/Class')
35
+ end
36
+
37
+ def self.respond_to!(method_name, value) # :nodoc:
38
+ return value if value.respond_to?(method_name)
39
+
40
+ raise Error.new("expected #{value} to respond to :#{method_name}")
41
+ end
42
+
43
+ def self.is?(expected, value) # :nodoc:
44
+ is!(of_module_or_class!(expected), value)
45
+ end
46
+
47
+ def self.is!(expected_kind, value) # :nodoc:
48
+ kind = of_module_or_class!(value)
49
+
50
+ if of_class?(kind)
51
+ kind <= expected_kind || expected_kind == ::Class
52
+ else
53
+ kind == expected_kind || kind.kind_of?(expected_kind)
54
+ end
55
+ end
56
+
57
+ def self.value(kind, arg, default) # :nodoc:
58
+ kind === arg ? arg : default
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ UNDEFINED = Object.new.freeze
5
+
6
+ private_constant :UNDEFINED
7
+ end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind'
4
+
3
5
  module Kind
4
6
  module Validator
5
- DEFAULT_STRATEGIES = Set.new(%w[instance_of kind_of]).freeze
7
+ DEFAULT_STRATEGIES = ::Set.new(%w[instance_of kind_of]).freeze
6
8
 
7
9
  class InvalidDefinition < ArgumentError
8
10
  OPTIONS = 'Options to define one: :of, :is, :respond_to, :instance_of, :array_of or :array_with'.freeze
@@ -38,3 +40,108 @@ module Kind
38
40
  end
39
41
  end
40
42
  end
43
+
44
+ require 'active_model'
45
+ require 'active_model/validations'
46
+
47
+ class KindValidator < ActiveModel::EachValidator
48
+ def validate_each(record, attribute, value)
49
+ return if options[:allow_nil] && value.nil?
50
+
51
+ return unless error = call_validation_for(attribute, value)
52
+
53
+ raise Kind::Error.new("#{attribute} #{error}") if options[:strict]
54
+
55
+ record.errors.add(attribute, error)
56
+ end
57
+
58
+ private
59
+
60
+ def call_validation_for(attribute, value)
61
+ expected = options[:with] || options[:in]
62
+
63
+ return validate_with_default_strategy(expected, value) if expected
64
+
65
+ return kind_of(expected, value) if expected = options[:of]
66
+ return kind_is(expected, value) if expected = options[:is]
67
+ return respond_to(expected, value) if expected = options[:respond_to]
68
+ return instance_of(expected, value) if expected = options[:instance_of]
69
+ return array_with(expected, value) if expected = options[:array_with]
70
+ return array_of(expected, value) if expected = options[:array_of]
71
+
72
+ raise Kind::Validator::InvalidDefinition.new(attribute)
73
+ end
74
+
75
+ def validate_with_default_strategy(expected, value)
76
+ send(Kind::Validator.default_strategy, expected, value)
77
+ end
78
+
79
+ def kind_of(expected, value)
80
+ types = Array(expected)
81
+
82
+ return if types.any? { |type| type === value }
83
+
84
+ "must be a kind of: #{types.map { |type| type.name }.join(', ')}"
85
+ end
86
+
87
+ CLASS_OR_MODULE = 'Class/Module'.freeze
88
+
89
+ def kind_is(expected, value)
90
+ return kind_is_not(expected, value) unless expected.kind_of?(::Array)
91
+
92
+ result = expected.map { |kind| kind_is_not(kind, value) }.tap(&:compact!)
93
+
94
+ result.empty? || result.size < expected.size ? nil : result.join(', ')
95
+ end
96
+
97
+ def kind_is_not(expected, value)
98
+ case expected
99
+ when ::Class
100
+ return if expected == Kind::Class[value] || value < expected
101
+
102
+ "must be the class or a subclass of `#{expected.name}`"
103
+ when ::Module
104
+ return if value.kind_of?(Class) && value <= expected
105
+ return if expected == Kind.of_module_or_class(value) || value.kind_of?(expected)
106
+
107
+ "must include the `#{expected.name}` module"
108
+ else
109
+ raise Kind::Error.new(CLASS_OR_MODULE, expected)
110
+ end
111
+ end
112
+
113
+ def respond_to(expected, value)
114
+ method_names = Array(expected)
115
+
116
+ expected_methods = method_names.select { |method_name| !value.respond_to?(method_name) }
117
+ expected_methods.map! { |method_name| "`#{method_name}`" }
118
+
119
+ return if expected_methods.empty?
120
+
121
+ methods = expected_methods.size == 1 ? 'method' : 'methods'
122
+
123
+ "must respond to the #{methods}: #{expected_methods.join(', ')}"
124
+ end
125
+
126
+ def instance_of(expected, value)
127
+ types = Array(expected)
128
+
129
+ return if types.any? { |type| value.instance_of?(type) }
130
+
131
+ "must be an instance of: #{types.map { |klass| klass.name }.join(', ')}"
132
+ end
133
+
134
+ def array_with(expected, value)
135
+ return if value.kind_of?(::Array) && !value.empty? && (value - Kind::Array[expected]).empty?
136
+
137
+ "must be an array with: #{expected.join(', ')}"
138
+ end
139
+
140
+ def array_of(expected, value)
141
+ types = Array(expected)
142
+
143
+ return if value.kind_of?(::Array) && !value.empty? && value.all? { |val| types.any? { |type| type === val } }
144
+
145
+ "must be an array of: #{types.map { |klass| klass.name }.join(', ')}"
146
+ end
147
+ end
data/lib/kind/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kind
4
- VERSION = '3.0.1'
4
+ VERSION = '5.1.0'
5
5
  end
data/test.sh CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/bin/bash
2
2
 
3
- bundle
4
-
5
- rm Gemfile.lock
6
-
7
3
  source $(dirname $0)/.travis.sh
8
4
 
5
+ echo ''
6
+ echo 'Resetting Gemfile'
7
+ echo ''
8
+
9
9
  rm Gemfile.lock
10
10
 
11
11
  bundle
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kind
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-25 00:00:00.000000000 Z
11
+ date: 2021-02-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A simple type system (at runtime) for Ruby - free of dependencies.
14
14
  email:
@@ -18,8 +18,10 @@ extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
20
  - ".gitignore"
21
+ - ".tool-versions"
21
22
  - ".travis.sh"
22
23
  - ".travis.yml"
24
+ - CHANGELOG.md
23
25
  - CODE_OF_CONDUCT.md
24
26
  - Gemfile
25
27
  - LICENSE.txt
@@ -29,18 +31,51 @@ files:
29
31
  - bin/setup
30
32
  - kind.gemspec
31
33
  - lib/kind.rb
32
- - lib/kind/active_model/kind_validator.rb
33
34
  - lib/kind/active_model/validation.rb
34
- - lib/kind/checker.rb
35
- - lib/kind/checker/factory.rb
36
- - lib/kind/checker/protocol.rb
37
- - lib/kind/empty.rb
38
- - lib/kind/error.rb
39
- - lib/kind/is.rb
40
- - lib/kind/maybe.rb
41
- - lib/kind/of.rb
42
- - lib/kind/types.rb
43
- - lib/kind/undefined.rb
35
+ - lib/kind/core.rb
36
+ - lib/kind/core/dig.rb
37
+ - lib/kind/core/empty.rb
38
+ - lib/kind/core/empty/constant.rb
39
+ - lib/kind/core/error.rb
40
+ - lib/kind/core/maybe.rb
41
+ - lib/kind/core/maybe/none.rb
42
+ - lib/kind/core/maybe/result.rb
43
+ - lib/kind/core/maybe/some.rb
44
+ - lib/kind/core/maybe/typed.rb
45
+ - lib/kind/core/maybe/wrappable.rb
46
+ - lib/kind/core/presence.rb
47
+ - lib/kind/core/try.rb
48
+ - lib/kind/core/type_checker.rb
49
+ - lib/kind/core/type_checkers.rb
50
+ - lib/kind/core/type_checkers/custom/boolean.rb
51
+ - lib/kind/core/type_checkers/custom/callable.rb
52
+ - lib/kind/core/type_checkers/custom/lambda.rb
53
+ - lib/kind/core/type_checkers/ruby/array.rb
54
+ - lib/kind/core/type_checkers/ruby/class.rb
55
+ - lib/kind/core/type_checkers/ruby/comparable.rb
56
+ - lib/kind/core/type_checkers/ruby/enumerable.rb
57
+ - lib/kind/core/type_checkers/ruby/enumerator.rb
58
+ - lib/kind/core/type_checkers/ruby/file.rb
59
+ - lib/kind/core/type_checkers/ruby/float.rb
60
+ - lib/kind/core/type_checkers/ruby/hash.rb
61
+ - lib/kind/core/type_checkers/ruby/integer.rb
62
+ - lib/kind/core/type_checkers/ruby/io.rb
63
+ - lib/kind/core/type_checkers/ruby/method.rb
64
+ - lib/kind/core/type_checkers/ruby/module.rb
65
+ - lib/kind/core/type_checkers/ruby/numeric.rb
66
+ - lib/kind/core/type_checkers/ruby/proc.rb
67
+ - lib/kind/core/type_checkers/ruby/queue.rb
68
+ - lib/kind/core/type_checkers/ruby/range.rb
69
+ - lib/kind/core/type_checkers/ruby/regexp.rb
70
+ - lib/kind/core/type_checkers/ruby/string.rb
71
+ - lib/kind/core/type_checkers/ruby/struct.rb
72
+ - lib/kind/core/type_checkers/ruby/symbol.rb
73
+ - lib/kind/core/type_checkers/ruby/time.rb
74
+ - lib/kind/core/type_checkers/stdlib/open_struct.rb
75
+ - lib/kind/core/type_checkers/stdlib/set.rb
76
+ - lib/kind/core/undefined.rb
77
+ - lib/kind/core/utils/kind.rb
78
+ - lib/kind/core/utils/undefined.rb
44
79
  - lib/kind/validator.rb
45
80
  - lib/kind/version.rb
46
81
  - test.sh
@@ -58,14 +93,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
58
93
  requirements:
59
94
  - - ">="
60
95
  - !ruby/object:Gem::Version
61
- version: 2.2.0
96
+ version: 2.1.0
62
97
  required_rubygems_version: !ruby/object:Gem::Requirement
63
98
  requirements:
64
99
  - - ">="
65
100
  - !ruby/object:Gem::Version
66
101
  version: '0'
67
102
  requirements: []
68
- rubygems_version: 3.0.6
103
+ rubygems_version: 3.1.4
69
104
  signing_key:
70
105
  specification_version: 4
71
106
  summary: A simple type system (at runtime) for Ruby.
@@ -1,96 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class KindValidator < ActiveModel::EachValidator
4
- def validate_each(record, attribute, value)
5
- return if options[:allow_nil] && value.nil?
6
-
7
- return unless error = call_validation_for(attribute, value)
8
-
9
- raise Kind::Error.new("#{attribute} #{error}") if options[:strict]
10
-
11
- record.errors.add(attribute, error)
12
- end
13
-
14
- private
15
-
16
- def call_validation_for(attribute, value)
17
- expected = options[:with] || options[:in]
18
-
19
- return validate_with_default_strategy(expected, value) if expected
20
-
21
- return kind_of(expected, value) if expected = options[:of]
22
- return kind_is(expected, value) if expected = options[:is]
23
- return respond_to(expected, value) if expected = options[:respond_to]
24
- return instance_of(expected, value) if expected = options[:instance_of]
25
- return array_with(expected, value) if expected = options[:array_with]
26
- return array_of(expected, value) if expected = options[:array_of]
27
-
28
- raise Kind::Validator::InvalidDefinition.new(attribute)
29
- end
30
-
31
- def validate_with_default_strategy(expected, value)
32
- send(Kind::Validator.default_strategy, expected, value)
33
- end
34
-
35
- def kind_of(expected, value)
36
- types = Array(expected)
37
-
38
- return if types.any? { |type| value.kind_of?(type) }
39
-
40
- "must be a kind of: #{types.map { |klass| klass.name }.join(', ')}"
41
- end
42
-
43
- CLASS_OR_MODULE = 'Class/Module'.freeze
44
-
45
- def kind_is(expected, value)
46
- return kind_is_not(expected, value) unless expected.kind_of?(Array)
47
-
48
- result = expected.map { |kind| kind_is_not(kind, value) }.compact
49
-
50
- result.empty? || result.size < expected.size ? nil : result.join(', ')
51
- end
52
-
53
- def kind_is_not(expected, value)
54
- case expected
55
- when Class
56
- return if expected == Kind.of.Class(value) || value < expected
57
-
58
- "must be the class or a subclass of `#{expected.name}`"
59
- when Module
60
- return if value.kind_of?(Class) && value <= expected
61
- return if expected == Kind.of.Module(value) || value.kind_of?(expected)
62
-
63
- "must include the `#{expected.name}` module"
64
- else
65
- raise Kind::Error.new(CLASS_OR_MODULE, expected)
66
- end
67
- end
68
-
69
- def respond_to(method_name, value)
70
- return if value.respond_to?(method_name)
71
-
72
- "must respond to the method `#{method_name}`"
73
- end
74
-
75
- def instance_of(expected, value)
76
- types = Array(expected)
77
-
78
- return if types.any? { |type| value.instance_of?(type) }
79
-
80
- "must be an instance of: #{types.map { |klass| klass.name }.join(', ')}"
81
- end
82
-
83
- def array_with(expected, value)
84
- return if value.kind_of?(Array) && !value.empty? && (value - Kind.of.Array(expected)).empty?
85
-
86
- "must be an array with: #{expected.join(', ')}"
87
- end
88
-
89
- def array_of(expected, value)
90
- types = Array(expected)
91
-
92
- return if value.kind_of?(Array) && !value.empty? && value.all? { |value| types.any? { |type| value.kind_of?(type) } }
93
-
94
- "must be an array of: #{types.map { |klass| klass.name }.join(', ')}"
95
- end
96
- end
data/lib/kind/checker.rb DELETED
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'kind/checker/factory'
4
- require 'kind/checker/protocol'
5
- module Kind
6
- class Checker
7
- include Protocol
8
-
9
- attr_reader :__kind
10
-
11
- def initialize(kind)
12
- @__kind = kind
13
- end
14
- end
15
- end