callbacks_attachable 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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +125 -0
- data/Rakefile +2 -0
- data/callbacks_attachable.gemspec +22 -0
- data/lib/callbacks_attachable/version.rb +3 -0
- data/lib/callbacks_attachable.rb +87 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a97fe4afa9fe03a90e7b5194914e27c8e0c8a25f
|
4
|
+
data.tar.gz: 70b01c27f7d5a7f4136e98d27021ac244ce02e6a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c7560a2ed1d41cdd5b6e80916bb77b31ae52eaa05257117f6245df012205cea3ab8f6e1fbe3b6f353fcead414a8ea21304dbe6ca4ab15005320aa470390882c8
|
7
|
+
data.tar.gz: 4ed4f18aba606945c81c9886efce2d54052378027e6f56b57bf12f7cde0184163699f6544a19fade0a5c51aa03146c60cfae88b35713f147aec7b6605124d214
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Christopher Aue
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# CallbacksAttachable
|
2
|
+
|
3
|
+
Attach callbacks to every ruby object including this mixin. Trigger all
|
4
|
+
callbacks attached under the same namespace and detach callback when you no
|
5
|
+
longer need them.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'callbacks_attachable'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install callbacks_attachable
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'callbacks_attachable'
|
27
|
+
|
28
|
+
class CallbacksHolder
|
29
|
+
include CallbacksAttachable
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
### Callbacks attached to the class
|
34
|
+
|
35
|
+
Attach callbacks to the class:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
callback = CallbacksHolder.on(:event) do |method|
|
39
|
+
puts self.__send__(method)
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
Callbacks attached to the class are executed in the context of it, i.e. `self`
|
44
|
+
points to the class.
|
45
|
+
|
46
|
+
Trigger all `:event` callbacks with:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
CallbacksHolder.trigger(:event, :name) # => puts 'CallbacksHolder'
|
50
|
+
```
|
51
|
+
|
52
|
+
The second and further arguments given to `.trigger` are passed down to the
|
53
|
+
callback.
|
54
|
+
|
55
|
+
Callbacks can be executed in the context of a different context by passing the
|
56
|
+
`context` option to `.trigger`:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
object = Object.new
|
60
|
+
CallbacksHolder.trigger(:event, :__id__, context: object)
|
61
|
+
# => puts the result of object.__id__
|
62
|
+
```
|
63
|
+
|
64
|
+
To detach a callback call `.off` with the event namespace and the callback
|
65
|
+
handle returned by `.on`:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
CallbacksHolder.off(:event, callback)
|
69
|
+
CallbacksHolder.trigger(:event, :name) # => nothing will happen
|
70
|
+
```
|
71
|
+
|
72
|
+
If you want to execute a callback just a single time attach it with `.once_on`:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
CallbacksHolder.once_on(:singularity) { puts 'callback called!' }
|
76
|
+
CallbacksHolder.trigger(:singularity) # => puts 'callback called!' and immediately
|
77
|
+
# detaches the callback
|
78
|
+
CallbacksHolder.trigger(:singularity) # => does nothing
|
79
|
+
```
|
80
|
+
|
81
|
+
To detach a callback when a condition is met use `.until_true_on`. The callback
|
82
|
+
will be detached if it has a truthy (!) return value.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
counter = 0
|
86
|
+
CallbacksHolder.until_true_on(:count_to_two) do
|
87
|
+
puts counter
|
88
|
+
counter >= 2
|
89
|
+
end
|
90
|
+
|
91
|
+
CallbacksHolder.trigger(:count_to_two) # => puts 0
|
92
|
+
CallbacksHolder.trigger(:count_to_two) # => puts 1
|
93
|
+
CallbacksHolder.trigger(:count_to_two) # => puts 2 and detaches the callback
|
94
|
+
CallbacksHolder.trigger(:count_to_two) # => does nothing
|
95
|
+
```
|
96
|
+
|
97
|
+
### Callbacks attached to an instance
|
98
|
+
|
99
|
+
All above mentioned methods on the class level also exist for each instance of
|
100
|
+
the class. Callbacks are executed if the instance they are attached to or their
|
101
|
+
class calls `#trigger`.
|
102
|
+
|
103
|
+
Callbacks are called in their lexical scope. So, `self` inside and outside
|
104
|
+
the callback is the same object. The context of a callback cannot be changed
|
105
|
+
with the `context` option of the `#trigger` method as it is possible for
|
106
|
+
class callbacks.
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
callbacks_holder1 = CallbacksHolder.new
|
110
|
+
callbacks_holder2 = CallbacksHolder.new
|
111
|
+
|
112
|
+
callback1 = callbacks_holder1.on(:event) { puts 'This is #1!' }
|
113
|
+
callback2 = callbacks_holder2.on(:event) { puts 'This is #2!' }
|
114
|
+
|
115
|
+
callbacks_holder1.trigger(:event) # => puts 'This is #1!'
|
116
|
+
callbacks_holder2.trigger(:event) # => puts 'This is #2!'
|
117
|
+
|
118
|
+
CallbacksHolder.trigger(:event) # => puts 'This is #1!' and 'This is #2!'
|
119
|
+
|
120
|
+
callbacks_holder1.off(:event, callback)
|
121
|
+
callbacks_holder2.off(:event, callback)
|
122
|
+
```
|
123
|
+
|
124
|
+
The two methods `#once_on` and `#until_true_on` are available for instances,
|
125
|
+
too, and work as you'd expect.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'callbacks_attachable/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "callbacks_attachable"
|
8
|
+
spec.version = CallbacksAttachable::VERSION
|
9
|
+
spec.authors = ["Christopher Aue"]
|
10
|
+
spec.email = ["mail@christopheraue.net"]
|
11
|
+
|
12
|
+
spec.summary = %q{Attach callbacks to ruby objects. Then, trigger and detach them.}
|
13
|
+
spec.homepage = "https://github.com/christopheraue/ruby-callbacks_attachable"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.8"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "callbacks_attachable/version"
|
2
|
+
|
3
|
+
module CallbacksAttachable
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def on(event, &callback)
|
10
|
+
callbacks[event] ||= []
|
11
|
+
callbacks[event] << callback
|
12
|
+
callback
|
13
|
+
end
|
14
|
+
|
15
|
+
def once_on(event, &callback)
|
16
|
+
klass = self
|
17
|
+
registered_callback = on(event) do |*args|
|
18
|
+
klass.off(event, registered_callback)
|
19
|
+
instance_exec(*args, &callback)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def until_true_on(event, &callback)
|
24
|
+
klass = self
|
25
|
+
registered_callback = on(event) do |*args|
|
26
|
+
klass.off(event, registered_callback) if instance_exec(*args, &callback)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def trigger(event, *args, context: self)
|
31
|
+
return true unless callbacks[event]
|
32
|
+
|
33
|
+
# dup the callback list so that removing callbacks while iterating does
|
34
|
+
# still call all callbacks during map.
|
35
|
+
callbacks[event].dup.map do |callback|
|
36
|
+
context.instance_exec(*args, &callback)
|
37
|
+
end.all?
|
38
|
+
end
|
39
|
+
|
40
|
+
def off(event, callback)
|
41
|
+
if callbacks[event]
|
42
|
+
callbacks[event].delete(callback)
|
43
|
+
callbacks.delete(event) if callbacks[event].empty?
|
44
|
+
end
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def callbacks
|
51
|
+
@callbacks ||= {}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def on(event, &block)
|
56
|
+
if_self(:on, event, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def once_on(event, &block)
|
60
|
+
if_self(:until_true_on, event) do |*args|
|
61
|
+
block.call(*args)
|
62
|
+
true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def until_true_on(event, &block)
|
67
|
+
if_self(:until_true_on, event, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
def trigger(event, *args)
|
71
|
+
self.class.trigger(event, *args, context: self)
|
72
|
+
end
|
73
|
+
|
74
|
+
def off(event, callback)
|
75
|
+
self.class.off(event, callback)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def if_self(method, event, &block)
|
81
|
+
instance = self
|
82
|
+
self.class.__send__(method, event) do |*args|
|
83
|
+
next false if instance != self
|
84
|
+
block.call(*args)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: callbacks_attachable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christopher Aue
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- mail@christopheraue.net
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- ".rspec"
|
50
|
+
- ".travis.yml"
|
51
|
+
- Gemfile
|
52
|
+
- LICENSE.txt
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- callbacks_attachable.gemspec
|
56
|
+
- lib/callbacks_attachable.rb
|
57
|
+
- lib/callbacks_attachable/version.rb
|
58
|
+
homepage: https://github.com/christopheraue/ruby-callbacks_attachable
|
59
|
+
licenses:
|
60
|
+
- MIT
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 2.4.8
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: Attach callbacks to ruby objects. Then, trigger and detach them.
|
82
|
+
test_files: []
|