kind 1.8.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []