kind 1.8.0 → 2.3.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.
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Kind
6
+ class Checker
7
+ class Factory
8
+ include Singleton
9
+
10
+ def self.create(kind)
11
+ instance.create(kind)
12
+ end
13
+
14
+ def initialize
15
+ @__checkers__ = {}
16
+ end
17
+
18
+ MODULE_OR_CLASS = 'Module/Class'.freeze
19
+
20
+ private_constant :MODULE_OR_CLASS
21
+
22
+ def create(kind)
23
+ @__checkers__[kind] ||= begin
24
+ kind_name = kind.name
25
+
26
+ if Kind::Of.const_defined?(kind_name, false)
27
+ Kind::Of.const_get(kind_name)
28
+ else
29
+ Kind::Checker.new(Kind::Of.(Module, kind, MODULE_OR_CLASS))
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Checker
5
+ module Protocol
6
+ def class?(value)
7
+ Kind::Is.__call__(__kind, value)
8
+ end
9
+
10
+ def instance(value, options = Empty::HASH)
11
+ default = options[:or]
12
+
13
+ return Kind::Of.(__kind, value) if ::Kind::Maybe::Value.none?(default)
14
+
15
+ Kind::Undefined != value && instance?(value) ? value : Kind::Of.(__kind, default)
16
+ end
17
+
18
+ def [](value, options = options = Empty::HASH)
19
+ instance(value, options)
20
+ end
21
+
22
+ def to_proc
23
+ @to_proc ||=
24
+ -> checker { -> value { checker.instance(value) } }.call(self)
25
+ end
26
+
27
+ def __is_instance__(value)
28
+ value.kind_of?(__kind)
29
+ end
30
+
31
+ def is_instance_to_proc
32
+ @is_instance_to_proc ||=
33
+ -> checker { -> value { checker.__is_instance__(value) } }.call(self)
34
+ end
35
+
36
+ def instance?(*args)
37
+ return is_instance_to_proc if args.empty?
38
+
39
+ return args.all? { |object| __is_instance__(object) } if args.size > 1
40
+
41
+ arg = args[0]
42
+ Kind::Undefined == arg ? is_instance_to_proc : __is_instance__(arg)
43
+ end
44
+
45
+ def or_nil(value)
46
+ return value if instance?(value)
47
+ end
48
+
49
+ def or_undefined(value)
50
+ or_nil(value) || Kind::Undefined
51
+ end
52
+
53
+ def __as_maybe__(value)
54
+ Kind::Maybe.new(or_nil(value))
55
+ end
56
+
57
+ def as_maybe_to_proc
58
+ @as_maybe_to_proc ||=
59
+ -> checker { -> value { checker.__as_maybe__(value) } }.call(self)
60
+ end
61
+
62
+ def as_maybe(value = Kind::Undefined)
63
+ return __as_maybe__(value) if Kind::Undefined != value
64
+
65
+ as_maybe_to_proc
66
+ end
67
+
68
+ def as_optional(value = Kind::Undefined)
69
+ as_maybe(value)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -2,8 +2,18 @@
2
2
 
3
3
  module Kind
4
4
  class Error < TypeError
5
- def initialize(kind_name, object)
6
- super("#{object.inspect} expected to be a kind of #{kind_name}")
5
+ UNDEFINED_OBJECT = Object.new
6
+
7
+ private_constant :UNDEFINED_OBJECT
8
+
9
+ def initialize(arg, object = UNDEFINED_OBJECT)
10
+ if UNDEFINED_OBJECT == object
11
+ # Will be used when the exception was raised with a message. e.g:
12
+ # raise Kind::Error, "some message"
13
+ super(arg)
14
+ else
15
+ super("#{object.inspect} expected to be a kind of #{arg}")
16
+ end
7
17
  end
8
18
  end
9
19
  end
@@ -2,9 +2,22 @@
2
2
 
3
3
  module Kind
4
4
  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
+
5
18
  module Value
6
19
  def self.none?(value)
7
- value == nil || value == Undefined
20
+ value.nil? || Undefined == value
8
21
  end
9
22
 
10
23
  def self.some?(value)
@@ -16,7 +29,7 @@ module Kind
16
29
  attr_reader :value
17
30
 
18
31
  def initialize(value)
19
- @value = value
32
+ @value = value.kind_of?(Result) ? value.value : value
20
33
  end
