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 CHANGED
@@ -188,7 +188,7 @@ class X
188
188
  bindable :name, validate: [String, Symbol]
189
189
 
190
190
  def initialize(name)
191
- self.name = name
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
- self.name = name
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
- self.name = name
270
- self.age = age
271
- self.points = points
293
+ self.name = name
294
+ self.age = age
295
+ self.points = points
272
296
 
273
- # You can hook for any method available
274
- %w'name age points'.each { |k, v| on_changed k, &method(:attribute_changed) }
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
- call_hooks(:on_loaded)
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 &method(:called_load)
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, &method(:called_load)
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 select a single hook, pass the name of the hook as a parameter. The next section has an example of this.
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.
@@ -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
 
@@ -4,7 +4,7 @@ module Ducktape
4
4
 
5
5
  class InvalidAttributeValueError < StandardError
6
6
  def initialize(name, value)
7
- super("value #{value.inspect} is invalid for attribute '#{name}'")
7
+ super("value #{value.inspect} is invalid for attribute #{name.to_s.inspect}")
8
8
  end
9
9
  end
10
10
 
@@ -7,7 +7,8 @@ module Ducktape
7
7
 
8
8
  def initialize(name, options = {})
9
9
 
10
- options.each_key { |k| puts "WARNING: invalid option #{k.inspect} for #{name.inspect} attribute. Will be ignored." unless VALID_OPTIONS.member?(k) }
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 |validation|
43
- return true if (validation.is_a?(Class) and value.is_a?(validation)) or
44
- (validation.is_a?(Proc) and validation.call(value)) or
45
- value == validation
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
@@ -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
- return unless block
20
- self.hooks[event.to_s].unshift block
21
- nil
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, block)
25
- self.hooks[event.to_s].delete(block)
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 { |hook| hook.call(event, caller, *args) }
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
- handled = hook.call(event, caller, *args)
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
@@ -1,3 +1,3 @@
1
1
  module Ducktape
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ducktape
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: