eventable 0.1.1 → 0.1.2
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.markdown +32 -3
- data/eventable.gemspec +1 -1
- data/example/example.rb +6 -0
- data/lib/eventable/errors.rb +1 -0
- data/lib/eventable/eventable.rb +19 -9
- data/lib/eventable/version.rb +1 -1
- data/spec/eventable/eventable_spec.rb +42 -0
- metadata +3 -5
- data/_brainstorm/finalizer.rb +0 -48
- data/_brainstorm/splat.rb +0 -27
data/README.markdown
CHANGED
@@ -16,6 +16,19 @@ Eventable will even automatically remove registered listeners when they get garb
|
|
16
16
|
|
17
17
|
Eventable also allows for more fine-grain control than Observable. You can register for specific events instead of just saying, "Hey, let me know when anything changes."
|
18
18
|
|
19
|
+
##Install##
|
20
|
+
|
21
|
+
`$ gem install eventable`
|
22
|
+
|
23
|
+
##Usage Instructions##
|
24
|
+
|
25
|
+
* Include the module
|
26
|
+
* **Important:** If you have an initialize method call `super` as first line (see below). If you don't have an initialize method you don't have to add one. Super is called automatically for you.
|
27
|
+
* Add an event, e.g. `event :your_event`
|
28
|
+
* Fire the event when it should be fired: `fire_event(:your_event)`
|
29
|
+
|
30
|
+
To reiterate you **must** call `super` in your `initialize` method or Eventable won't work and you'll get an error. Eventable needs to create a mutex to make it thread safe, if you don't call `super` the mutex variable won't be created.
|
31
|
+
|
19
32
|
##Examples##
|
20
33
|
This example shows the basics of using Eventable to add an event to a class and listen for that event. (Without threading it's a bit pointless):
|
21
34
|
|
@@ -27,6 +40,9 @@ This example shows the basics of using Eventable to add an event to a class and
|
|
27
40
|
# This is all you have to do to add an event (after you include Eventable)
|
28
41
|
event :stuff_happens
|
29
42
|
|
43
|
+
# There's no initialize method so you do
|
44
|
+
# not have to worry about calling super.
|
45
|
+
|
30
46
|
# don't name your method fire_event, that's taken
|
31
47
|
def do_event
|
32
48
|
puts "firing :stuff_happens"
|
@@ -70,6 +86,12 @@ This example shows you how you might actually use it in a multi-threaded environ
|
|
70
86
|
include Eventable
|
71
87
|
event :stuff_happens
|
72
88
|
event :other_stuff_happens
|
89
|
+
|
90
|
+
def initialize
|
91
|
+
# If you don't call super Eventable will raise an error
|
92
|
+
super # <= VERY important, comment this out to see the error
|
93
|
+
# do your initialize stuff
|
94
|
+
end
|
73
95
|
|
74
96
|
def make_stuff_happen(parent_id)
|
75
97
|
# You handle concurrency however you want, threads or fibers, up to you.
|
@@ -141,16 +163,23 @@ This example shows you how you might actually use it in a multi-threaded environ
|
|
141
163
|
puts "all done"
|
142
164
|
|
143
165
|
|
144
|
-
|
145
166
|
##Version History##
|
146
167
|
|
168
|
+
Ver: 0.1.2
|
169
|
+
|
170
|
+
Design updates/fixes:
|
171
|
+
|
172
|
+
* Renamed most instance variables to help avoid name collisions.
|
173
|
+
* Threadsafe mutex creation. Make sure you call `super` in your class's initialize method!
|
174
|
+
|
147
175
|
**2011.06.10**
|
148
176
|
Ver: 0.1.1
|
149
177
|
|
150
|
-
Features:
|
178
|
+
Features:
|
151
179
|
If events fired specifically returns true and returns false if it can't for whatever reason (e.g. no listeners registered).
|
152
180
|
|
153
|
-
Fixes:
|
181
|
+
Fixes:
|
182
|
+
|
154
183
|
* Throws error if event is fired and no listeners are registered (Thanks for bug report Benjamin Yu)
|
155
184
|
* Workaround for RubyGems pre 1.8.3 date bug that locks up all of RubyGems (Thanks again Benjamin Yu)
|
156
185
|
|
data/eventable.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.email = ["mikbe.tk@gmail.com"]
|
14
14
|
s.homepage = "http://mikbe.tk/projects#eventable"
|
15
15
|
s.summary = %q{An incredibly simple and easy to use event mixin module.}
|
16
|
-
s.description = %q{Provides an easy to use and understand event model. If you want a simple, light-weight way to add events to your classes
|
16
|
+
s.description = %q{Provides an easy to use and understand event model. If you want a simple, light-weight way to add events to your classes this is the solution for you.}
|
17
17
|
|
18
18
|
s.add_development_dependency('rspec', "~>2.6")
|
19
19
|
|
data/example/example.rb
CHANGED
@@ -6,6 +6,12 @@ class EventedClass
|
|
6
6
|
event :stuff_happens
|
7
7
|
event :other_stuff_happens
|
8
8
|
|
9
|
+
def initialize
|
10
|
+
# If you don't call super Eventable will raise an error
|
11
|
+
super # <= VERY important, comment this out to see the error
|
12
|
+
# do your initialize stuff
|
13
|
+
end
|
14
|
+
|
9
15
|
def make_stuff_happen(parent_id)
|
10
16
|
# You handle concurrency however you want, threads or fibers, up to you.
|
11
17
|
Thread.new{
|
data/lib/eventable/errors.rb
CHANGED
data/lib/eventable/eventable.rb
CHANGED
@@ -15,13 +15,13 @@ module Eventable
|
|
15
15
|
|
16
16
|
# register an event
|
17
17
|
def event(event_name)
|
18
|
-
@
|
19
|
-
@
|
18
|
+
@eventable_events ||= []
|
19
|
+
@eventable_events << event_name unless @eventable_events.include? event_name
|
20
20
|
end
|
21
21
|
|
22
22
|
# returns a list of registered events
|
23
23
|
def events
|
24
|
-
@
|
24
|
+
@eventable_events.clone
|
25
25
|
end
|
26
26
|
|
27
27
|
end
|
@@ -30,11 +30,15 @@ module Eventable
|
|
30
30
|
self.class.events
|
31
31
|
end
|
32
32
|
|
33
|
+
def initialize
|
34
|
+
@eventable_mutex = Mutex.new
|
35
|
+
end
|
36
|
+
|
33
37
|
# When the event happens the class where it happens runs this
|
34
38
|
def fire_event(event, *return_value, &block)
|
35
39
|
# We don't want the callback array being altered when we're trying to read it
|
36
|
-
|
37
|
-
@
|
40
|
+
check_mutex
|
41
|
+
@eventable_mutex.synchronize {
|
38
42
|
|
39
43
|
return false unless @callbacks && @callbacks[event] && !@callbacks[event].empty?
|
40
44
|
|
@@ -61,8 +65,8 @@ module Eventable
|
|
61
65
|
raise Errors::UnknownEvent unless events.include? event
|
62
66
|
|
63
67
|
# Make access to the callback cache threadsafe
|
64
|
-
|
65
|
-
@
|
68
|
+
check_mutex
|
69
|
+
@eventable_mutex.synchronize {
|
66
70
|
@callbacks ||= {}
|
67
71
|
@callbacks[event] ||= {}
|
68
72
|
|
@@ -84,8 +88,8 @@ module Eventable
|
|
84
88
|
|
85
89
|
# Allows objects to stop listening to events
|
86
90
|
def unregister_for_event(args)
|
87
|
-
|
88
|
-
@
|
91
|
+
check_mutex
|
92
|
+
@eventable_mutex.synchronize {
|
89
93
|
event = args[:event]
|
90
94
|
return unless @callbacks && @callbacks[event]
|
91
95
|
|
@@ -98,6 +102,12 @@ module Eventable
|
|
98
102
|
}
|
99
103
|
end
|
100
104
|
|
105
|
+
private
|
106
|
+
|
107
|
+
def check_mutex
|
108
|
+
raise Errors::SuperNotCalledInInitialize, "You must include super in your class's initialize method" unless @eventable_mutex
|
109
|
+
end
|
110
|
+
|
101
111
|
# Wrapper for the finalize proc. You have to call a method
|
102
112
|
# from define_finalizer; you can't just put this proc in there.
|
103
113
|
def unregister_finalizer(event, listener_id, callback)
|
data/lib/eventable/version.rb
CHANGED
@@ -7,6 +7,48 @@ describe Eventable do
|
|
7
7
|
@listener = ListenClass.new
|
8
8
|
end
|
9
9
|
|
10
|
+
context "when inheriting eventable" do
|
11
|
+
|
12
|
+
it "should not raise an error if class has no initialize method" do
|
13
|
+
lambda{
|
14
|
+
class Foo
|
15
|
+
include Eventable
|
16
|
+
event :do_stuff
|
17
|
+
end
|
18
|
+
f = Foo.new
|
19
|
+
f.fire_event(:do_stuff)
|
20
|
+
}.should_not raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should not raise an error if super is called in initialize" do
|
24
|
+
lambda{
|
25
|
+
class Foo
|
26
|
+
include Eventable
|
27
|
+
event :do_stuff
|
28
|
+
def initialize
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
f = Foo.new
|
33
|
+
f.fire_event(:do_stuff)
|
34
|
+
}.should_not raise_error
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should raise an error if super is not called in initialize" do
|
38
|
+
lambda{
|
39
|
+
class Foo
|
40
|
+
include Eventable
|
41
|
+
event :do_stuff
|
42
|
+
def initialize
|
43
|
+
end
|
44
|
+
end
|
45
|
+
f = Foo.new
|
46
|
+
f.fire_event(:do_stuff)
|
47
|
+
}.should raise_error(Eventable::Errors::SuperNotCalledInInitialize)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
10
52
|
context "when specifiying an event" do
|
11
53
|
|
12
54
|
it 'should list the event from the class' do
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: eventable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Mike Bethany
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-18 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -23,7 +23,7 @@ dependencies:
|
|
23
23
|
version: "2.6"
|
24
24
|
type: :development
|
25
25
|
version_requirements: *id001
|
26
|
-
description: Provides an easy to use and understand event model. If you want a simple, light-weight way to add events to your classes
|
26
|
+
description: Provides an easy to use and understand event model. If you want a simple, light-weight way to add events to your classes this is the solution for you.
|
27
27
|
email:
|
28
28
|
- mikbe.tk@gmail.com
|
29
29
|
executables: []
|
@@ -38,8 +38,6 @@ files:
|
|
38
38
|
- LICENSE.txt
|
39
39
|
- README.markdown
|
40
40
|
- Rakefile
|
41
|
-
- _brainstorm/finalizer.rb
|
42
|
-
- _brainstorm/splat.rb
|
43
41
|
- autotest/discover.rb
|
44
42
|
- eventable.gemspec
|
45
43
|
- example/example.rb
|
data/_brainstorm/finalizer.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
# how does the finalizer work?
|
2
|
-
# I need to unregister the object when the finalizer
|
3
|
-
# is called but will it be called if I'm holding a reference to it?
|
4
|
-
|
5
|
-
class Foo
|
6
|
-
def self.finalize(object_id)
|
7
|
-
proc {puts "finalize me: #{object_id}"}
|
8
|
-
end
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
ObjectSpace.define_finalizer( self, self.class.finalize(self.object_id))
|
12
|
-
end
|
13
|
-
|
14
|
-
def hello
|
15
|
-
puts "hello: #{self.object_id}"
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
@y = []
|
21
|
-
def external_finalizer(object_id)
|
22
|
-
proc do
|
23
|
-
puts "external finalizer: #{object_id}"
|
24
|
-
@y.delete(object_id)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
(0..4).each do |i|
|
29
|
-
f = Foo.new
|
30
|
-
ObjectSpace.define_finalizer(f, external_finalizer(f.object_id))
|
31
|
-
GC.start
|
32
|
-
@y << f.object_id
|
33
|
-
puts "i: #{i}"
|
34
|
-
end
|
35
|
-
|
36
|
-
@y.each do |object_id|
|
37
|
-
begin
|
38
|
-
obj = ObjectSpace._id2ref(object_id)
|
39
|
-
obj.hello
|
40
|
-
rescue RangeError => e
|
41
|
-
puts "das error: #{!!e.message.match(/is recycled object/)}"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
puts "all done : note the finalizers firing AFTER this..."
|
46
|
-
|
47
|
-
|
48
|
-
|
data/_brainstorm/splat.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
def foo(a,b,c)
|
2
|
-
puts
|
3
|
-
puts "foo"
|
4
|
-
puts "a:#{a}; b: #{b}; c: #{c};"
|
5
|
-
end
|
6
|
-
|
7
|
-
def foo_explicit_block(a, b, c, &block)
|
8
|
-
puts
|
9
|
-
puts "foo_with_block"
|
10
|
-
puts "a:#{a}; b: #{b}; c: #{c};"
|
11
|
-
puts block.inspect
|
12
|
-
end
|
13
|
-
|
14
|
-
def foo_implied_block(a,b,c)
|
15
|
-
puts
|
16
|
-
puts "foo_implied_block"
|
17
|
-
puts "a:#{a}; b: #{b}; c: #{c};"
|
18
|
-
yield [a,b,c]
|
19
|
-
end
|
20
|
-
|
21
|
-
def bar(*args, &block)
|
22
|
-
foo(*args, &block)
|
23
|
-
foo_explicit_block(*args, &block)
|
24
|
-
foo_implied_block(*args, &block)
|
25
|
-
end
|
26
|
-
|
27
|
-
bar(1,2,3) {|x| puts x}
|