21
34
 
22
35
  def value_or(method_name = Undefined, &block)
@@ -42,9 +55,9 @@ module Kind
42
55
  INVALID_DEFAULT_ARG = 'the default value must be defined as an argument or block'.freeze
43
56
 
44
57
  def value_or(default = Undefined, &block)
45
- raise ArgumentError, INVALID_DEFAULT_ARG if default == Undefined && !block
58
+ raise ArgumentError, INVALID_DEFAULT_ARG if Undefined == default && !block
46
59
 
47
- Maybe::Value.some?(default) ? default : block.call
60
+ Undefined != default ? default : block.call
48
61
  end
49
62
 
50
63
  def none?; true; end
@@ -56,7 +69,7 @@ module Kind
56
69
  alias_method :then, :map
57
70
 
58
71
  def try(method_name = Undefined, &block)
59
- Kind.of.Symbol(method_name) if method_name != Undefined
72
+ Kind.of.Symbol(method_name) if Undefined != method_name
60
73
 
61
74
  nil
62
75
  end
@@ -79,8 +92,9 @@ module Kind
79
92
  def map(&fn)
80
93
  result = fn.call(@value)
81
94
 
82
- return NONE_WITH_NIL_VALUE if result == nil
83
- return NONE_WITH_UNDEFINED_VALUE if result == Undefined
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
84
98
 
85
99
  Some.new(result)
86
100
  end
@@ -88,7 +102,7 @@ module Kind
88
102
  alias_method :then, :map
89
103
 
90
104
  def try(method_name = Undefined, *args, &block)
91
- fn = method_name == Undefined ? block : Kind.of.Symbol(method_name).to_proc
105
+ fn = Undefined == method_name ? block : Kind.of.Symbol(method_name).to_proc
92
106
 
93
107
  result = args.empty? ? fn.call(value) : fn.call(*args.unshift(value))
94
108
 
@@ -98,13 +112,49 @@ module Kind
98
112
 
99
113
  def self.new(value)
100
114
  result_type = Maybe::Value.none?(value) ? None : Some
101
- result_type.new(value.is_a?(Result) ? value.value : value)
115
+ result_type.new(value)
102
116
  end
103
117
 
104
- def self.[](value);
118
+ def self.[](value)
105
119
  new(value)
106
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
107
139
  end
108
140
 
109
141
  Optional = Maybe
142
+
143
+ None = Maybe.none
144
+
145
+ def self.None
146
+ Kind::None
147
+ end
148
+
149
+ def self.Some(value)
150
+ Maybe.some(value)
151
+ end
152
+
153
+ def self.Maybe(kind)
154
+ Maybe::Typed.new(kind)
155
+ end
156
+
157
+ def self.Optional(kind)
158
+ Maybe::Typed.new(kind)
159
+ end
110
160
  end
@@ -10,15 +10,25 @@ module Kind
10
10
  def self.%{method_name}(object = Undefined, options = Empty::HASH)
11
11
  default = options[:or]
12
12
 
13
- return Kind::Of::%{kind_name} if object == Undefined && default.nil?
13
+ return Kind::Of::%{kind_name} if Undefined == object && default.nil?
14
14
 
15
- Kind::Of.(::%{kind_name_to_check}, (object || default))
15
+ is_instance = Kind::Of::%{kind_name}.__is_instance__(object)
16
+
17
+ return object if is_instance
18
+
19
+ Kind::Of.(::%{kind_name_to_check}, object && default ? default : object || default)
20
+ end
21
+ RUBY
22
+
23
+ KIND_OF_IS = <<-RUBY
24
+ def self.%{method_name}?(*args)
25
+ Kind::Of::%{kind_name}.instance?(*args)
16
26
  end
17
27
  RUBY
18
28
 
19
29
  KIND_IS = <<-RUBY
20
30
  def self.%{method_name}(value = Undefined)
21
- return Kind::Is::%{kind_name} if value == Undefined
31
+ return Kind::Is::%{kind_name} if Undefined == value
22
32
 
23
33
  Kind::Is.__call__(::%{kind_name_to_check}, value)
24
34
  end
@@ -88,11 +98,12 @@ module Kind
88
98
  kind_name = params[:kind_name]
89
99
  params[:kind_name_to_check] ||= kind_name
90
100
 
