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 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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in eventception.gemspec
4
+ gemspec
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
+ [![Build Status](https://travis-ci.org/dcsg/eventception.svg?branch=master)](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,11 @@
1
+ module TodoList
2
+ class Todo
3
+ attr_reader :title
4
+ attr_reader :description
5
+
6
+ def initialize(title:, description:)
7
+ @title = title
8
+ @description = description
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eventception'
4
+
5
+ module TodoList
6
+ class TodoCreatedEvent < Eventception::Event
7
+ NAME = 'todo.created'.freeze
8
+
9
+ attr_reader :todo
10
+
11
+ def initialize(todo)
12
+ @todo = todo
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module TodoList
2
+ class TodoListener
3
+ def on_creation(event)
4
+ puts "created a new to-do with title: '#{event.todo.title}' and description: '#{event.todo.description}'"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eventception
4
+ class BaseSubscriber
5
+ def subscribed_events
6
+ raise NoMethodError, 'Method needs to be implement. Consider extend this class.'
7
+ end
8
+ end
9
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Eventception
2
+ VERSION = '0.0.1'.freeze
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'eventception/event'
2
+ require 'eventception/listener_handler'
3
+ require 'eventception/dispatcher'
4
+ require 'eventception/base_subscriber'
5
+ require 'eventception/version'
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
@@ -0,0 +1,5 @@
1
+ RSpec.describe 'TodoList example' do
2
+ it 'runs without error' do
3
+ load File.expand_path('../../../examples/todo_list/run.rb', Pathname.new(__FILE__).realpath)
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'eventception'
5
+
6
+ RSpec.configure do |config|
7
+ end
@@ -0,0 +1,13 @@
1
+ module Eventception
2
+ module Support
3
+ class Listener
4
+ def on_before(_event)
5
+ puts 'before'
6
+ end
7
+
8
+ def on_after(_event)
9
+ puts 'after'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eventception'
4
+
5
+ module Eventception
6
+ module Support
7
+ class TestEvent < Eventception::Event
8
+ BEFORE = 'test.before'
9
+ AFTER = 'test.after'
10
+ end
11
+ end
12
+ 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