eapi 0.0.1 → 0.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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/Gemfile +1 -0
- data/README.md +495 -2
- data/eapi.gemspec +0 -1
- data/lib/eapi.rb +10 -2
- data/lib/eapi/children.rb +30 -0
- data/lib/eapi/common.rb +8 -15
- data/lib/eapi/definition_runner.rb +109 -0
- data/lib/eapi/methods/names.rb +0 -4
- data/lib/eapi/methods/properties.rb +1 -109
- data/lib/eapi/methods/types.rb +52 -5
- data/lib/eapi/type_checker.rb +16 -0
- data/lib/eapi/value_converter.rb +40 -0
- data/lib/eapi/version.rb +1 -1
- data/spec/basic_spec.rb +58 -0
- data/spec/definition_spec.rb +26 -0
- data/spec/function_spec.rb +71 -0
- data/spec/list_spec.rb +124 -0
- data/spec/spec_helper.rb +2 -2
- data/spec/to_h_spec.rb +87 -0
- data/spec/type_spec.rb +86 -0
- data/spec/{eapi_validations_spec.rb → validations_spec.rb} +59 -21
- metadata +20 -25
- data/.coveralls.yml +0 -1
- data/spec/eapi_basic_spec.rb +0 -39
- data/spec/eapi_list_spec.rb +0 -92
- data/spec/eapi_to_h_spec.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4eaa6b7386dfe974f725d80fa008f3d8dbe5ee46
|
4
|
+
data.tar.gz: a9b8fb3d839460128e7595c11db9754b18ac0347
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6cd68cc945520b4e4eaf62a8f60be1f53e06bd21226befb7817cfcefeb01168d04dd75cb7491eae835cd2aa1b24cf3321d838fa56bae973f709b05b191c5db2
|
7
|
+
data.tar.gz: d5688563cada1584200aa2ca6febca6303fba83115484a3dfe80e103dda2282fdb5a7dc3871aa9858415a9eb8f478669ffd255ed674bbb6b63a9a5d00fc67d06
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
ruby gem for building complex structures that will end up in hashes (initially devised for ElasticSearch search requests)
|
4
4
|
|
5
|
-
[](http://badge.fury.io/rb/eapi)
|
6
6
|
[](https://travis-ci.org/eturino/eapi)
|
7
|
+
[](https://codeclimate.com/github/eturino/eapi)
|
8
|
+
[](https://codeclimate.com/github/eturino/eapi)
|
7
9
|
|
8
10
|
## Installation
|
9
11
|
|
@@ -19,9 +21,500 @@ Or install it yourself as:
|
|
19
21
|
|
20
22
|
$ gem install eapi
|
21
23
|
|
24
|
+
## Dependencies
|
25
|
+
|
26
|
+
### Ruby version
|
27
|
+
|
28
|
+
Works with ruby 2. Tested with MRI 2.1.1 and 2.0.0
|
29
|
+
|
30
|
+
### Gem dependencies
|
31
|
+
|
32
|
+
This gem uses ActiveSupport (version 4) and also the ActiveModel Validations (version 4)
|
33
|
+
|
34
|
+
Extracted from the gemspec:
|
35
|
+
```
|
36
|
+
spec.add_dependency 'activesupport', '~> 4'
|
37
|
+
spec.add_dependency 'activemodel', '~> 4'
|
38
|
+
```
|
39
|
+
|
22
40
|
## Usage
|
23
41
|
|
24
|
-
|
42
|
+
### including EAPI into your class
|
43
|
+
|
44
|
+
Just include the module `Eapi::Common` into your class.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class MyTestKlass
|
48
|
+
include Eapi::Common
|
49
|
+
|
50
|
+
property :something
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
### Initialize
|
55
|
+
|
56
|
+
`Eapi::Common` will add a `initialize` method to your class that will accept a hash. It will recognise the defined properties in that hash and will set them.
|
57
|
+
|
58
|
+
For now any unrecognised property in the hash will be ignored. This may change in the future.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
class MyTestKlass
|
62
|
+
include Eapi::Common
|
63
|
+
|
64
|
+
property :something
|
65
|
+
end
|
66
|
+
|
67
|
+
x = MyTestKlass.new something: 1
|
68
|
+
x.something # => 1
|
69
|
+
```
|
70
|
+
|
71
|
+
### Object creation shortcut: calling methods in Eapi
|
72
|
+
|
73
|
+
Calling a method with the desired class name in `Eapi` module will do the same as `DesiredClass.new(...)`. The name can be the same as the class, or an underscorised version, or a simple underscored one.
|
74
|
+
|
75
|
+
The goal is to use `Eapi.esr_search(name: 'Paco')` as a shortcut to `Esr::Search.new(name: 'Paco')`. We can also use `Eapi.Esr_Search(...)` and other combinations.
|
76
|
+
|
77
|
+
To show this feature and all the combinations for method names, we'll use the 2 example classes that are used in the actual test rspec.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
class MyTestKlassOutside
|
81
|
+
include Eapi::Common
|
82
|
+
|
83
|
+
property :something
|
84
|
+
end
|
85
|
+
|
86
|
+
module Somewhere
|
87
|
+
class TestKlassInModule
|
88
|
+
include Eapi::Common
|
89
|
+
|
90
|
+
property :something
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
As shown by rspec run:
|
96
|
+
|
97
|
+
```
|
98
|
+
initialise using method calls to Eapi
|
99
|
+
Eapi.MyTestKlassOutside(...)
|
100
|
+
calls MyTestKlassOutside.new
|
101
|
+
Eapi.my_test_klass_outside(...)
|
102
|
+
calls MyTestKlassOutside.new
|
103
|
+
Eapi.Somewhere__TestKlassInModule(...)
|
104
|
+
calls Somewhere::TestKlassInModule.new
|
105
|
+
Eapi.somewhere__test_klass_in_module(...)
|
106
|
+
calls Somewhere::TestKlassInModule.new
|
107
|
+
Eapi.Somewhere_TestKlassInModule(...)
|
108
|
+
calls Somewhere::TestKlassInModule.new
|
109
|
+
Eapi.somewhere_test_klass_in_module(...)
|
110
|
+
calls Somewhere::TestKlassInModule.new
|
111
|
+
```
|
112
|
+
|
113
|
+
### Defining properties
|
114
|
+
|
115
|
+
We define properties in our class with the instruction `property` as shown:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
class MyTestKlass
|
119
|
+
include Eapi::Common
|
120
|
+
|
121
|
+
property :one
|
122
|
+
property :two
|
123
|
+
end
|
124
|
+
```
|
125
|
+
#### Setting proeprties on object creation
|
126
|
+
We can then assign the properties on object creation:
|
127
|
+
```ruby
|
128
|
+
x = MyTestKlass.new one: 1, two: 2
|
129
|
+
```
|
130
|
+
#### Getters
|
131
|
+
|
132
|
+
A getter method will be created for each property
|
133
|
+
```ruby
|
134
|
+
x = MyTestKlass.new one: 1, two: 2
|
135
|
+
x.one # => 1
|
136
|
+
x.two # => 2
|
137
|
+
```
|
138
|
+
|
139
|
+
#### Setters
|
140
|
+
|
141
|
+
Also, a setter will be created for each property
|
142
|
+
```ruby
|
143
|
+
x = MyTestKlass.new one: 1, two: 2
|
144
|
+
x.one = :other
|
145
|
+
x.one # => :other
|
146
|
+
```
|
147
|
+
|
148
|
+
#### Fluent setters (for method chaining)
|
149
|
+
Besides the normal setter, a fluent setter (`set_my_prop`) will be created for each property. `self` is returned on this setters, enabling Method Chaining.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
x = MyTestKlass.new one: 1, two: 2
|
153
|
+
res = x.set_one(:other)
|
154
|
+
x.one # => :other
|
155
|
+
res.equal? x # => true
|
156
|
+
|
157
|
+
x.set_one(:hey).set_two(:you)
|
158
|
+
x.one # => :hey
|
159
|
+
x.two # => :you
|
160
|
+
```
|
161
|
+
|
162
|
+
#### Getter method as fluent setter
|
163
|
+
|
164
|
+
The getter method also works as fluent setter. If we pass an argument to it, it will call the fluent setter
|
165
|
+
```ruby
|
166
|
+
x = MyTestKlass.new
|
167
|
+
res = x.one :fluent
|
168
|
+
x.one # => :fluent
|
169
|
+
res.equal? x # => true
|
170
|
+
```
|
171
|
+
|
172
|
+
### Convert to hashes: `to_h` and `create_hash`
|
173
|
+
|
174
|
+
All Eapi classes respond to `to_h` and return a hash, as it is the main purpose of this gem. It will execute any validation (see property definition), and if everything is ok, it will convert it to a simple hash structure.
|
175
|
+
|
176
|
+
#### Methods involved
|
177
|
+
|
178
|
+
Inside, `to_h` will call `valid?`, raise an error of type `Eapi::Errors::InvalidElementError` if something is not right, and if everything is ok it will call `create_hash`.
|
179
|
+
|
180
|
+
The `create_hash` method will create a hash with the properties as keys. Each value will be "converted".
|
181
|
+
|
182
|
+
#### Values conversion
|
183
|
+
|
184
|
+
By default, each property will be converted into a simple element (Array, Hash, or simple value).
|
185
|
+
|
186
|
+
If a value is an Array or a Set, `to_a` will be invoked and all values will be "converted" in the same way.
|
187
|
+
|
188
|
+
If a value respond to `to_h`, it will be called. That way, if the value of a property (or an element of an Array) is an Eapi object, it will be validated and converted into a simple hash structure.
|
189
|
+
|
190
|
+
important: *any nil value will be omitted* in the final hash.
|
191
|
+
|
192
|
+
#### Example
|
193
|
+
|
194
|
+
To demonstrate this behaviour we'll have an Eapi enabled class `ExampleEapi` and another `ComplexValue` class that responds to `to_h`. We'll set into the `ExampleEapi` object complex properties to demonstrate the conversion into a simple structure.
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
class ComplexValue
|
198
|
+
def to_h
|
199
|
+
{
|
200
|
+
a: Set.new(['hello', 'world', MyTestObject.new])
|
201
|
+
}
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class ExampleEapi
|
206
|
+
include Eapi::Common
|
207
|
+
|
208
|
+
property :something, required: true
|
209
|
+
property :other
|
210
|
+
end
|
211
|
+
|
212
|
+
# TESTING `to_h`
|
213
|
+
|
214
|
+
list = Set.new [
|
215
|
+
OpenStruct.new(a: 1, 'b' => 2),
|
216
|
+
{c: 3, 'd' => 4},
|
217
|
+
nil
|
218
|
+
]
|
219
|
+
|
220
|
+
eapi = ExampleEapi.new something: list, other: ComplexValue.new
|
221
|
+
eapi.to_h # =>
|
222
|
+
# {
|
223
|
+
# something: [
|
224
|
+
# {a: 1, b: 2},
|
225
|
+
# {c: 3, d: 4},
|
226
|
+
# ],
|
227
|
+
#
|
228
|
+
# other: {
|
229
|
+
# a: [
|
230
|
+
# 'hello',
|
231
|
+
# 'world',
|
232
|
+
# {a: 'hello'}
|
233
|
+
# ]
|
234
|
+
# }
|
235
|
+
# }
|
236
|
+
```
|
237
|
+
|
238
|
+
### Property definition
|
239
|
+
|
240
|
+
When defining the property, we can specify some options to specify what values are expected in that property. This serves for validation and automatic initialisation.
|
241
|
+
|
242
|
+
It uses `ActiveModel::Validations`. When `to_h` is called in an Eapi object, the `valid?` method will be called and if the object is not valid an `Eapi::Errors::InvalidElementError` error will raise.
|
243
|
+
|
244
|
+
#### Mark a property as Required with `required` option
|
245
|
+
|
246
|
+
A required property will fail if the value is not present. It will use `ActiveModel::Validations` inside and will effectively do a `validates_presence_of :property_name`.
|
247
|
+
|
248
|
+
example:
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
class TestKlass
|
252
|
+
include Eapi::Common
|
253
|
+
|
254
|
+
property :something, required: true
|
255
|
+
end
|
256
|
+
|
257
|
+
eapi = TestKlass.new
|
258
|
+
eapi.valid? # => false
|
259
|
+
eapi.errors.full_messages # => ["Something can't be blank"]
|
260
|
+
eapi.errors.messages # => {something: ["can't be blank"]}
|
261
|
+
```
|
262
|
+
|
263
|
+
#### Specify the property's Type with `type` option
|
264
|
+
|
265
|
+
If a property is defined to be of a specific type, the value will be validated to meet that criteria. It means that the value must be of the specified type. It will use `value.kind_of?(type)` and if that fails it will use `value.is?(type)` if defined.
|
266
|
+
|
267
|
+
example:
|
268
|
+
|
269
|
+
```ruby
|
270
|
+
class TestKlass
|
271
|
+
include Eapi::Common
|
272
|
+
|
273
|
+
property :something, type: Hash
|
274
|
+
end
|
275
|
+
|
276
|
+
eapi = TestKlass.new something: 1
|
277
|
+
eapi.valid? # => false
|
278
|
+
eapi.errors.full_messages # => ["Something must be a Hash"]
|
279
|
+
eapi.errors.messages # => {something: ["must be a Hash"]}
|
280
|
+
```
|
281
|
+
|
282
|
+
Also, if a type is specified, then a `init_property_name` method is created that will set a new object of the given type in the property.
|
283
|
+
|
284
|
+
```ruby
|
285
|
+
class TestKlass
|
286
|
+
include Eapi::Common
|
287
|
+
|
288
|
+
property :something, type: Hash
|
289
|
+
end
|
290
|
+
|
291
|
+
eapi = TestKlass.new
|
292
|
+
eapi.something # => nil
|
293
|
+
eapi.init_something
|
294
|
+
eapi.something # => {}
|
295
|
+
```
|
296
|
+
|
297
|
+
To trigger the error, the value must not be an instance of the given Type, and also must not respond `true` to `value.is?(type)`
|
298
|
+
|
299
|
+
#### Custom validation with `validate_with` option
|
300
|
+
|
301
|
+
A more specific validation can be used using `validate_with`, that works the same way as `ActiveModel::Validations`.
|
302
|
+
|
303
|
+
example:
|
304
|
+
|
305
|
+
```ruby
|
306
|
+
class TestKlass
|
307
|
+
include Eapi::Common
|
308
|
+
|
309
|
+
property :something, validate_with: ->(record, attr, value) do
|
310
|
+
record.errors.add(attr, "must pass my custom validation") unless value == :valid_val
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
eapi = TestKlass.new something: 1
|
315
|
+
eapi.valid? # => false
|
316
|
+
eapi.errors.full_messages # => ["Something must pass my custom validation"]
|
317
|
+
eapi.errors.messages # => {something: ["must pass my custom validation"]}
|
318
|
+
```
|
319
|
+
|
320
|
+
#### Validations from `ActiveModel::Validations`
|
321
|
+
|
322
|
+
All other ActiveModel::Validations can be used:
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
class TestKlass
|
326
|
+
include Eapi::Common
|
327
|
+
|
328
|
+
property :something
|
329
|
+
validates :something, numericality: true
|
330
|
+
end
|
331
|
+
|
332
|
+
eapi = TestKlass.new something: 'something'
|
333
|
+
eapi.valid? # => false
|
334
|
+
eapi.errors.full_messages # => ["Something is not a number"]
|
335
|
+
eapi.errors.messages # => {something: ["must is not a number"]}
|
336
|
+
```
|
337
|
+
|
338
|
+
#### Unrecognised property definition options
|
339
|
+
|
340
|
+
If the definition contained any unrecognised options, it will still be stored. No error is reported yet, but this behaviour may change in the future.
|
341
|
+
|
342
|
+
#### See property definition with `.definition_for` class method
|
343
|
+
|
344
|
+
You can see (but not edit) the definition of a property calling the `definition_for` class method. It will also contain the unrecognised options.
|
345
|
+
|
346
|
+
```ruby
|
347
|
+
class TestKlass
|
348
|
+
include Eapi::Common
|
349
|
+
|
350
|
+
property :something, type: Hash, unrecognised_option: 1
|
351
|
+
end
|
352
|
+
|
353
|
+
definition = TestKlass.definition_for :something # => { type: Hash, unrecognised_option: 1 }
|
354
|
+
|
355
|
+
# attempt to change the definition...
|
356
|
+
definition[:type] = Array
|
357
|
+
|
358
|
+
# ...has no effect
|
359
|
+
TestKlass.definition_for :something # => { type: Hash, unrecognised_option: 1 }
|
360
|
+
```
|
361
|
+
|
362
|
+
### List properties
|
363
|
+
|
364
|
+
a property can be defined as a multiple property. This will affect the methods defined in the class (it will create a fluent 'adder' method `add_property_name`), and also the automatic initialisation.
|
365
|
+
|
366
|
+
#### Define property as multiple with `multiple` option
|
367
|
+
|
368
|
+
A property marked as `multiple` will be initialised with an empty array. If no type is specified then it will use Array as a type, only for purposes of the `init_property_name` method.
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
class TestKlass
|
372
|
+
include Eapi::Common
|
373
|
+
|
374
|
+
property :something, multiple: true
|
375
|
+
end
|
376
|
+
```
|
377
|
+
|
378
|
+
#### Adder method `add_property_name`
|
379
|
+
|
380
|
+
For a property marked as multiple, an extra fluent method called `add_property_name` will be created. This work very similar to the fluent setter `set_property_name` but inside it will append the value (using the shovel method `<<`) instead of setting it.
|
381
|
+
|
382
|
+
If the property is `nil` when `add_property_name` is called, then it will call `init_property_name` before.
|
383
|
+
|
384
|
+
```ruby
|
385
|
+
class TestKlass
|
386
|
+
include Eapi::Common
|
387
|
+
|
388
|
+
property :something, multiple: true
|
389
|
+
end
|
390
|
+
|
391
|
+
x = TestKlass.new
|
392
|
+
x.add_something(1).add_something(2)
|
393
|
+
x.something # => [1, 2]
|
394
|
+
```
|
395
|
+
|
396
|
+
#### Implicit `multiple` depending on Type
|
397
|
+
|
398
|
+
Even without `multiple` option specified, if the `type` option is:
|
399
|
+
* `Array`
|
400
|
+
* `Set`
|
401
|
+
* a class that responds to `is_multiple?` with true
|
402
|
+
|
403
|
+
then the property is marked as multiple.
|
404
|
+
|
405
|
+
example: (all `TestKlass` properties are marked as multiple)
|
406
|
+
```ruby
|
407
|
+
class MyCustomList
|
408
|
+
def self.is_multiple?
|
409
|
+
true
|
410
|
+
end
|
411
|
+
|
412
|
+
def <<(val)
|
413
|
+
@list |= []
|
414
|
+
@list << val
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
class TestKlass
|
419
|
+
include Eapi::Common
|
420
|
+
|
421
|
+
property :p1, multiple: true
|
422
|
+
property :p2, type: Array
|
423
|
+
property :p3, type: Set
|
424
|
+
property :p4, type: MyCustomList
|
425
|
+
end
|
426
|
+
|
427
|
+
x = TestKlass.new
|
428
|
+
x.add_p1(1).add_p2(2).add_p3(3).add_p4(4)
|
429
|
+
```
|
430
|
+
|
431
|
+
#### Element validation
|
432
|
+
|
433
|
+
Same as property validation, but for specific the elements in the list.
|
434
|
+
|
435
|
+
We can use `element_type` option in the definition, and it will check the type of each element in the list, same as `type` option does with the type of the property's value.
|
436
|
+
|
437
|
+
We can also specify `validate_element_with` option, and it will act the same as `validate_with` but for each element in the list.
|
438
|
+
|
439
|
+
```ruby
|
440
|
+
class TestKlass
|
441
|
+
include Eapi::Common
|
442
|
+
|
443
|
+
property :something, multiple: true, element_type: Hash
|
444
|
+
property :other, multiple: true, validate_element_with: ->(record, attr, value) do
|
445
|
+
record.errors.add(attr, "element must pass my custom validation") unless value == :valid_val
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
eapi = TestKlass.new
|
450
|
+
eapi.add_something 1
|
451
|
+
|
452
|
+
eapi.valid? # => false
|
453
|
+
eapi.errors.full_messages # => ["Something element must be a Hash"]
|
454
|
+
eapi.errors.messages # => {something: ["must element be a Hash"]}
|
455
|
+
|
456
|
+
eapi.something [{a: :b}]
|
457
|
+
eapi.valid? # => true
|
458
|
+
|
459
|
+
eapi.add_other 1
|
460
|
+
eapi.valid? # => false
|
461
|
+
eapi.errors.full_messages # => ["Other element must pass my custom validation"]
|
462
|
+
eapi.errors.messages # => {other: ["element must pass my custom validation"]}
|
463
|
+
|
464
|
+
eapi.other [:valid_val]
|
465
|
+
eapi.valid? # => true
|
466
|
+
```
|
467
|
+
|
468
|
+
### Pose as other types
|
469
|
+
|
470
|
+
An Eapi class can poses as other types, for purposes of `type` checking in a property definition. We use the class method `is` for this.
|
471
|
+
|
472
|
+
the `is?` method is also available as an instance method.
|
473
|
+
|
474
|
+
Eapi also creates specific instance and class methods like `is_a_some_type?` or `is_an_another_type?`.
|
475
|
+
|
476
|
+
example:
|
477
|
+
|
478
|
+
```ruby
|
479
|
+
class SuperTestKlass
|
480
|
+
include Eapi::Common
|
481
|
+
end
|
482
|
+
|
483
|
+
class TestKlass < SuperTestKlass
|
484
|
+
is :one_thing, :other_thing, OtherType
|
485
|
+
end
|
486
|
+
|
487
|
+
TestKlass.is? TestKlass # => true
|
488
|
+
TestKlass.is? 'TestKlass' # => true
|
489
|
+
TestKlass.is? :TestKlass # => true
|
490
|
+
|
491
|
+
TestKlass.is? SuperTestKlass # => true
|
492
|
+
TestKlass.is? 'SuperTestKlass' # => true
|
493
|
+
TestKlass.is? :SuperTestKlass # => true
|
494
|
+
|
495
|
+
TestKlass.is? :one_thing # => true
|
496
|
+
TestKlass.is? :other_thing # => true
|
497
|
+
TestKlass.is? :other_thing # => true
|
498
|
+
TestKlass.is? OtherType # => true
|
499
|
+
TestKlass.is? :OtherType # => true
|
500
|
+
|
501
|
+
TestKlass.is? SomethingElse # => false
|
502
|
+
TestKlass.is? :SomethingElse # => false
|
503
|
+
|
504
|
+
# also works on instance
|
505
|
+
obj = TestKlass.new
|
506
|
+
obj.is? TestKlass # => true
|
507
|
+
obj.is? :one_thing # => true
|
508
|
+
|
509
|
+
# specific type test methods
|
510
|
+
TestKlass.is_a_test_klass? # => true
|
511
|
+
TestKlass.is_an_one_thing? # => true
|
512
|
+
TestKlass.is_a_super_duper_thing? # => false
|
513
|
+
|
514
|
+
obj.is_a_test_klass? # => true
|
515
|
+
obj.is_an_one_thing? # => true
|
516
|
+
obj.is_a_super_duper_thing? # => false
|
517
|
+
```
|
25
518
|
|
26
519
|
## Contributing
|
27
520
|
|