kind 0.6.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +66 -34
  3. data/lib/kind.rb +104 -46
  4. data/lib/kind/version.rb +1 -1
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33889c9e4fc1ad94b529e15711ae7d8a36271710367769d3d675301977e1e83c
4
- data.tar.gz: 39ed9dfb4b99ca346ebc82561592f4d867479576c6d749aa41f8d86b4e2bf2df
3
+ metadata.gz: c1ca16c1feac58c7a1303c0ee1cda8cd5831a2270039ed8301912afc7a3a9f55
4
+ data.tar.gz: 61dba4fdf5c8c94118f93ca5d16ab55364601fc5846f9d574cf44df05f6dff2a
5
5
  SHA512:
6
- metadata.gz: 1b4093ba9909ea58cd319e0cafda608dc78f259da971685c1a9d778a012d17c5f5ab027739aed74c0eb354b7f7572e01749d3ce7822b9be4a15d7cb26be5f254
7
- data.tar.gz: 881b88913a5172e3cb3bcfd73b466bd26422d0213e9088600c3c611bb20b3c326dfb8f6bdfcac0556b2aa5f2462a197522e59f94f147eaa9beb85102ee4dc0f5
6
+ metadata.gz: 8397153bb070e3e27d4f2dfd0557a5f050a7ec292f4e2e779ffb7cdda26c125c29c64c283d8adc8ea09dce5ad4b23ed18575f35c711353a2184fbdff4effe34a
7
+ data.tar.gz: 7fee91750a8b24697e023a4be435b8c8db78cdd3f365dd70f5b9b0fda6daf549b3235528e94f6d9f985b5850ee031cbdd84475f8aabe4b549392c2304b7d8fa0
data/README.md CHANGED
@@ -23,6 +23,7 @@ One of the goals of this project is to do simple type checking like `"some strin
23
23
  - [Kind.of](#kindof)
24
24
  - [Kind.is](#kindis)
25
25
  - [How to create a new type checker?](#how-to-create-a-new-type-checker)
26
+ - [What happens if a custom type checker has a namespace?](#what-happens-if-a-custom-type-checker-has-a-namespace)
26
27
  - [Development](#development)
27
28
  - [Contributing](#contributing)
28
29
  - [License](#license)
@@ -58,11 +59,9 @@ def sum(a, b)
58
59
  Kind.of.Numeric(a) + Kind.of.Numeric(b)
59
60
  end
60
61
 
61
- sum(1, 1)
62
- # 2
62
+ sum(1, 1) # 2
63
63
 
64
- sum('1', 1)
65
- # Kind::Error ("\"1\" expected to be a kind of Numeric")
64
+ sum('1', 1 # Kind::Error ("\"1\" expected to be a kind of Numeric")
66
65
  ```
67
66
 
68
67
  ### Verifying the kind of some object
@@ -70,16 +69,13 @@ sum('1', 1)
70
69
  By default, basic verifications are strict. So, when you perform `Kind.of.Hash(value)`, if the given value was a Hash, the value itself will be returned, but if it isn't the right type, an error will be raised.
71
70
 
72
71
  ```ruby
73
- Kind.of.Hash('')
74
- # raise Kind::Error, "'' expected to be a kind of Hash"
72
+ Kind.of.Hash('') # raise Kind::Error, "'' expected to be a kind of Hash"
75
73
 
76
- Kind.of.Hash({a: 1})
77
- # {a: 1}
74
+ Kind.of.Hash({a: 1}) # {a: 1}
78
75
 
79
76
  # ---
80
77
 
81
- Kind.of.Boolean(nil)
82
- # raise Kind::Error, "nil expected to be a kind of Boolean"
78
+ Kind.of.Boolean(nil) # raise Kind::Error, "nil expected to be a kind of Boolean"
83
79
 
84
80
  Kind.of.Boolean(true) # true
85
81
  Kind.of.Boolean(false) # false
@@ -90,13 +86,11 @@ When the verified value is nil, it is possible to define a default value with th
90
86
  ```ruby
91
87
  value = nil
92
88
 
93
- Kind.of.Hash(value, or: {})
94
- # {}
89
+ Kind.of.Hash(value, or: {}) # {}
95
90
 
96
91
  # ---
97
92
 
