kind 0.6.0 → 1.0.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 (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: