callbacks_attachable 1.2.0 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 81a1654f014d9e55c862d04cddc2220b77d08fc2
4
- data.tar.gz: 38e878bb95354ecbc13fcae519e8a35dc70c0779
3
+ metadata.gz: 8b3cbb8d4bd7ce32583ce955de64c50b6c573df8
4
+ data.tar.gz: 9f7a8fa43ff82a24fad63277df0a8966fff4bc9e
5
5
  SHA512:
6
- metadata.gz: 41df492f40cc6746035513c53a4e503d647359db01bd3b88697eb324b258a5d500178b90a26db91ecff10e4ee4b3814a7fed0f2b5f4df16cf4c907714f4e3fae
7
- data.tar.gz: 9dc191acaa137a6478b245039b7586c7ca30d73ebbfce741207ff64e4105078d54bc0075223f4f4315af37cab71837aac1882437de343c699a3f90aebc34e3d3
6
+ metadata.gz: 04afd03c6c374d2cbd40052dafd3c92f9884d8a77303643aec0b91dec3a4655887e27d5d881d5eeb78419765313040cf2e658b6f2e842a33a82a92cf7cc228b6
7
+ data.tar.gz: 48e973e7699b8ead7d098afb8589b261cdf481340a757693a9e629ed4a9906fbe1c42df06349318f6d0f47c906319f5bdbbc9581e9925857a5344e38489cf4e4
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015, 2016 Christopher Aue
3
+ Copyright (c) 2015 - 2017 Christopher Aue
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,22 +1,21 @@
1
1
  # CallbacksAttachable
2
2
 
3
- Attach callbacks to a class and trigger them for all its instances or just one
4
- particular instance. Additionally, instances can also have their own set of
5
- individual callbacks.
3
+ Attach callbacks to classes to be triggered for all instances or attach them
4
+ to an individual instance to be triggered only for this instance.
6
5
 
7
6
  ## Installation
8
7
 
9
8
  Add this line to your application's Gemfile:
10
9
 
11
10
  ```ruby
12
- gem 'callbacks_attachable'
11
+ gem 'callbacks_attachable', '~> 2.0'
13
12
  ```
14
13
 
15
14
  And then execute:
16
15
 
17
16
  $ bundle
18
17
 
19
- Or install it yourself as:
18
+ Or install it yourself:
20
19
 
21
20
  $ gem install callbacks_attachable
22
21
 
@@ -32,90 +31,72 @@ end
32
31
 
33
32
  ### Callbacks attached to the class
34
33
 
35
- Attach callbacks for an event to the class with:
34
+ Attach callbacks for an event for all instances of a class with:
36
35
 
37
36
  ```ruby
38
- callback = AClass.on(:event) do |method|
39
- puts self.__send__(method)
37
+ callback = AClass.on(:event) do |instance, method|
38
+ puts instance.__send__(method)
40
39
  end
41
- ```
42
40
 
43
- Callbacks attached to a class are executed in the context of each existing
44
- instance. To trigger all `:event` callbacks for all instances of `AClass`, use:
41
+ instance0 = AClass.new(value: 0)
42
+ instance1 = AClass.new(value: 1)
45
43
 
46
- ```ruby
47
- AClass.trigger(:event, :name) # => puts 'AClass'
44
+ AClass.trigger(:event, :value) # => 0
45
+ # 1
48
46
  ```
49
47
 
50
- The second and other arguments given to `.trigger` are passed to the callback.
51
-
52
- Callbacks attached to a class can also be triggered for just one of its
53
- instances with `.trigger_for`:
54
-
55
- ```ruby
56
- an_instance = AClass.new
57
- AClass.trigger_for(an_instance, :event, :__id__)
58
- # => puts the result of an_instance.__id__
59
- ```
48
+ The first argument of the callback is the instance the callback is executed
49
+ for. The second and further arguments given to `.trigger` are passed to the
50
+ callback as additional arguments.
60
51
 
61
- To detach a callback call `.off` with the event's name and the callback handle
62
- returned by `.on`:
52
+ Registering a callback returns a `Callback` object. To cancel the callback use
53
+ `#cancel` on that object.
63
54
 
64
55
  ```ruby
65
56
  callback = AClass.on(:event) { do_something }
66
- AClass.off(:event, callback)
57
+ callback.cancel
67
58
  ```
68
59
 
69
60
  If you want to execute a callback just a single time attach it with `.once_on`:
70
61
 