98
- Kind.of.Boolean(nil, or: true)
99
- # true
93
+ Kind.of.Boolean(nil, or: true) # true
100
94
  ```
101
95
 
102
96
  As an alternative syntax, you can use the `Kind::Of` instead of the method. e.g: `Kind::Of::Hash('')`
@@ -134,24 +128,19 @@ Kind.of.Boolean.instance?(false) # true
134
128
  You can use `Kind.is` to verify if some class has the expected type as its ancestor.
135
129
 
136
130
  ```ruby
137
- Kind.is.Hash(String)
138
- # false
131
+ Kind.is.Hash(String) # false
139
132
 
140
- Kind.is.Hash(Hash)
141
- # true
133
+ Kind.is.Hash(Hash) # true
142
134
 
143
- Kind.is.Hash(ActiveSupport::HashWithIndifferentAccess)
144
- # true
135
+ Kind.is.Hash(ActiveSupport::HashWithIndifferentAccess) # true
145
136
  ```
146
137
 
147
138
  And just for convenience, you can use the method `Kind.of.*.class?` to verify if the given class has the expected type as its ancestor.
148
139
 
149
140
  ```ruby
150
- Kind.of.Hash.class?(Hash)
151
- # true
141
+ Kind.of.Hash.class?(Hash) # true
152
142
 
153
- Kind.of.Hash.class?(ActiveSupport::HashWithIndifferentAccess)
154
- # true
143
+ Kind.of.Hash.class?(ActiveSupport::HashWithIndifferentAccess) # true
155
144
  ```
156
145
 
157
146
  [⬆️ Back to Top](#table-of-contents-)
@@ -218,24 +207,67 @@ end
218
207
  # Usage examples: #
219
208
  # --------------- #
220
209
 
221
- Kind.of.User(User.new)
222
- # #<User:0x0000...>
210
+ Kind.of.User(User.new) # #<User:0x0000...>
223
211
 
224
- Kind.of.User({})
225
- # Kind::Error ({} expected to be a kind of User)
212
+ Kind.of.User({}) # Kind::Error ({} expected to be a kind of User)
226
213
 
227
- Kind.of.User.or_nil({})
228
- # nil
229
-
230
- Kind.of.User.instance?({}) # false
231
- Kind.of.User.class?(Hash) # false
214
+ Kind.of.User.or_nil({}) # nil
232
215
 
216
+ Kind.of.User.instance?({}) # false
233
217
  Kind.of.User.instance?(User) # true
234
- Kind.of.User.class?(User) # true
218
+
219
+ Kind.of.User.class?(Hash) # false
220
+ Kind.of.User.class?(User) # true
235
221
  ```
236
222
 
237
223
  [⬆️ Back to Top](#table-of-contents-)
238
224
 
225
+ ## What happens if a custom type checker has a namespace?
226
+
227
+ The type checker will preserve the namespace. ;)
228
+
229
+ ```ruby
230
+ module Account
231
+ class User
232
+ Kind::Types.add(self)
233
+ end
234
+ end
235
+
236
+ module Account
237
+ class User
238
+ class Membership
239
+ Kind::Types.add(self)
240
+ end
241
+ end
242
+ end
243
+
244
+ Kind.of.Account::User(Account::User.new) # #<Account::User:0x0000...>
245
+
246
+ Kind.of.Account::User({}) # Kind::Error ({} expected to be a kind of Account::User)
247
+
248
+ Kind.of.Account::User.or_nil({}) # nil
249
+
250
+ Kind.of.Account::User.instance?({}) # false
251
+ Kind.of.Account::User.instance?(User) # true
252
+
253
+ Kind.of.Account::User.class?(Hash) # false
254
+ Kind.of.Account::User.class?(User) # true
255
+
256
+ # ---
257
+
258
+ Kind.of.Account::User::Membership(Account::User::Membership.new) # #<Account::User::Membership:0x0000...>
259
+
260
+ Kind.of.Account::User::Membership({}) # Kind::Error ({} expected to be a kind of Account::User::Membership)
261
+
262
+ Kind.of.Account::User::Membership.or_nil({}) # nil
263
+
264
+ Kind.of.Account::User::Membership.instance?({}) # false
265
+ Kind.of.Account::User::Membership.instance?(User) # true
266
+
267
+ Kind.of.Account::User::Membership.class?(Hash) # false
268
+ Kind.of.Account::User::Membership.class?(User) # true
269
+ ```
270
+
239
271
  ## Development