91
- kind_checker = ::Module.new { extend Checkable }
101
+ kind_checker = ::Module.new { extend Checker::Protocol }
92
102
  kind_checker.module_eval("def self.__kind; #{params[:kind_name_to_check]}; end")
93
103
 
94
104
  kind_of_mod.instance_eval(KIND_OF % params)
95
105
  kind_of_mod.const_set(method_name, kind_checker)
106
+ kind_of_mod.instance_eval(KIND_OF_IS % params)
96
107
  end
97
108
 
98
109
  unless kind_is_mod.respond_to?(method_name)
@@ -19,7 +19,7 @@ module Kind
19
19
  end
20
20
 
21
21
  def undefined.default(value, default)
22
- return self if value != self
22
+ return self if self != value
23
23
 
24
24
  default.respond_to?(:call) ? default.call : default
25
25
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module Validator
5
+ DEFAULT_STRATEGIES = Set.new(%w[instance_of kind_of]).freeze
6
+
7
+ class InvalidDefinition < ArgumentError
8
+ OPTIONS = 'Options to define one: :of, :is, :respond_to, :instance_of, :array_of or :array_with'.freeze
9
+
10
+ def initialize(attribute)
11
+ super "invalid type definition for :#{attribute} attribute. #{OPTIONS}"
12
+ end
13
+
14
+ private_constant :OPTIONS
15
+ end
16
+
17
+ class InvalidDefaultStrategy < ArgumentError
18
+ OPTIONS =
19
+ DEFAULT_STRATEGIES.map { |option| ":#{option}" }.join(', ')
20
+
21
+ def initialize(option)
22
+ super "#{option.inspect} is an invalid option. Please use one of these: #{OPTIONS}"
23
+ end
24
+
25
+ private_constant :OPTIONS
26
+ end
27
+
28
+ def self.default_strategy
29
+ @default_strategy ||= :kind_of
30
+ end
31
+
32
+ def self.default_strategy=(option)
33
+ if DEFAULT_STRATEGIES.member?(String(option))
34
+ @default_strategy = option.to_sym
35
+ else
36
+ raise InvalidDefaultStrategy.new(option)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kind
4
- VERSION = '1.8.0'
4
+ VERSION = '2.3.0'
5
5
  end
data/test.sh ADDED
@@ -0,0 +1,11 @@
1
+ #!/bin/bash
2
+
3
+ bundle
4
+
5
+ rm Gemfile.lock
6
+
7
+ source $(dirname $0)/.travis.sh
8
+
9
+ rm Gemfile.lock
10
+
11
+ bundle
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kind
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 2.3.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-05-04 00:00:00.000000000 Z
11
+ date: 2020-06-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Basic type system for Ruby (free of dependencies).
13
+ description: A simple type system (at runtime) for Ruby - free of dependencies.
14
14
  email:
15
15
  - rodrigo.serradura@gmail.com
16
16
  executables: []
@@ -18,6 +18,7 @@ extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
20
  - ".gitignore"
21
+ - ".travis.sh"
21
22
  - ".travis.yml"
22
23
  - CODE_OF_CONDUCT.md
23
24
  - Gemfile
@@ -28,7 +29,11 @@ files:
28
29
  - bin/setup
29
30
  - kind.gemspec
30
31
  - lib/kind.rb
32
+ - lib/kind/active_model/kind_validator.rb
33
+ - lib/kind/active_model/validation.rb
31
34
  - lib/kind/checker.rb
35
+ - lib/kind/checker/factory.rb
36
+ - lib/kind/checker/protocol.rb
32
37
  - lib/kind/empty.rb
33
38
  - lib/kind/error.rb
34
39
  - lib/kind/is.rb
@@ -36,7 +41,9 @@ files:
36
41
  - lib/kind/of.rb
37
42
  - lib/kind/types.rb
38
43
  - lib/kind/undefined.rb
44
+ - lib/kind/validator.rb
39
45
  - lib/kind/version.rb
46
+ - test.sh
40
47
  homepage: https://github.com/serradura/kind
41
48
  licenses:
42
49
  - MIT
@@ -61,5 +68,5 @@ requirements: []
61
68
  rubygems_version: 3.0.6
62
69
  signing_key:
63
70
  specification_version: 4
64
- summary: Basic type system for Ruby.
71
+ summary: A simple type system (at runtime) for Ruby.
65
72
  test_files: []