71
62
  ```ruby
72
- AClass.once_on(:singularity) { puts 'callback called!' }
73
- AClass.trigger(:singularity) # => puts 'callback called!' and immediately
74
- # detaches the callback
75
- AClass.trigger(:singularity) # => does nothing
63
+ AClass.once_on(:singular) { puts 'callback called!' }
64
+ AClass.trigger(:singular) # => puts 'callback called!' and immediately
65
+ # detaches the callback
66
+ AClass.trigger(:singular) # => does nothing
76
67
  ```
77
68
 
78
- To detach a callback when a condition is met use `.until_true_on`. The callback
79
- will be detached if it has a truthy (!) return value.
69
+ To filter the instances to call the callback for, use the `:if` option:
80
70
 
81
71
  ```ruby
82
- counter = 0
83
- AClass.until_true_on(:count_to_two) do
84
- puts counter
85
- counter >= 2
72
+ instance0 = AClass.new(value: 0)
73
+ instance1 = AClass.new(value: 1)
74
+ instance2 = AClass.new(value: 2)
75
+ AClass.on(:even, if: proc{ |inst| inst.value.even? }) do |instance|
76
+ puts instance.value
86
77
  end
87
78
 
88
- AClass.trigger(:count_to_two) # => puts 0
89
- AClass.trigger(:count_to_two) # => puts 1
90
- AClass.trigger(:count_to_two) # => puts 2 and detaches the callback
91
- AClass.trigger(:count_to_two) # => does nothing
79
+ AClass.trigger(:even) # => 0
80
+ # 2
92
81
  ```
93
82
 
83
+ This is especially useful in combination with `.once_on` to execute the
84
+ callback just for the first instance meeting certain criteria.
85
+
94
86
  ### Callbacks attached to an instance
95
87
 
96
88
  All above mentioned methods on the class level also exist for each instance of
97
- the class. Callbacks for an individual instance are executed by calling
98
- `#trigger` on it. They are also called, when calling `.trigger` or
99
- `.trigger_for(instance, ...)` on its class.
89
+ the class.
100
90
 
101
- Callbacks attached to individual instances are evaluated in their lexical scope.
102
- So, `self` inside and outside the callback is the same object.
91
+ Callbacks for an individual instance are executed by calling `#trigger` on it.
92
+ This also executes callbacks attached to the class.
103
93
 
104
94
  ```ruby
105
- an_instance1 = AClass.new
106
- an_instance2 = AClass.new
107
-
108
- callback1 = an_instance1.on(:event) { puts 'This is #1!' }
109
- callback2 = an_instance2.on(:event) { puts 'This is #2!' }
110
-
111
- an_instance1.trigger(:event) # => puts 'This is #1!'
112
- an_instance2.trigger(:event) # => puts 'This is #2!'
95
+ instance = AClass.new
113
96
 
114
- AClass.trigger(:event) # => puts 'This is #1!' and 'This is #2!'
115
-
116
- an_instance1.off(:event, callback)
117
- an_instance2.off(:event, callback)
118
- ```
97
+ AClass.on(:event) { puts 'class callback' }
98
+ instance.on(:event) { puts 'instance callback' }
119
99
 
