event_dispatcher 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d8af04d32455e73210d27f128e787bc377211342
4
+ data.tar.gz: c380073bd768ca64c093d5faaa06f07c0ff5e1d6
5
+ SHA512:
6
+ metadata.gz: beda259764f924a76b4b329e329c630496c5521ed32c32b6a8eacc3af38b764a2eae8de03aee7af62d36fc28792752e09808a75a442436a1ee4eedbd905c6937
7
+ data.tar.gz: 3f235e2205c5ca51a5a140c247fa313bf6f39a1f6fbe90c135b6762c53c2f3877e36a9e5bc6eab111644e52bcc263d7c690bb9f0068c6a48abf4762e290c0502
@@ -0,0 +1,7 @@
1
+ * Massive refactoring of the existing classes to allow more abstraction and breacking code apart into more logical pieces.
2
+ * Using Class `Struct` as Data container for listeners.
3
+ * Commenting all methods for better understanding.
4
+ * Adding more test unit for maximum quality assurance.
5
+ * Extending documentation.
6
+
7
+
@@ -0,0 +1,96 @@
1
+ event_dispatcher
2
+ ====================
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/event_dispatcher.png)](http://badge.fury.io/rb/event_dispatcher)
5
+
6
+ event_dispatcher gem provides a simple observer implementation, allowing you to subscribe and listen for events in your application with a simple and effective way.
7
+
8
+ It is very strongly inspired by the [Symfony EventDispatcher component](http://symfony.com/components/EventDispatcher)
9
+
10
+ ## Install
11
+ Install the gem :
12
+
13
+ gem install event_dispatcher
14
+
15
+ Accessing the gem :
16
+
17
+ require 'event_dispatcher'
18
+
19
+ ## Usage
20
+ ### Creating an Event
21
+ When an event is dispatched, it's identified by a unique name, which any number of listeners might be listening to. An event instance is also created and passed to all of the listeners :
22
+
23
+ class UserEvent
24
+ attr_reader :user, :login_time
25
+
26
+ def initialize(user, login_time)
27
+ @user = user
28
+ @login_time = login_time
29
+ end
30
+
31
+ def log
32
+ "#{login_time} : User #{user} has just logged in."
33
+ end
34
+ end
35
+
36
+ ### Creating an EventDispatcher
37
+ The Dispatcher is the commander of the event dispatcher system, it maintains a registry of listeners. It's responsible for notifying listeners when events are dispatched :
38
+
39
+ dispatcher = EventDispatcher::Dispatcher.new
40
+
41
+ ### Connecting Listeners
42
+ To take advantage of an existing event, a listener needs to be connected to the dispatcher so that it can be notified when the event is dispatched. There are two ways for attaching a listener to the dispatcher.
43
+
44
+ Using a block :
45
+
46
+ listener = lambda do |event|
47
+ puts event.log
48
+ end
49
+
50
+ dispatcher.add_listener(:user_login, listener)
51
+
52
+ Or using a class instance with a handler method for the event :
53
+
54
+ class UserEventListener
55
+ def handle(event)
56
+ puts event.log
57
+ end
58
+ end
59
+
60
+ listener = UserEventListener.new
61
+ dispatcher.add_listener(:user_login, listener.method(:handle))
62
+
63
+ You may also specify a priority when subscribing to events. Listeners with higher priority will be run first :
64
+
65
+ dispatcher.add_listener(:user_login, listener1, 1)
66
+ dispatcher.add_listener(:user_login, listener2, 2)
67
+ dispatcher.add_listener(:user_login, listener3, 3)
68
+
69
+
70
+ ### Dispatching an Event
71
+ Notifies all listeners of the given event, so the event instance is then passed to each listener :
72
+
73
+ event = UserEvent.new('Bobby', Time.now)
74
+ dispatcher.dispatch(:user_login, event)
75
+
76
+ ### Stopping Event propagation
77
+ You may wish to stop the propagation of an event to other listeners. To do so you need to first mixing the module Event in your custom event class, then setting the event instance variable `stop_propagation` to true :
78
+
79
+ class UserEvent
80
+ include EventDispatcher::Event
81
+ # ...
82
+ # ...
83
+ end
84
+
85
+ listener = lambda do |event|
86
+ puts event.log
87
+ event.stop_propagation = true
88
+ end
89
+
90
+ ## Tests
91
+ rake test
92
+
93
+ ## Licence
94
+ LGPL, see LICENCE.
95
+
96
+
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.verbose = true
6
+ end
7
+
8
+ desc "Run event_dispatcher tests"
9
+ task :default => :test
@@ -1,75 +1,75 @@
1
1
  module EventDispatcher
2
2
  class Dispatcher
3
+ attr_reader :listeners
4
+
3
5
  def initialize
4
6
  @listeners = {}
5
- @sorted = {}
6
7
  end
7
-
8
- def get_listeners( event_name = nil )
9
- if event_name
10
- sort_listeners!( event_name ) if @sorted[event_name].nil?
11
8
 
12
- return @sorted[event_name]
13
- end
9
+ # Checks if the given event has some listeners that want to listen to.
10
+ def has_listeners?(event_name)
11
+ event_name = symbolize_key(event_name)
12
+ @listeners.key?(event_name) && @listeners[event_name].size > 0 ? true : false
13
+ end
14
14
 
15
- @listeners.each_key { |event_name| sort_listeners!( event_name ) if @sorted[event_name].nil? }
15
+ # Using Struct as data container for each listener.
16
+ Listener = Struct.new(:listener, :priority)
16
17
 
17
- return @sorted
18
- end
18
+ # Connects a listener to the dispatcher so that it can be notified when an event is dispatched.
19
+ def add_listener(event_name, listener, priority = 0)
20
+ raise ArgumentError.new("Priority must be a Fixnum") unless priority.is_a?(Fixnum)
21
+ event_name = symbolize_key(event_name)
22
+ @listeners[event_name] ||= []
23
+ @listeners[event_name] << Listener.new(listener, priority)
19
24
 
20
- def has_listeners?( event_name = nil )
21
- get_listeners( event_name ).count > 0 ? true : false
25
+ sort_listeners!(event_name)
22
26
  end
23
-
24
- def add_listener( event_name, listener, priority = 0 )
25
- @listeners[event_name] ||= {}
26
- @listeners[event_name][priority] ||= []
27
- @listeners[event_name][priority].push(listener)
28
27
 
29
- @sorted[event_name].clear unless @sorted[event_name].nil?
30
- end
31
-
32
- def remove_listener( event_name, listener )
33
- return if @listeners[event_name].nil?
28
+ # Removes an event listener from the specified event.
29
+ def remove_listener!(event_name, listener)
30
+ event_name = symbolize_key(event_name)
31
+ return unless @listeners.key?(event_name) && !listener.nil?
32
+ @listeners[event_name].delete_if { |l| l.listener.eql?(listener) }
34
33
 
35
- @listeners[event_name].each_pair do |priority, listeners|
36
- listeners.delete_if { |item| p listener == item }
34
+ sort_listeners!(event_name)
35
+ end
36
+
37
+ # Removes a collection of event listeners attached to a specified event, or reset the entire container of listeners if no event is given.
38
+ def remove_listeners!(event_name = nil)
39
+ if event_name.nil?
40
+ @listeners.clear
41
+ else
42
+ event_name = symbolize_key(event_name)
43
+ @listeners.delete(event_name) if @listeners.key?(event_name)
37
44
  end
38
-
39
- @sorted[event_name].clear unless @sorted[event_name].nil?
40
45
  end
41
-
42
- def dispatch( event_name, event = nil )
43
- return if @listeners[event_name].nil? || event.nil?
44
-
45
- do_dispatch( get_listeners( event_name ), event )
46
-
46
+
47
+ # Notifies all listeners of the given event. The event instance is then passed to each listener of that event.
48
+ def dispatch(event_name, event)
49
+ event_name = symbolize_key(event_name)
50
+ return unless @listeners.key?(event_name) && !event.nil?
51
+ @listeners[event_name].each do |l|
52
+ invoke_callable(l.listener, event)
53
+ # A Listener is able to tell the dispatcher to stop all propagation of the event to future listeners.
54
+ break if event.respond_to?(:stop_propagation) && event.stop_propagation
55
+ end
47
56
  end
48
57
 
49
58
  private
50
-
51
- def sort_listeners!( event_name )
52
- @sorted[event_name] = {}
53
-
54
- if @listeners[event_name]
55
- @sorted[event_name] = @listeners[event_name].sort
56
-
57
- end
59
+ # For better performance we convert each string key into symbol one.
60
+ def symbolize_key(key)
61
+ key.to_sym rescue key
58
62
  end
59
-
60
- def do_dispatch( listeners, event )
61
- listeners.each do |priority|
62
- priority[1].each do |listener|
63
- if listener.is_a?(Hash)
64
- listener[:object].send( listener[:method], event )
65
- elsif listener.respond_to?(:call)
66
- listener.call( event )
67
- else
68
- raise ArgumentError.new("Listener must be a Hash or Block")
69
- end
70
- end
71
- end
63
+
64
+ # Sorts the list of listeners attached to given event by priority. The higher the priority, the earlier the listener is called.
65
+ def sort_listeners!(event_name)
66
+ @listeners[event_name].sort_by! { |l| l.priority }
72
67
  end
73
68
 
69
+ # Calls the listener instance method which is responsible of handling the event object.
70
+ def invoke_callable(callable, event)
71
+ raise ArgumentError.new("Listener must be a block or a callable object's method") unless callable.respond_to?(:call)
72
+ callable.call(event)
73
+ end
74
74
  end
75
75
  end
@@ -1,15 +1,9 @@
1
1
  module EventDispatcher
2
2
  module Event
3
- def propagation_stopped
4
- @propagation_stopped ||= false
5
- end
6
-
7
- def stop_propagation
8
- @propagation_stopped = true
9
- end
10
-
11
- def propagation_stopped?
12
- propagation_stopped
3
+ attr_accessor :stop_propagation
4
+ # A listener can prevent any other listeners from being called.
5
+ def initialize
6
+ @stop_propagation = false
13
7
  end
14
8
  end
15
9
  end
@@ -0,0 +1,155 @@
1
+ require 'test/unit'
2
+ require 'event_dispatcher'
3
+
4
+
5
+ class DispatcherTest < Test::Unit::TestCase
6
+ PREFOO = :pre_foo
7
+ POSTFOO = :post_foo
8
+
9
+ def setup
10
+ @dispatcher = EventDispatcher::Dispatcher.new
11
+ @listener = TestEventListener.new
12
+ end
13
+
14
+ def test_initial_state
15
+ assert_empty @dispatcher.listeners
16
+
17
+ assert_equal false, @dispatcher.has_listeners?(PREFOO)
18
+ assert_equal false, @dispatcher.has_listeners?(POSTFOO)
19
+ end
20
+
21
+ def test_add_listener
22
+ @dispatcher.add_listener(:pre_foo, @listener)
23
+ @dispatcher.add_listener(:post_foo, @listener)
24
+
25
+ assert_equal true, @dispatcher.has_listeners?(PREFOO)
26
+ assert_equal true, @dispatcher.has_listeners?(POSTFOO)
27
+ assert_equal 1, @dispatcher.listeners[PREFOO].size
28
+ assert_equal 1, @dispatcher.listeners[PREFOO].size
29
+ assert_equal 2, @dispatcher.listeners.size
30
+ end
31
+
32
+ def test_remove_listeners
33
+ @dispatcher.add_listener(PREFOO, @listener)
34
+ @dispatcher.add_listener(POSTFOO, @listener)
35
+ @dispatcher.remove_listeners!(PREFOO)
36
+
37
+ assert_equal false, @dispatcher.has_listeners?(PREFOO)
38
+ assert_equal true, @dispatcher.has_listeners?(POSTFOO)
39
+ assert_equal 1, @dispatcher.listeners.size
40
+
41
+ @dispatcher.remove_listeners!
42
+
43
+ assert_empty @dispatcher.listeners
44
+ end
45
+
46
+ def test_remove_listener!
47
+ listener1 = TestEventListener.new
48
+ listener2 = TestEventListener.new
49
+ listener3 = TestEventListener.new
50
+
51
+ @dispatcher.add_listener(:pre_foo, listener1)
52
+ @dispatcher.add_listener(:pre_foo, listener2)
53
+ @dispatcher.add_listener(:pre_foo, listener3)
54
+
55
+ assert_equal true, @dispatcher.has_listeners?(PREFOO)
56
+ assert_equal 3, @dispatcher.listeners[PREFOO].size
57
+
58
+ @dispatcher.remove_listener!(:pre_foo, listener1)
59
+ @dispatcher.remove_listener!(:pre_foo, listener2)
60
+
61
+ assert_equal true, @dispatcher.has_listeners?(PREFOO)
62
+ assert_equal 1, @dispatcher.listeners.size
63
+
64
+ @dispatcher.remove_listener!(:not_exists, listener3)
65
+
66
+ assert_equal true, @dispatcher.has_listeners?(PREFOO)
67
+ assert_equal 1, @dispatcher.listeners.size
68
+ end
69
+
70
+ def test_listener_sorts_by_priority
71
+ listener1 = TestEventListener.new
72
+ listener2 = TestEventListener.new
73
+ listener3 = TestEventListener.new
74
+ listener4 = TestEventListener.new
75
+ listener5 = TestEventListener.new
76
+
77
+ @dispatcher.add_listener(:pre_foo, listener1, 0)
78
+ @dispatcher.add_listener(:pre_foo, listener2, -10)
79
+ @dispatcher.add_listener(:pre_foo, listener3, 10)
80
+ @dispatcher.add_listener(:post_foo, listener4)
81
+ @dispatcher.add_listener(:post_foo, listener5, 10)
82
+
83
+ assert_equal [:pre_foo, :post_foo], @dispatcher.listeners.keys
84
+ @dispatcher.listeners
85
+
86
+ assert_equal true, @dispatcher.listeners[PREFOO].first.include?(-10)
87
+ assert_equal true, @dispatcher.listeners[PREFOO].last.include?(10)
88
+ assert_equal true, @dispatcher.listeners[PREFOO][1].include?(0)
89
+ assert_equal true, @dispatcher.listeners[POSTFOO].first.include?(0)
90
+ assert_equal true, @dispatcher.listeners[POSTFOO].last.include?(10)
91
+ end
92
+
93
+ def test_dispatcher_for_block
94
+ invoked = 0
95
+ foo = '
96
+ '
97
+ block1 = lambda do |event|
98
+ invoked += 1
99
+ foo = event.foo
100
+ end
101
+
102
+ @dispatcher.add_listener(:pre_foo, block1)
103
+ test_event = FooEvent.new
104
+ @dispatcher.dispatch(:pre_foo, test_event)
105
+
106
+ assert_equal 1, invoked
107
+ assert_equal 'foo', foo
108
+ end
109
+
110
+ def test_dispatcher_for_class
111
+
112
+ assert_equal 'bar', @listener.bar
113
+ test_event = FooEvent.new
114
+ @dispatcher.add_listener(:pre_foo, @listener.method(:handle))
115
+ @dispatcher.dispatch(:pre_foo, test_event)
116
+
117
+ assert_equal 'foo', @listener.bar
118
+ end
119
+
120
+ def test_stop_event_propagation
121
+ other_listener = TestEventListener.new
122
+ test_event = FooEvent.new
123
+ @dispatcher.add_listener(:pre_foo, @listener.method(:handle),10)
124
+ @dispatcher.add_listener(:pre_foo, other_listener.method(:handle))
125
+ @dispatcher.dispatch(:pre_foo, test_event)
126
+
127
+ assert_equal 'foo', other_listener.bar
128
+ assert_equal 'bar', @listener.bar
129
+ end
130
+
131
+ end
132
+
133
+ class TestEventListener
134
+
135
+ attr_accessor :bar
136
+
137
+ def initialize
138
+ @bar = 'bar'
139
+ end
140
+
141
+ def handle(event)
142
+ @bar = event.foo
143
+ event.stop_propagation = true
144
+ end
145
+ end
146
+
147
+ class FooEvent
148
+ include EventDispatcher::Event
149
+ attr_accessor :foo
150
+
151
+ def initialize
152
+ @foo = 'foo'
153
+ end
154
+ end
155
+
@@ -0,0 +1,21 @@
1
+ require 'test/unit'
2
+ require 'event_dispatcher'
3
+
4
+ class EventTest < Test::Unit::TestCase
5
+ def setup
6
+ @event = TestEventClass.new
7
+ end
8
+
9
+ def test_if_propagation_stopped
10
+ assert_equal false, @event.stop_propagation
11
+ end
12
+
13
+ def test_stop_propagation_and_if_propagation_stopped
14
+ @event.stop_propagation = true
15
+ assert_equal true, @event.stop_propagation
16
+ end
17
+ end
18
+
19
+ class TestEventClass
20
+ include EventDispatcher::Event
21
+ end
metadata CHANGED
@@ -1,49 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_dispatcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Amine Asli
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-10-01 00:00:00.000000000 Z
11
+ date: 2013-12-17 00:00:00.000000000 Z
13
12
  dependencies: []
14
- description: EventDispatcher implements a lightweight version of the Observer design
15
- pattern. It's a ruby port of the well known Symfony EventDispatcher
13
+ description: event_dispatcher gem provides a simple observer implementation, allowing
14
+ you to subscribe and listen for events in your application in a simple and effective
15
+ way. It is very strongly inspired by the Symfony EventDispatcher component
16
16
  email: phobosapp@yahoo.com
17
17
  executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - Rakefile
22
+ - README.md
23
+ - CHANGELOG.md
21
24
  - lib/event_dispatcher.rb
22
- - lib/event_dispatcher/dispatcher.rb
23
25
  - lib/event_dispatcher/event.rb
26
+ - lib/event_dispatcher/dispatcher.rb
27
+ - test/test_event.rb
28
+ - test/test_dispatcher.rb
24
29
  homepage: https://github.com/ThatAmine/event_dispatcher
25
30
  licenses:
26
31
  - LGPL
32
+ metadata: {}
27
33
  post_install_message:
28
34
  rdoc_options: []
29
35
  require_paths:
30
36
  - lib
31
37
  required_ruby_version: !ruby/object:Gem::Requirement
32
- none: false
33
38
  requirements:
34
- - - ! '>='
39
+ - - '>='
35
40
  - !ruby/object:Gem::Version
36
- version: '0'
41
+ version: 1.9.3
37
42
  required_rubygems_version: !ruby/object:Gem::Requirement
38
- none: false
39
43
  requirements:
40
- - - ! '>='
44
+ - - '>='
41
45
  - !ruby/object:Gem::Version
42
46
  version: '0'
43
47
  requirements: []
44
48
  rubyforge_project:
45
- rubygems_version: 1.8.11
49
+ rubygems_version: 2.1.11
46
50
  signing_key:
47
- specification_version: 3
48
- summary: EventDispatcher implements a lightweight version of the Observer design pattern.
49
- test_files: []
51
+ specification_version: 4
52
+ summary: event_dispatcher implements a lightweight version of the Observer design
53
+ pattern.
54
+ test_files:
55
+ - test/test_event.rb
56
+ - test/test_dispatcher.rb