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.
@@ -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
 
@@ -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 without a bunch of unrelated IO stuff this is the solution for you. (If you want to monitor IO events check out EventMachine)}
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
 
@@ -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{
@@ -1,5 +1,6 @@
1
1
  module Eventable
2
2
  module Errors
3
3
  UnknownEvent = Class.new(StandardError)
4
+ SuperNotCalledInInitialize = Class.new(ScriptError)
4
5
  end
5
6
  end
@@ -15,13 +15,13 @@ module Eventable
15
15
 
16
16
  # register an event
17
17
  def event(event_name)
18
- @events ||= []
19
- @events << event_name unless @events.include? event_name
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
- @events.clone
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
- @mutex ||= Mutex.new
37
- @mutex.synchronize{
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
- @mutex ||= Mutex.new
65
- @mutex.synchronize {
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
- @mutex ||= Mutex.new
88
- @mutex.synchronize {
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)
@@ -1,3 +1,3 @@
1
1
  module Eventable
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -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.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-10 00:00:00 Z
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 without a bunch of unrelated IO stuff this is the solution for you. (If you want to monitor IO events check out EventMachine)
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
@@ -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
-
@@ -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}