120
- The two methods `#once_on` and `#until_true_on` are available for instances,
121
- too. They work as you'd expect.
100
+ instance.trigger(:event) # => 'class callback'
101
+ # 'instance callback'
102
+ ```
@@ -6,9 +6,8 @@ Gem::Specification.new do |spec|
6
6
  spec.version = CallbacksAttachable::VERSION
7
7
  spec.summary = %q{Attach callbacks to classes or individual instances.}
8
8
  spec.description = <<-DESC
9
- Attach callbacks to a class and trigger them for all its instances or just one
10
- particular instance. Additionally, instances can also have their own set of
11
- individual callbacks.
9
+ Attach callbacks to classes to be triggered for all instances or attach them
10
+ to an individual instance to be triggered only for this instance.
12
11
  DESC
13
12
 
14
13
  spec.homepage = "https://github.com/christopheraue/m-ruby-callbacks_attachable"
@@ -1,5 +1,4 @@
1
1
  require_relative "../mrblib/callbacks_attachable"
2
2
  require_relative "../mrblib/version"
3
3
  require_relative "../mrblib/callback_registry"
4
- require_relative "../mrblib/instance_callback"
5
- require_relative "../mrblib/all_instances_callback"
4
+ require_relative "../mrblib/callback"
data/mrbgem.rake CHANGED
@@ -4,9 +4,8 @@ MRuby::Gem::Specification.new('mruby-callbacks_attachable') do |spec|
4
4
  spec.version = CallbacksAttachable::VERSION
5
5
  spec.summary = %q{Attach callbacks to classes or individual instances.}
6
6
  spec.description = <<-DESC
7
- Attach callbacks to a class and trigger them for all its instances or just one
8
- particular instance. Additionally, instances can also have their own set of
9
- individual callbacks.
7
+ Attach callbacks to classes to be triggered for all instances or attach them
8
+ to an individual instance to be triggered only for this instance.
10
9
  DESC
11
10
 
12
11
  spec.homepage = "https://github.com/christopheraue/m-ruby-callbacks_attachable"
@@ -0,0 +1,27 @@
1
+ module CallbacksAttachable
2
+ class Callback
3
+ def initialize(registry, event, opts = {}, callback)
4
+ @registry = registry
5
+ @event = event
6
+ @call_condition = opts.fetch(:if, false)
7
+ @cancel_condition = opts.fetch(:until, false)
8
+ @callback = callback
9
+ end
10
+
11
+ def call(instance, args)
12
+ return if @call_condition and not @call_condition.call instance, *args
13
+ @callback.call(instance, *args)
14
+ cancel if @cancel_condition and @cancel_condition.call instance, *args
15
+ end
16
+
17
+ def on_cancel(&on_cancel)
18
+ @on_cancel = on_cancel
19
+ end
20
+
21
+ def cancel
22
+ @registry.deregister @event, self
23
+ @on_cancel.call if @on_cancel
24
+ true
25
+ end
26
+ end
27
+ end
@@ -1,45 +1,28 @@
1
1
  module CallbacksAttachable
2
2
  class CallbackRegistry
3
- def initialize(object, callback_class)
4
- @object = object
5
- @callback_class = callback_class
3
+ def initialize(owner)
4
+ @owner = owner
6
5
  @callbacks = {}
7
6
  end
8
7
 
9
- def on(event, opts = {}, &callback)
10
- @callbacks[event] ||= []
11
- @callbacks[event] << @callback_class.new(@object, opts, &callback)
12
- @callbacks[event].last
8
+ def register(event, opts, callback)
9
+ @callbacks[event] ||= {}
10
+ callback = Callback.new(self, event, opts, callback)
11
+ @callbacks[event][callback] = true
12
+ callback
13
13
  end
14
14
 
15
- def once_on(event, *opts, &callback)
16
- callback_registry = self
17
- registered_callback = @object.on(event, *opts) do |*args|
18
- callback_registry.off(event, registered_callback)
19
- yield(*args)
20
- end
21
- end
22
-
23
- def until_true_on(event, *opts, &callback)
24
- callback_registry = self
25
- registered_callback = @object.on(event, *opts) do |*args|
26
- yield(*args).tap do |result|
27
- callback_registry.off(event, registered_callback) if result
28
- end
29
- end
15
+ def registered?(event)
16
+ @callbacks.key? event
30
17
  end
31
18
 
32
19
  def trigger(instance, event, args)
33
- return true unless @callbacks[event]
34
-
35
- # dup the callback list so that removing callbacks while iterating does
36
- # still call all callbacks during map.
37
- @callbacks[event].dup.all?{ |callback| callback.call(instance, args) }
20
+ @callbacks[event] and @callbacks[event].keys.each{ |callback| callback.call(instance, args) }
38
21
  end
39
22
 
40
- def off(event, callback)
41
- return unless @callbacks[event]
23
+ def deregister(event, callback)
42
24
  @callbacks[event].delete(callback)
25
+ @callbacks.delete(event) if @callbacks[event].empty?
43
26
  end
44
27
  end
45
28
  end
@@ -1,67 +1,40 @@
1
1
  module CallbacksAttachable
2
- def self.included(klass)
3
- klass.extend ClassMethods
4
- end
5
-
6
- module ClassMethods
7
- def on(*args, &block)
8
- __callback_registry__.on(*args, &block)
2
+ module RegistryOwnable
3
+ def on(event, opts = {}, &callback)
4
+ __callbacks__.register(event, opts, callback)
9
5
  end
10
- alias on_event on
11
6
 
12
- def once_on(*args, &block)
13
- __callback_registry__.once_on(*args, &block)
7
+ def once_on(event, opts = {}, &callback)
8
+ on(event, opts.merge(until: proc{ true }), &callback)
14
9
  end
15
- alias once_on_event once_on
16
10
 
17
- def until_true_on(*args, &block)
18
- __callback_registry__.until_true_on(*args, &block)
19
- end
20
- alias until_true_on_event until_true_on
21
-
22
- def off(*args)
23
- __callback_registry__.off(*args)
24
- end
25
- alias off_event off
26
-
27
- def trigger(event, *args)
28
- ObjectSpace.each_object(self).all?{ |inst| inst.trigger(event, *args) }
11
+ def on?(event)
12
+ __callbacks__.registered? event
29
13
  end
30
- alias trigger_event trigger
31
14
 
32
- def __callback_registry__
33
- @__callback_registry__ ||= CallbackRegistry.new(self, AllInstancesCallback)
15
+ private def __callbacks__
16
+ @__callbacks__ ||= CallbackRegistry.new(self)
34
17
  end
35
18
  end
36
19
 
37
- def on(*args, &block)
38
- __callback_registry__.on(*args, &block)
39
- end
40
- alias on_event on
20
+ include RegistryOwnable
41
21
 
42
- def once_on(*args, &block)
43
- __callback_registry__.once_on(*args, &block)
22
+ def self.included(klass)
23
+ klass.extend ClassMethods
44
24
  end
45
- alias once_on_event once_on
46
25
 
47
- def until_true_on(*args, &block)
48
- __callback_registry__.until_true_on(*args, &block)
49
- end
50
- alias until_true_on_event until_true_on
26
+ module ClassMethods
27
+ include RegistryOwnable
51
28
 
52
- def off(*args)
53
- __callback_registry__.off(*args)
29
+ def trigger(event, *args)
30
+ ObjectSpace.each_object(self).all?{ |inst| inst.trigger(event, *args) }
31
+ end
54
32
  end
55
- alias off_event off
56
33
 
57
34
  def trigger(event, *args)
58
- self.class.__callback_registry__.trigger(self, event, args) and __callback_registry__.trigger(self, event, args)
59
- end
60
- alias trigger_event trigger
61
-
62
- private
63
-
64
- def __callback_registry__
65
- @__callback_registry__ ||= CallbackRegistry.new(self, InstanceCallback)
35
+ @class_callbacks ||= self.class.__send__ :__callbacks__
36
+ @class_callbacks.trigger(self, event, args)
37
+ @__callbacks__ and @__callbacks__.trigger(self, event, args)
38
+ true
66
39
  end
67
40
  end
data/mrblib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module CallbacksAttachable
2
- VERSION = "1.2.0"
2
+ VERSION = "2.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: callbacks_attachable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Aue
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-18 00:00:00.000000000 Z
11
+ date: 2017-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,9 +53,8 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.4'
55
55
  description: |
56
- Attach callbacks to a class and trigger them for all its instances or just one
57
- particular instance. Additionally, instances can also have their own set of
58
- individual callbacks.
56
+ Attach callbacks to classes to be triggered for all instances or attach them
57
+ to an individual instance to be triggered only for this instance.
59
58
  email:
60
59
  - rubygems@christopheraue.net
61
60
  executables: []
@@ -70,10 +69,9 @@ files:
70
69
  - callbacks_attachable.gemspec
71
70
  - lib/callbacks_attachable.rb
72
71
  - mrbgem.rake
73
- - mrblib/all_instances_callback.rb
72
+ - mrblib/callback.rb
74
73
  - mrblib/callback_registry.rb
75
74
  - mrblib/callbacks_attachable.rb
76
- - mrblib/instance_callback.rb
77
75
  - mrblib/version.rb
78
76
  homepage: https://github.com/christopheraue/m-ruby-callbacks_attachable
79
77
  licenses:
@@ -1,16 +0,0 @@
1
- module CallbacksAttachable
2
- class AllInstancesCallback
3
- def initialize(klass, opts = {}, &callback)
4
- @class = klass
5
- @skip = opts.fetch(:skip, 0)
6
- @callback = callback
7
- @call_counts = {}
8
- end
9
-
10
- def call(instance, args)
11
- @call_counts[instance.__id__] = @call_counts[instance.__id__].to_i + 1
12
- return true if @call_counts[instance.__id__] <= @skip
13
- false != instance.instance_exec(*args, &@callback)
14
- end
15
- end
16
- end
@@ -1,16 +0,0 @@
1
- module CallbacksAttachable
2
- class InstanceCallback
3
- def initialize(instance, opts = {}, &callback)
4
- @instance = instance
5
- @skip = opts.fetch(:skip, 0)
6
- @callback = callback
7
- @call_count = 0
8
- end
9
-
10
- def call(instance, args)
11
- @call_count += 1
12
- return true if @call_count <= @skip
13
- @callback.call(*args) != false
14
- end
15
- end
16
- end