240
272
 
241
273
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -26,19 +26,13 @@ module Kind
26
26
  end
27
27
  end
28
28
 
29
- class Checker
30
- attr_reader :type
31
-
32
- def initialize(type)
33
- @type = type
34
- end
35
-
29
+ module Checker
36
30
  def class?(value)
37
- Kind::Is.call(@type, value)
31
+ Kind::Is.(__kind, value)
38
32
  end
39
33
 
40
34
  def instance?(value)
41
- value.is_a?(@type)
35
+ value.is_a?(__kind)
42
36
  end
43
37
 
44
38
  def or_nil(value)
@@ -59,13 +53,15 @@ module Kind
59
53
  self.call(::Class, object)
60
54
  end
61
55
 
62
- const_set(:Class, ::Class.new(Checker) do
63
- def instance?(value)
64
- Kind::Is.Class(value)
65
- end
56
+ const_set(:Class, ::Module.new do
57
+ extend Checker
66
58
 
67
- alias class? instance?
68
- end.new(::Class).freeze)
59
+ def self.__kind; ::Class; end
60
+
61
+ def self.class?(value); Kind::Is.Class(value); end
62
+
63
+ def self.instance?(value); class?(value); end
64
+ end)
69
65
 
70
66
  def self.Module(object = nil)
71
67
  return Module if object.nil?
@@ -73,49 +69,105 @@ module Kind
73
69
  self.call(::Module, object)
74
70
  end
75
71
 
76
- const_set(:Module, ::Class.new(Checker) do
77
- def instance?(value)
78
- Kind::Is.Module(value)
79
- end
72
+ const_set(:Module, ::Module.new do
73
+ extend Checker
74
+
75
+ def self.__kind; ::Module; end
80
76
 
81
- alias class? instance?
82
- end.new(::Module).freeze)
77
+ def self.class?(value); Kind::Is.Module(value); end
78
+
79
+ def self.instance?(value); class?(value); end
80
+ end)
83
81
  end
84
82
 
85
83
  module Types
86
84
  extend self
87
85
 
86
+ COLONS = '::'.freeze
87
+
88
88
  KIND_OF = <<-RUBY
89
- def self.%{name}(object = nil, options = {})
89
+ def self.%{method_name}(object = nil, options = {})
90
90
  default = options[:or]
91
91
 
92
- return Kind::Of::%{name} if object.nil? && default.nil?
92
+ return Kind::Of::%{kind_name} if object.nil? && default.nil?
93
93
 
94
- Kind::Of.call(::%{name}, (object || default), name: "%{name}".freeze)
94
+ Kind::Of.(::%{kind_name}, (object || default), name: "%{kind_name}".freeze)
95
95
  end
96
96
  RUBY
97
97
 
98
98
  KIND_IS = <<-RUBY
99
- def self.%{name}(value)
100
- Kind::Is.call(::%{name}, value)
99
+ def self.%{method_name}(value = nil)
100
+ return Kind::Is::%{kind_name} if value.nil?
101
+
102
+ Kind::Is.(::%{kind_name}, value)
101
103
  end
102
104
  RUBY
103
105
 
104
- private_constant :KIND_OF, :KIND_IS
106
+ private_constant :KIND_OF, :KIND_IS, :COLONS
105
107
 
106
- def add(mod, name: nil)
107
- mod_name = Kind.of.Module(mod).name
108
- chk_name = name ? Kind::Of.(String, name) : mod_name
108
+ def add(kind, name: nil)
109
+ kind_name = Kind.of.Module(kind).name
110
+ checker = name ? Kind::Of.(String, name) : kind_name
109
111
 
110
- unless Of.respond_to?(chk_name)
111
- Of.instance_eval(KIND_OF % { name: chk_name })
112
- Of.const_set(chk_name, Checker.new(mod).freeze)
112
+ case
113
+ when checker.include?(COLONS)
114
+ add_kind_with_namespace(checker, method_name: name)
115
+ else
116
+ add_root(checker, kind_name, method_name: name, create_kind_is_mod: false)
113
117
  end
118
+ end
114
119
 
