ducktape 0.2.0 → 0.2.1
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.
- data/README.md +92 -13
- data/lib/ducktape/bindable.rb +3 -3
- data/lib/ducktape/bindable_attribute.rb +1 -1
- data/lib/ducktape/bindable_attribute_metadata.rb +7 -5
- data/lib/ducktape/hookable.rb +18 -11
- data/lib/version.rb +1 -1
- metadata +1 -1
data/README.md
CHANGED
@@ -188,7 +188,7 @@ class X
|
|
188
188
|
bindable :name, validate: [String, Symbol]
|
189
189
|
|
190
190
|
def initialize(name)
|
191
|
-
|
191
|
+
self.name = name
|
192
192
|
end
|
193
193
|
end
|
194
194
|
```
|
@@ -202,11 +202,35 @@ class X
|
|
202
202
|
bindable :name, validate: ->(value){ !value.nil? }
|
203
203
|
|
204
204
|
def initialize(name)
|
205
|
-
|
205
|
+
self.name = name
|
206
206
|
end
|
207
207
|
end
|
208
208
|
```
|
209
209
|
|
210
|
+
Additionally, it has built-in support for regular expressions:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
class X
|
214
|
+
include Ducktape::Bindable
|
215
|
+
|
216
|
+
bindable :name, validate: /ruby/
|
217
|
+
|
218
|
+
def initialize(name)
|
219
|
+
self.name = name
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
#passes
|
224
|
+
X.new('rubygems')
|
225
|
+
|
226
|
+
#fails
|
227
|
+
begin
|
228
|
+
X.new('diamonds')
|
229
|
+
rescue => e
|
230
|
+
puts e.message
|
231
|
+
end
|
232
|
+
```
|
233
|
+
|
210
234
|
Validation also works with any kind of objects. For example, to make an enumerable:
|
211
235
|
|
212
236
|
```ruby
|
@@ -266,12 +290,12 @@ class X
|
|
266
290
|
bindable :points, validate: Integer
|
267
291
|
|
268
292
|
def initialize(name, age, points)
|
269
|
-
|
270
|
-
|
271
|
-
|
293
|
+
self.name = name
|
294
|
+
self.age = age
|
295
|
+
self.points = points
|
272
296
|
|
273
|
-
|
274
|
-
|
297
|
+
# You can hook for any method available
|
298
|
+
%w'name age points'.each { |k, v| on_changed k, &method(:attribute_changed) }
|
275
299
|
end
|
276
300
|
end
|
277
301
|
|
@@ -315,16 +339,18 @@ class X
|
|
315
339
|
def_hook :on_loaded #define one or more hooks
|
316
340
|
|
317
341
|
def load
|
318
|
-
|
342
|
+
# do other stuff here
|
343
|
+
|
344
|
+
call_hooks(:on_loaded)
|
319
345
|
end
|
320
346
|
end
|
321
347
|
|
322
348
|
x = X.new
|
323
349
|
|
324
|
-
x.on_loaded
|
350
|
+
x.on_loaded method(:called_load)
|
325
351
|
|
326
352
|
#if we didn't create a hook with def_hook we could still use:
|
327
|
-
#x.add_hook :on_loaded,
|
353
|
+
#x.add_hook :on_loaded, method(:called_load)
|
328
354
|
|
329
355
|
x.load
|
330
356
|
```
|
@@ -334,9 +360,62 @@ The output should be something like:
|
|
334
360
|
=> "X<14e35b4> called \"on_loaded\""
|
335
361
|
```
|
336
362
|
|
363
|
+
### Named hooks
|
364
|
+
|
365
|
+
It is possible to define a hook by providing a method name. This makes it possible to separate logic from definition.
|
366
|
+
Note that the method must exist for the instance that calls the hooks (through `call_hooks` or `call_handlers`).
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
#class logic file
|
370
|
+
|
371
|
+
class Y
|
372
|
+
def called_load(event, owner)
|
373
|
+
puts "loaded #{self}" #self == owner
|
374
|
+
end
|
375
|
+
|
376
|
+
def load
|
377
|
+
#do some stuff here
|
378
|
+
|
379
|
+
call_hooks(:on_loaded)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
```
|
383
|
+
|
384
|
+
```ruby
|
385
|
+
#instance definition file
|
386
|
+
|
387
|
+
x = Y.tap do |y|
|
388
|
+
y.on_loaded :called_load
|
389
|
+
end
|
390
|
+
|
391
|
+
x.load
|
392
|
+
```
|
393
|
+
|
394
|
+
The output should be something like:
|
395
|
+
```ruby
|
396
|
+
=> "loaded Y<3539af>"
|
397
|
+
```
|
398
|
+
|
399
|
+
This also allows to dynamically bind the hook by overriding the method.
|
400
|
+
|
401
|
+
```ruby
|
402
|
+
#taking the same instance from before
|
403
|
+
|
404
|
+
x.define_singleton_method(:called_load) { puts "singleton #{self} has loaded" }
|
405
|
+
```
|
406
|
+
|
407
|
+
The output should now be:
|
408
|
+
```ruby
|
409
|
+
=> "singleton Y<3539af> has loaded"
|
410
|
+
```
|
411
|
+
|
337
412
|
### Removing hooks
|
338
413
|
|
339
|
-
To remove all hooks from an object call the `#clear_hooks` method. To
|
414
|
+
To remove all hooks from an object call the `#clear_hooks` method. To remove all hooks from a single event, pass the name of the event as a parameter. The next section has an example of this.
|
415
|
+
|
416
|
+
To remove a single hook from an event, call the `#remove_hook` with the name of the event, and the hook name or hook proc corresponding with how the hook was added.
|
417
|
+
|
418
|
+
### Handlers
|
340
419
|
|
341
420
|
### Hookable arrays and hashes
|
342
421
|
|
@@ -396,6 +475,6 @@ The output will only be for the `on_changed` hook, which wasn't removed:
|
|
396
475
|
|
397
476
|
Future work
|
398
477
|
===========
|
399
|
-
* Pass a hook by method name. This will provide a more dynamic binding if the method is overriden.
|
400
478
|
* Multi-sourced BA's.
|
401
|
-
* More complex binding source paths instead of just the member name (e.g.: ruby like 'a.b.c' or xml like 'a/b/c').
|
479
|
+
* More complex binding source paths instead of just the member name (e.g.: ruby like 'a.b.c' or xml like 'a/b/c').
|
480
|
+
* Add built-in support for hookable arrays and hashes in bindable attributes.
|
data/lib/ducktape/bindable.rb
CHANGED
@@ -51,9 +51,9 @@ module Ducktape
|
|
51
51
|
nil
|
52
52
|
end
|
53
53
|
|
54
|
-
def on_changed(attr_name, &block)
|
55
|
-
return nil unless block
|
56
|
-
get_bindable_attr(attr_name.to_s).on_changed(&block)
|
54
|
+
def on_changed(attr_name, hook = nil, &block)
|
55
|
+
return nil unless block || hook
|
56
|
+
get_bindable_attr(attr_name.to_s).on_changed(hook, &block)
|
57
57
|
block
|
58
58
|
end
|
59
59
|
|
@@ -7,7 +7,8 @@ module Ducktape
|
|
7
7
|
|
8
8
|
def initialize(name, options = {})
|
9
9
|
|
10
|
-
options.
|
10
|
+
options.keys.reject { |k| VALID_OPTIONS.member?(k) }.
|
11
|
+
each { |k| puts "WARNING: invalid option #{k.inspect} for #{name.inspect} attribute. Will be ignored." }
|
11
12
|
|
12
13
|
if name.is_a? BindableAttributeMetadata
|
13
14
|
@name = name.name
|
@@ -39,10 +40,11 @@ module Ducktape
|
|
39
40
|
|
40
41
|
def validate(value)
|
41
42
|
return true unless @validation
|
42
|
-
@validation.each do |
|
43
|
-
return true if (
|
44
|
-
|
45
|
-
|
43
|
+
@validation.each do |v|
|
44
|
+
return true if ( v.is_a?(Class) && value.is_a?(v) ) ||
|
45
|
+
( v.is_a?(Proc) && v.(value) ) ||
|
46
|
+
( v.is_a?(Regexp) && value =~ v ) ||
|
47
|
+
value == v
|
46
48
|
end
|
47
49
|
false
|
48
50
|
end
|
data/lib/ducktape/hookable.rb
CHANGED
@@ -3,7 +3,7 @@ module Ducktape
|
|
3
3
|
|
4
4
|
module ClassMethods
|
5
5
|
def def_hook(*events)
|
6
|
-
events.each { |e| define_method e, ->(&block){ add_hook(e, &block) } }
|
6
|
+
events.each { |e| define_method e, ->(method_name = nil, &block){ add_hook(e, method_name, &block) } }
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
@@ -15,23 +15,25 @@ module Ducktape
|
|
15
15
|
raise 'Cannot extend, only include.'
|
16
16
|
end
|
17
17
|
|
18
|
-
def add_hook(event, &block)
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
def add_hook(event, hook = nil, &block)
|
19
|
+
hook = block if block #block has precedence
|
20
|
+
return unless hook
|
21
|
+
hook = hook.to_s unless hook.is_a?(Proc)
|
22
|
+
self.hooks[event.to_s].unshift(hook)
|
23
|
+
hook
|
22
24
|
end
|
23
25
|
|
24
|
-
def remove_hook(event,
|
25
|
-
|
26
|
+
def remove_hook(event, hook)
|
27
|
+
hook = hook.to_s unless hook.is_a?(Proc)
|
28
|
+
self.hooks[event.to_s].delete(hook)
|
26
29
|
end
|
27
30
|
|
28
31
|
def clear_hooks(event = nil)
|
29
32
|
if event
|
30
33
|
self.hooks.delete(event.to_s)
|
31
34
|
else
|
32
|
-
self.hooks.clear
|
35
|
+
self.hooks.clear.dup
|
33
36
|
end
|
34
|
-
nil
|
35
37
|
end
|
36
38
|
|
37
39
|
protected
|
@@ -41,14 +43,19 @@ module Ducktape
|
|
41
43
|
|
42
44
|
def call_hooks(event, caller, *args)
|
43
45
|
return unless self.hooks.has_key? event.to_s
|
44
|
-
self.hooks[event.to_s].each
|
46
|
+
self.hooks[event.to_s].each do |hook|
|
47
|
+
hook = caller.method(hook) unless hook.is_a?(Proc)
|
48
|
+
hook.(event, caller, *args)
|
49
|
+
end
|
45
50
|
nil
|
46
51
|
end
|
47
52
|
|
53
|
+
# Similar to `call_hooks`, but stops calling other hooks when a hook returns a value other than nil or false.
|
48
54
|
def call_handlers(event, caller, *args)
|
49
55
|
return unless self.hooks.has_key? event.to_s
|
50
56
|
self.hooks[event.to_s].each do |hook|
|
51
|
-
|
57
|
+
hook = caller.method(hook) unless hook.is_a?(Proc)
|
58
|
+
handled = hook.(event, caller, *args)
|
52
59
|
return handled if handled
|
53
60
|
end
|
54
61
|
nil
|
data/lib/version.rb
CHANGED