ducktape 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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