115
- unless Is.respond_to?(chk_name)
116
- Is.instance_eval(KIND_IS % { name: chk_name })
120
+ private
121
+
122
+ def add_root(checker, kind_name, method_name:, create_kind_is_mod:)
123
+ root_checker = method_name ? method_name : checker
124
+ root_kind_name = method_name ? method_name : kind_name
125
+
126
+ add_kind(root_checker, root_kind_name, Kind::Of, Kind::Is, create_kind_is_mod)
127
+ end
128
+
129
+ def add_kind_with_namespace(checker, method_name:)
130
+ raise NotImplementedError if method_name
131
+
132
+ const_names = checker.split(COLONS)
133
+ const_names.each_with_index do |const_name, index|
134
+ if index == 0
135
+ add_root(const_name, const_name, method_name: nil, create_kind_is_mod: true)
136
+ else
137
+ add_node(const_names, index)
138
+ end
139
+ end
140
+ end
141
+
142
+ def add_node(const_names, index)
143
+ namespace = const_names[0..(index-1)]
144
+ namespace_name = namespace.join(COLONS)
145
+
146
+ kind_of_mod = Kind::Of.const_get(namespace_name)
147
+ kind_is_mod = Kind::Is.const_get(namespace_name)
148
+
149
+ checker = const_names[index]
150
+ kind_name = const_names[0..index].join(COLONS)
151
+
152
+ add_kind(checker, kind_name, kind_of_mod, kind_is_mod, true)
153
+ end
154
+
155
+ def add_kind(checker, kind_name, kind_of_mod, kind_is_mod, create_kind_is_mod)
156
+ params = { method_name: checker, kind_name: kind_name }
157
+
158
+ unless kind_of_mod.respond_to?(checker)
159
+ kind_checker = ::Module.new { extend Checker }
160
+ kind_checker.module_eval("def self.__kind; #{kind_name}; end")
161
+
162
+ kind_of_mod.instance_eval(KIND_OF % params)
163
+ kind_of_mod.const_set(checker, kind_checker)
164
+ end
165
+
166
+ unless kind_is_mod.respond_to?(checker)
167
+ kind_is_mod.instance_eval(KIND_IS % params)
168
+ kind_is_mod.const_set(checker, Module.new) if create_kind_is_mod
169
+ end
117
170
  end
118
- end
119
171
  end
120
172
 
121
173
  def self.is; Is; end
@@ -162,15 +214,17 @@ module Kind
162
214
  raise Kind::Error.new('Boolean'.freeze, bool)
163
215
  end
164
216
 
165
- const_set(:Boolean, ::Class.new(Checker) do
166
- def class?(value)
167
- Kind.is.Boolean(value)
168
- end
217
+ const_set(:Boolean, ::Module.new do
218
+ extend Checker
169
219
 
170
- def instance?(value)
220
+ def self.__kind; [TrueClass, FalseClass].freeze; end
221
+
222
+ def self.class?(value); Kind.is.Boolean(value); end
223
+
224
+ def self.instance?(value);
171
225
  value.is_a?(TrueClass) || value.is_a?(FalseClass)
172
226
  end
173
- end.new([TrueClass, FalseClass].freeze).freeze)
227
+ end)
174
228
 
175
229
  # -- Lambda
176
230
 
@@ -186,11 +240,15 @@ module Kind
186
240
  raise Kind::Error.new('Lambda'.freeze, func)
187
241
  end
188
242
 
189
- const_set(:Lambda, ::Class.new(Checker) do
190
- def instance?(value)
191
- value.is_a?(@type) && value.lambda?
243
+ const_set(:Lambda, ::Module.new do
244
+ extend Checker
245
+
246
+ def self.__kind; ::Proc; end
247
+
248
+ def self.instance?(value)
249
+ value.is_a?(__kind) && value.lambda?
192
250
  end
193
- end.new(::Proc).freeze)
251
+ end)
194
252
  end
195
253
 
196
254
  private_constant :Checker
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kind
4
- VERSION = '0.6.0'
4
+ VERSION = '1.0.0'
5
5
  end
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: 0.6.0
4
+ version: 1.0.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-01-07 00:00:00.000000000 Z
11
+ date: 2020-03-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Basic type system for Ruby (free of dependencies).
14
14
  email: