eventception 0.0.1
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/CHANGELOG.md +10 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +65 -0
- data/README.md +86 -0
- data/Rakefile +17 -0
- data/eventception.gemspec +28 -0
- data/examples/todo_list/run.rb +19 -0
- data/examples/todo_list/todo.rb +11 -0
- data/examples/todo_list/todo_created_event.rb +15 -0
- data/examples/todo_list/todo_listener.rb +7 -0
- data/lib/eventception/base_subscriber.rb +9 -0
- data/lib/eventception/dispatcher.rb +188 -0
- data/lib/eventception/event.rb +19 -0
- data/lib/eventception/listener_handler.rb +23 -0
- data/lib/eventception/priority_listeners.rb +46 -0
- data/lib/eventception/version.rb +3 -0
- data/lib/eventception.rb +5 -0
- data/pkg/eventception-0.0.1.gem +0 -0
- data/spec/eventception/dispatcher_spec.rb +211 -0
- data/spec/eventception/listener_handler_spec.rb +75 -0
- data/spec/examples/todo_list_spec.rb +5 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/listener.rb +13 -0
- data/spec/support/subscriber.rb +23 -0
- data/spec/support/test_event.rb +12 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: caefa105ae332455e90b0834581a77c3c90cf44b
|
4
|
+
data.tar.gz: d4f3bf6f98004732be5c9edd84f20790f941319f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 88d5ca641b33ac10cc87ef059f1ec870aa2706a779a2b9fd91211cb421efa02b3819656b4a92393117ed6410686c633b982ce2ef2ab8ffaa4af27df5b97698a9
|
7
|
+
data.tar.gz: 20629ad6754729360fce12e56eaa0b13c01807b99add1f3a33ce3ecf9e91fc50cbcf4a176a5b79d1fddd5c8fc32aa0e289530bd729582141e94e39090d11cb90
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
|
3
|
+
|
4
|
+
## [Unreleased]
|
5
|
+
|
6
|
+
## 0.0.1 - 2017-07-13
|
7
|
+
|
8
|
+
First release
|
9
|
+
|
10
|
+
[Unreleased]: https://github.com/dcsg/eventception/compare/0.0.1...HEAD
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
eventception (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.3.0)
|
10
|
+
byebug (9.0.6)
|
11
|
+
coderay (1.1.1)
|
12
|
+
diff-lcs (1.3)
|
13
|
+
method_source (0.8.2)
|
14
|
+
parallel (1.11.2)
|
15
|
+
parser (2.4.0.0)
|
16
|
+
ast (~> 2.2)
|
17
|
+
powerpack (0.1.1)
|
18
|
+
pry (0.10.4)
|
19
|
+
coderay (~> 1.1.0)
|
20
|
+
method_source (~> 0.8.1)
|
21
|
+
slop (~> 3.4)
|
22
|
+
pry-byebug (3.4.2)
|
23
|
+
byebug (~> 9.0)
|
24
|
+
pry (~> 0.10)
|
25
|
+
rainbow (2.2.2)
|
26
|
+
rake
|
27
|
+
rake (12.0.0)
|
28
|
+
rspec (3.6.0)
|
29
|
+
rspec-core (~> 3.6.0)
|
30
|
+
rspec-expectations (~> 3.6.0)
|
31
|
+
rspec-mocks (~> 3.6.0)
|
32
|
+
rspec-core (3.6.0)
|
33
|
+
rspec-support (~> 3.6.0)
|
34
|
+
rspec-expectations (3.6.0)
|
35
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
36
|
+
rspec-support (~> 3.6.0)
|
37
|
+
rspec-mocks (3.6.0)
|
38
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
39
|
+
rspec-support (~> 3.6.0)
|
40
|
+
rspec-support (3.6.0)
|
41
|
+
rubocop (0.49.1)
|
42
|
+
parallel (~> 1.10)
|
43
|
+
parser (>= 2.3.3.1, < 3.0)
|
44
|
+
powerpack (~> 0.1)
|
45
|
+
rainbow (>= 1.99.1, < 3.0)
|
46
|
+
ruby-progressbar (~> 1.7)
|
47
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
48
|
+
ruby-progressbar (1.8.1)
|
49
|
+
slop (3.6.0)
|
50
|
+
unicode-display_width (1.3.0)
|
51
|
+
|
52
|
+
PLATFORMS
|
53
|
+
ruby
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
bundler (~> 1.15)
|
57
|
+
eventception!
|
58
|
+
pry
|
59
|
+
pry-byebug
|
60
|
+
rake
|
61
|
+
rspec (~> 3.6)
|
62
|
+
rubocop
|
63
|
+
|
64
|
+
BUNDLED WITH
|
65
|
+
1.15.1
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
## Eventception
|
2
|
+
|
3
|
+
[](https://travis-ci.org/dcsg/eventception)
|
4
|
+
|
5
|
+
Eventception is a lightweight and simple Ruby Event System inspired on [Symfony Event Dispatcher](https://symfony.com/doc/current/components/event_dispatcher.html).
|
6
|
+
|
7
|
+
## How to Install
|
8
|
+
|
9
|
+
Add the following to your `Gemfile`:
|
10
|
+
```ruby
|
11
|
+
gem 'eventception', '~> 0.0.1'
|
12
|
+
```
|
13
|
+
|
14
|
+
## How to use
|
15
|
+
|
16
|
+
#### Events
|
17
|
+
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. As you'll see later, the Event object itself often contains data about the event being dispatched.
|
18
|
+
|
19
|
+
#### The Dispatcher
|
20
|
+
The dispatcher is the central object of the event dispatcher system.
|
21
|
+
In general, a single dispatcher is created, which maintains a registry of listeners.
|
22
|
+
When an event is dispatched via the dispatcher, it notifies all listeners registered with that event:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
require 'eventception'
|
26
|
+
|
27
|
+
dispatcher = Eventception::Dispatcher.new
|
28
|
+
```
|
29
|
+
|
30
|
+
#### Adding Listeners
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
require 'eventception'
|
34
|
+
class TodoListener
|
35
|
+
def on_creation(event)
|
36
|
+
puts "created a new to-do with title: '#{event.todo.title}'"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
listener = TodoListener.new
|
41
|
+
dispatcher.add_listener(
|
42
|
+
event_name: 'todo.created',
|
43
|
+
listener: listener,
|
44
|
+
listener_method: 'on_creation'
|
45
|
+
)
|
46
|
+
```
|
47
|
+
|
48
|
+
#### Creating and Dispatching an Event
|
49
|
+
|
50
|
+
##### Creating the Event
|
51
|
+
```ruby
|
52
|
+
require 'eventception'
|
53
|
+
class TodoCreatedEvent < Eventception::Event
|
54
|
+
NAME = 'todo.created'.freeze
|
55
|
+
|
56
|
+
attr_reader :todo
|
57
|
+
|
58
|
+
def initialize(todo)
|
59
|
+
@todo = todo
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
##### Dispatching the Event
|
65
|
+
```ruby
|
66
|
+
class Todo
|
67
|
+
attr_reader :title
|
68
|
+
|
69
|
+
def initialize(title:)
|
70
|
+
@title = title
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
todo = Todo.new('My To-do')
|
75
|
+
event = TodoCreatedEvent.new(todo)
|
76
|
+
dispatcher.dispatch(TodoCreatedEvent::NAME, event)
|
77
|
+
|
78
|
+
# STDOUT: created a new to-do with title: 'My To-do'
|
79
|
+
```
|
80
|
+
|
81
|
+
## Contributing
|
82
|
+
|
83
|
+
## Contributors
|
84
|
+
|
85
|
+
* [Daniel Gomes](https://github.com/dcsg)
|
86
|
+
* [Ivo Anjo](https://github.com/ivoanjo)
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup :default, :test, :development
|
4
|
+
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
9
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
task :spec
|
13
|
+
|
14
|
+
require 'rubocop/rake_task'
|
15
|
+
RuboCop::RakeTask.new
|
16
|
+
|
17
|
+
task default: %i[rubocop spec]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
4
|
+
require 'eventception'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'eventception'
|
8
|
+
spec.version = Eventception::VERSION
|
9
|
+
spec.authors = ['Daniel Gomes', 'Ivo Anjo']
|
10
|
+
spec.email = ['danielcesargomes@gmail.com', 'ivo.anjo@ist.utl.pt']
|
11
|
+
spec.homepage = 'https://github.com/dcsg/eventception'
|
12
|
+
spec.summary = 'Eventception - a lightweight and simple Ruby Event System.'
|
13
|
+
spec.description = 'A lightweight and simple Ruby Event System inspired on Symfony Event Dispatcher.'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = Dir['**/*'].keep_if { |file| File.file?(file) }
|
17
|
+
spec.test_files = Dir['spec/**/*']
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
spec.required_ruby_version = '>= 2.2.0'
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.15'
|
22
|
+
spec.add_development_dependency 'rspec', '~> 3.6'
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'rubocop'
|
25
|
+
spec.add_development_dependency 'pry'
|
26
|
+
spec.add_development_dependency 'pry-byebug' unless RUBY_PLATFORM == 'java'
|
27
|
+
spec.add_development_dependency 'pry-debugger-jruby' if RUBY_PLATFORM == 'java'
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'eventception'
|
2
|
+
require_relative 'todo_created_event'
|
3
|
+
require_relative 'todo_listener'
|
4
|
+
require_relative 'todo'
|
5
|
+
|
6
|
+
# configure the event dispatcher and register the listener
|
7
|
+
dispatcher = Eventception::Dispatcher.new
|
8
|
+
dispatcher.add_listener(
|
9
|
+
event_name: TodoList::TodoCreatedEvent::NAME,
|
10
|
+
listener: TodoList::TodoListener.new,
|
11
|
+
listener_method: 'on_creation',
|
12
|
+
)
|
13
|
+
|
14
|
+
# create a new to-do and dispatch the event
|
15
|
+
todo = TodoList::Todo.new(title: 'Emit and Event', description: 'Event emitted')
|
16
|
+
event = TodoList::TodoCreatedEvent.new(todo)
|
17
|
+
dispatcher.dispatch(event_name: TodoList::TodoCreatedEvent::NAME, event: event)
|
18
|
+
|
19
|
+
# STDOUT: "created a new to-do with title: 'Emit and Event' and description: 'Event emitted'"
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'eventception/priority_listeners'
|
2
|
+
require 'eventception/listener_handler'
|
3
|
+
|
4
|
+
module Eventception
|
5
|
+
class Dispatcher
|
6
|
+
private
|
7
|
+
|
8
|
+
def event_listeners
|
9
|
+
@event_listeners ||= Hash.new { |hash, key|
|
10
|
+
priority_listeners_hash = Hash.new { |priority_hash, priority_level|
|
11
|
+
priority_hash[priority_level] = PriorityListeners.new(priority: priority_level)
|
12
|
+
}
|
13
|
+
|
14
|
+
hash[key] = priority_listeners_hash
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def sorted
|
19
|
+
@sorted ||= Hash.new { |hash, key| hash[key] = {} }
|
20
|
+
end
|
21
|
+
|
22
|
+
public
|
23
|
+
|
24
|
+
# Dispatches an event to all registered listeners.
|
25
|
+
#
|
26
|
+
# == Parameters:
|
27
|
+
# event_name::
|
28
|
+
# The name of the event to dispatch. The name of
|
29
|
+
# the event is the name of the method that is
|
30
|
+
# invoked on listeners.
|
31
|
+
# event::
|
32
|
+
# The event to pass to the event handlers/listeners
|
33
|
+
# If not supplied, an empty Event instance is created.
|
34
|
+
#
|
35
|
+
# == Returns:
|
36
|
+
# The Event.
|
37
|
+
#
|
38
|
+
def dispatch(event_name:, event: Eventception::Event.new)
|
39
|
+
if listeners_for?(event_name: event_name)
|
40
|
+
do_dispatch(listeners: listeners_for(event_name: event_name), event: event)
|
41
|
+
end
|
42
|
+
|
43
|
+
event
|
44
|
+
end
|
45
|
+
|
46
|
+
# Gets all listeners sorted by descending priority.
|
47
|
+
#
|
48
|
+
# == Returns:
|
49
|
+
# All event listeners sorted by event_name and descending priority.
|
50
|
+
#
|
51
|
+
def listeners
|
52
|
+
return [] if event_listeners.empty?
|
53
|
+
|
54
|
+
event_listeners.each_key do |event_name|
|
55
|
+
sort_listeners(event_name) if sorted[event_name].empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
sorted
|
59
|
+
end
|
60
|
+
|
61
|
+
# Checks whether are any registered listeners.
|
62
|
+
#
|
63
|
+
# == Returns:
|
64
|
+
# Boolean
|
65
|
+
#
|
66
|
+
def listeners?
|
67
|
+
listeners.any?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Gets all listeners for the specific event sorted by descending priority.
|
71
|
+
#
|
72
|
+
# == Returns:
|
73
|
+
# The event listeners for the specific event sorted by descending priority.
|
74
|
+
#
|
75
|
+
def listeners_for(event_name:)
|
76
|
+
return [] if event_listeners[event_name].empty?
|
77
|
+
|
78
|
+
sort_listeners(event_name) if sorted[event_name].empty?
|
79
|
+
|
80
|
+
sorted[event_name]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Checks whether are any registered listeners for the specific event.
|
84
|
+
#
|
85
|
+
# == Returns:
|
86
|
+
# Boolean
|
87
|
+
#
|
88
|
+
def listeners_for?(event_name:)
|
89
|
+
event_listeners[event_name].any?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Add an event listener that listens to the specified event.
|
93
|
+
#
|
94
|
+
# == Parameters:
|
95
|
+
# event_name::
|
96
|
+
# The event to listen on
|
97
|
+
# listener::
|
98
|
+
# The listener
|
99
|
+
# priority::
|
100
|
+
# The higher this value, the earlier an event listener will be triggered in the chain (defaults to 0)
|
101
|
+
#
|
102
|
+
def add_listener(event_name:, listener:, listener_method:, priority: 0)
|
103
|
+
event_listeners[event_name][priority] << ListenerHandler.new(listener, listener_method)
|
104
|
+
sorted.delete(event_name)
|
105
|
+
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def remove_listener(event_name:, listener:, listener_method:)
|
110
|
+
return unless listeners_for?(event_name: event_name)
|
111
|
+
|
112
|
+
listener_for_event = event_listeners.fetch(event_name)
|
113
|
+
|
114
|
+
listener_for_event.each do |priority, priority_listeners|
|
115
|
+
sorted.delete(event_name) if priority_listeners.delete(ListenerHandler.new(listener, listener_method))
|
116
|
+
|
117
|
+
listener_for_event.delete(priority) if priority_listeners.empty?
|
118
|
+
end
|
119
|
+
|
120
|
+
event_listeners.delete(event_name) if listener_for_event.empty?
|
121
|
+
end
|
122
|
+
|
123
|
+
# Add an event subscriber.
|
124
|
+
#
|
125
|
+
# The subscriber is asked for all the events he is interested in and added as a listener for these events.
|
126
|
+
#
|
127
|
+
# == Parameters:
|
128
|
+
# subscriber::
|
129
|
+
# The subscriber
|
130
|
+
#
|
131
|
+
def add_subscriber(subscriber:)
|
132
|
+
subscriber.subscribed_events.each do |event_subscribed|
|
133
|
+
add_listener(
|
134
|
+
event_name: event_subscribed.fetch(:event_name),
|
135
|
+
listener: subscriber,
|
136
|
+
listener_method: event_subscribed.fetch(:listener_method),
|
137
|
+
priority: event_subscribed[:priority] || 0,
|
138
|
+
)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def remove_subscriber(subscriber:)
|
143
|
+
subscriber.subscribed_events.each do |event_subscribed|
|
144
|
+
remove_listener(
|
145
|
+
event_name: event_subscribed.fetch(:event_name),
|
146
|
+
listener: subscriber,
|
147
|
+
listener_method: event_subscribed.fetch(:listener_method),
|
148
|
+
)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
protected
|
153
|
+
|
154
|
+
# Triggers the listeners of an event.
|
155
|
+
#
|
156
|
+
# This method can be overridden to add functionality that is executed for each listener.
|
157
|
+
#
|
158
|
+
# == Parameters:
|
159
|
+
# listeners::
|
160
|
+
# The event listeners
|
161
|
+
# event::
|
162
|
+
# The event
|
163
|
+
#
|
164
|
+
def do_dispatch(listeners:, event:)
|
165
|
+
listeners.each do |_priority, priority_listeners|
|
166
|
+
priority_listeners.each do |listener_handler|
|
167
|
+
return nil if event.propagation_stopped?
|
168
|
+
|
169
|
+
listener_handler.call(event)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
nil
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
# Sorts the internal list of listeners for the given event by priority.
|
179
|
+
#
|
180
|
+
# == Parameters:
|
181
|
+
# event_name::
|
182
|
+
# The event name
|
183
|
+
#
|
184
|
+
def sort_listeners(event_name)
|
185
|
+
sorted[event_name] = event_listeners[event_name].sort_by { |key, _| -key }.to_h
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Eventception
|
2
|
+
class Event
|
3
|
+
private
|
4
|
+
|
5
|
+
def propagation_stopped
|
6
|
+
@propagation_stopped ||= false
|
7
|
+
end
|
8
|
+
|
9
|
+
public
|
10
|
+
|
11
|
+
def propagation_stopped?
|
12
|
+
propagation_stopped
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop_propagation
|
16
|
+
@propagation_stopped = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Eventception
|
2
|
+
class ListenerHandler
|
3
|
+
attr_reader :listener
|
4
|
+
attr_reader :method
|
5
|
+
|
6
|
+
def initialize(listener, method)
|
7
|
+
@listener = listener
|
8
|
+
@method = method
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(event)
|
12
|
+
listener.public_send(method, event)
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
eql?(other)
|
17
|
+
end
|
18
|
+
|
19
|
+
def eql?(other)
|
20
|
+
listener == other.listener && method == other.method
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Eventception
|
2
|
+
class PriorityListeners
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
attr_reader :listeners
|
8
|
+
|
9
|
+
public
|
10
|
+
|
11
|
+
attr_reader :priority
|
12
|
+
|
13
|
+
def initialize(priority:)
|
14
|
+
@priority = priority
|
15
|
+
@listeners = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def <<(listener)
|
19
|
+
listeners << listener
|
20
|
+
end
|
21
|
+
|
22
|
+
def <=>(other)
|
23
|
+
other.priority <=> priority
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(listener)
|
27
|
+
listeners.delete(listener)
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(&block)
|
31
|
+
listeners.each(&block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def size
|
35
|
+
listeners.size
|
36
|
+
end
|
37
|
+
|
38
|
+
def count
|
39
|
+
size
|
40
|
+
end
|
41
|
+
|
42
|
+
def empty?
|
43
|
+
listeners.empty?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/eventception.rb
ADDED
Binary file
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/listener'
|
3
|
+
require 'support/subscriber'
|
4
|
+
require 'support/test_event'
|
5
|
+
require 'eventception/listener_handler'
|
6
|
+
|
7
|
+
describe Eventception::Dispatcher do
|
8
|
+
subject(:dispatcher) { described_class.new }
|
9
|
+
|
10
|
+
let(:listener) { Eventception::Support::Listener.new }
|
11
|
+
let(:listener_method) { 'on_before' }
|
12
|
+
let(:subscriber) { Eventception::Support::Subscriber.new }
|
13
|
+
let(:event_name) { :on_after }
|
14
|
+
let(:priority) { 0 }
|
15
|
+
|
16
|
+
describe '#listeners?' do
|
17
|
+
context 'when no event_name is provided' do
|
18
|
+
context 'and does not have any listeners associated' do
|
19
|
+
it do
|
20
|
+
expect(dispatcher.listeners?).to be false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'and has one or more listeners associated' do
|
25
|
+
before do
|
26
|
+
dispatcher.add_listener(event_name: event_name, listener: listener, listener_method: listener_method)
|
27
|
+
end
|
28
|
+
|
29
|
+
it do
|
30
|
+
expect(dispatcher.listeners?).to be true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when an event_name is provided' do
|
36
|
+
context 'and does not have any listener associated' do
|
37
|
+
it do
|
38
|
+
expect(dispatcher.listeners_for?(event_name: event_name)).to be false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'and has one or more listeners associated' do
|
43
|
+
before do
|
44
|
+
dispatcher.add_listener(event_name: event_name, listener: listener, listener_method: listener_method)
|
45
|
+
end
|
46
|
+
|
47
|
+
it do
|
48
|
+
expect(dispatcher.listeners_for?(event_name: event_name)).to be true
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when a different event_name is provided without any listener associated' do
|
52
|
+
it do
|
53
|
+
expect(dispatcher.listeners_for?(event_name: :invalid)).to be false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#add_listener' do
|
61
|
+
context 'when no priority is provided' do
|
62
|
+
before do
|
63
|
+
dispatcher.add_listener(event_name: event_name, listener: listener, listener_method: listener_method)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'adds a new listener to the event_name with 0 priority' do
|
67
|
+
listeners = dispatcher.listeners_for(event_name: event_name)
|
68
|
+
|
69
|
+
expect(listeners[priority].count).to eq 1
|
70
|
+
expect(listeners[priority].first.listener).to eq listener
|
71
|
+
expect(listeners[priority].first.method).to eq listener_method
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'when priority of 10 is provided' do
|
76
|
+
let(:priority) { 10 }
|
77
|
+
|
78
|
+
before do
|
79
|
+
dispatcher.add_listener(
|
80
|
+
event_name: event_name,
|
81
|
+
listener: listener,
|
82
|
+
listener_method: listener_method,
|
83
|
+
priority: priority,
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'adds a new listener to the event_name with a different priority' do
|
88
|
+
listeners = dispatcher.listeners_for(event_name: event_name)
|
89
|
+
|
90
|
+
expect(listeners[priority].count).to eq 1
|
91
|
+
expect(listeners[priority].first.listener).to eq listener
|
92
|
+
expect(listeners[priority].first.method).to eq listener_method
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#listeners' do
|
98
|
+
context 'when listeners exist' do
|
99
|
+
before do
|
100
|
+
dispatcher.add_listener(event_name: event_name, listener: listener, listener_method: listener_method)
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when no event_name is provided' do
|
104
|
+
it 'adds a new listener to the event_name with 0 priority' do
|
105
|
+
listeners = dispatcher.listeners
|
106
|
+
|
107
|
+
expect(listeners.size).to eq 1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'when an event_name is provided' do
|
112
|
+
it 'adds a new listener to the event_name with 10 priority' do
|
113
|
+
listeners = dispatcher.listeners_for(event_name: event_name)
|
114
|
+
|
115
|
+
expect(listeners[priority].count).to eq 1
|
116
|
+
expect(listeners[priority].first.listener).to eq listener
|
117
|
+
expect(listeners[priority].first.method).to eq listener_method
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#remove_listener' do
|
124
|
+
let(:listener_method2) { 'on_after' }
|
125
|
+
|
126
|
+
context 'when two listeners exist for the same event_name' do
|
127
|
+
before do
|
128
|
+
dispatcher.add_listener(event_name: event_name, listener: listener, listener_method: listener_method)
|
129
|
+
dispatcher.add_listener(event_name: event_name, listener: listener, listener_method: listener_method2)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'has two listeners' do
|
133
|
+
expect(dispatcher.listeners_for?(event_name: event_name)).to be true
|
134
|
+
expect(dispatcher.listeners_for(event_name: event_name)[priority].size).to eq 2
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'and one of the listeners is removed' do
|
138
|
+
it 'has only one listener' do
|
139
|
+
dispatcher.remove_listener(event_name: event_name, listener: listener, listener_method: listener_method)
|
140
|
+
expect(dispatcher.listeners_for(event_name: event_name)[priority].size).to eq 1
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'and both listeners are removed' do
|
145
|
+
it 'has zero listeners' do
|
146
|
+
dispatcher.remove_listener(event_name: event_name, listener: listener, listener_method: listener_method)
|
147
|
+
dispatcher.remove_listener(event_name: event_name, listener: listener, listener_method: listener_method2)
|
148
|
+
expect(dispatcher.listeners_for?(event_name: event_name)).to be false
|
149
|
+
expect(dispatcher.listeners_for(event_name: event_name).size).to eq 0
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe '#add_subscriber' do
|
156
|
+
before do
|
157
|
+
dispatcher.add_subscriber(subscriber: subscriber)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'subscribes two events' do
|
161
|
+
listeners = dispatcher.listeners
|
162
|
+
expect(listeners.size).to eq 2
|
163
|
+
listeners.each do |_event_name, listener|
|
164
|
+
expect(listener[priority].size).to eq 1
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#remove_subscriber' do
|
170
|
+
before do
|
171
|
+
dispatcher.add_subscriber(subscriber: subscriber)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'removes the subscriber listeners' do
|
175
|
+
expect(dispatcher.listeners.size).to eq 2
|
176
|
+
dispatcher.remove_subscriber(subscriber: subscriber)
|
177
|
+
expect(dispatcher.listeners?).to be false
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe '#dispatch' do
|
182
|
+
context 'when using subscribers' do
|
183
|
+
before do
|
184
|
+
dispatcher.add_subscriber(subscriber: subscriber)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'calls the subscriber methods' do
|
188
|
+
expect(subscriber).to receive('on_before').and_call_original
|
189
|
+
expect(subscriber).to receive('on_after').and_call_original
|
190
|
+
|
191
|
+
expect {
|
192
|
+
dispatcher.dispatch(event_name: Eventception::Support::TestEvent::BEFORE)
|
193
|
+
}.to output(/on before, propagation stopped/).to_stdout
|
194
|
+
expect {
|
195
|
+
dispatcher.dispatch(event_name: Eventception::Support::TestEvent::AFTER)
|
196
|
+
}.to output(/on after, propagation stopped/).to_stdout
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context 'when using listeners' do
|
201
|
+
before do
|
202
|
+
dispatcher.add_listener(event_name: event_name, listener: listener, listener_method: listener_method)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'executes the listener method' do
|
206
|
+
expect(listener).to receive(listener_method).and_call_original
|
207
|
+
expect { dispatcher.dispatch(event_name: event_name) }.to output(/before/).to_stdout
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'eventception'
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'support/listener'
|
4
|
+
require 'support/subscriber'
|
5
|
+
require 'support/test_event'
|
6
|
+
require 'support/listener'
|
7
|
+
|
8
|
+
describe Eventception::ListenerHandler do
|
9
|
+
subject(:event_handler) { described_class.new(listener, listener_method) }
|
10
|
+
|
11
|
+
let(:listener_method) { 'on_before' }
|
12
|
+
let(:listener_method2) { 'on_after' }
|
13
|
+
let(:listener) { Eventception::Support::Listener.new }
|
14
|
+
let(:listener2) { Eventception::Support::Listener.new }
|
15
|
+
|
16
|
+
describe '#call' do
|
17
|
+
it 'executes the listener method' do
|
18
|
+
expect(listener).to receive(listener_method)
|
19
|
+
|
20
|
+
event_handler.call(Eventception::Event.new)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#==' do
|
25
|
+
context 'when the two event handlers have the same listener and method' do
|
26
|
+
let(:event_handler2) { described_class.new(listener, listener_method) }
|
27
|
+
|
28
|
+
it do
|
29
|
+
expect(event_handler == event_handler2).to be true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when the two event handlers have different listener and same method' do
|
34
|
+
let(:event_handler2) { described_class.new(listener2, listener_method) }
|
35
|
+
|
36
|
+
it do
|
37
|
+
expect(event_handler == event_handler2).to be false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when the two event handlers have same listener but different method' do
|
42
|
+
let(:event_handler2) { described_class.new(listener, listener_method2) }
|
43
|
+
|
44
|
+
it do
|
45
|
+
expect(event_handler == event_handler2).to be false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#eql?' do
|
51
|
+
context 'when the two event handlers have the same listener and method' do
|
52
|
+
let(:event_handler2) { described_class.new(listener, listener_method) }
|
53
|
+
|
54
|
+
it do
|
55
|
+
expect(event_handler.eql?(event_handler2)).to be true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when the two event handlers have different listener and same method' do
|
60
|
+
let(:event_handler2) { described_class.new(listener2, listener_method) }
|
61
|
+
|
62
|
+
it do
|
63
|
+
expect(event_handler.eql?(event_handler2)).to be false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when the two event handlers have same listener but different method' do
|
68
|
+
let(:event_handler2) { described_class.new(listener, listener_method2) }
|
69
|
+
|
70
|
+
it do
|
71
|
+
expect(event_handler.eql?(event_handler2)).to be false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'eventception'
|
2
|
+
require_relative 'test_event'
|
3
|
+
|
4
|
+
module Eventception
|
5
|
+
module Support
|
6
|
+
class Subscriber < Eventception::BaseSubscriber
|
7
|
+
def subscribed_events
|
8
|
+
[
|
9
|
+
{ event_name: TestEvent::BEFORE, listener_method: 'on_before', priority: 0 },
|
10
|
+
{ event_name: TestEvent::AFTER, listener_method: 'on_after' },
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_before(event)
|
15
|
+
puts "on before, propagation stopped? #{event.propagation_stopped?}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_after(event)
|
19
|
+
puts "on after, propagation stopped? #{event.propagation_stopped?}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: eventception
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Gomes
|
8
|
+
- Ivo Anjo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-07-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.15'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.15'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rspec
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '3.6'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '3.6'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rubocop
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: pry
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: pry-byebug
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
description: A lightweight and simple Ruby Event System inspired on Symfony Event
|
99
|
+
Dispatcher.
|
100
|
+
email:
|
101
|
+
- danielcesargomes@gmail.com
|
102
|
+
- ivo.anjo@ist.utl.pt
|
103
|
+
executables: []
|
104
|
+
extensions: []
|
105
|
+
extra_rdoc_files: []
|
106
|
+
files:
|
107
|
+
- CHANGELOG.md
|
108
|
+
- Gemfile
|
109
|
+
- Gemfile.lock
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- eventception.gemspec
|
113
|
+
- examples/todo_list/run.rb
|
114
|
+
- examples/todo_list/todo.rb
|
115
|
+
- examples/todo_list/todo_created_event.rb
|
116
|
+
- examples/todo_list/todo_listener.rb
|
117
|
+
- lib/eventception.rb
|
118
|
+
- lib/eventception/base_subscriber.rb
|
119
|
+
- lib/eventception/dispatcher.rb
|
120
|
+
- lib/eventception/event.rb
|
121
|
+
- lib/eventception/listener_handler.rb
|
122
|
+
- lib/eventception/priority_listeners.rb
|
123
|
+
- lib/eventception/version.rb
|
124
|
+
- pkg/eventception-0.0.1.gem
|
125
|
+
- spec/eventception/dispatcher_spec.rb
|
126
|
+
- spec/eventception/listener_handler_spec.rb
|
127
|
+
- spec/examples/todo_list_spec.rb
|
128
|
+
- spec/spec_helper.rb
|
129
|
+
- spec/support/listener.rb
|
130
|
+
- spec/support/subscriber.rb
|
131
|
+
- spec/support/test_event.rb
|
132
|
+
homepage: https://github.com/dcsg/eventception
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 2.2.0
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.6.11
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Eventception - a lightweight and simple Ruby Event System.
|
156
|
+
test_files:
|
157
|
+
- spec/eventception/dispatcher_spec.rb
|
158
|
+
- spec/eventception/listener_handler_spec.rb
|
159
|
+
- spec/examples/todo_list_spec.rb
|
160
|
+
- spec/spec_helper.rb
|
161
|
+
- spec/support/listener.rb
|
162
|
+
- spec/support/subscriber.rb
|
163
|
+
- spec/support/test_event.rb
|