Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Immutable Dispatcher #13

Merged
merged 3 commits into from
Jul 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ Style/TrailingCommaInArguments:

Style/BlockDelimiters:
EnforcedStyle: semantic

Style/SpaceInsideHashLiteralBraces:
Enabled: false
20 changes: 18 additions & 2 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2017-07-13 18:23:49 +0100 using RuboCop version 0.49.1.
# on 2017-07-14 08:36:02 +0100 using RuboCop version 0.49.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 4
# Offense count: 5
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
Max: 169

# Offense count: 1
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 11

# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods.
# SupportedStyles: line_count_based, semantic, braces_for_chaining
# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
# FunctionalMethods: let, let!, subject, watch
# IgnoredMethods: lambda, proc, it
Style/BlockDelimiters:
Exclude:
- 'lib/eventception/immutable_dispatcher.rb'

# Offense count: 2
# Cop supports --auto-correct.
Style/MutableConstant:
Expand Down
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@ All notable changes to this project will be documented in this file. This change

## [Unreleased]

## 0.0.1 - 2017-07-13
#### Adds

* Your contribution here.
* [#13](https://github.com/dcsg/eventception/pull/13): Add an Immutable Dispatcher - [@dcsg](https://github.com/dcsg).

#### Changes

* Your contribution here.
* [#11](https://github.com/dcsg/eventception/pull/11): Change the license from MIT to GNU LGPLv3 - [@dcsg](https://github.com/dcsg).

## v0.0.1 - 2017-07-13

First release

[Unreleased]: https://github.com/dcsg/eventception/compare/0.0.1...HEAD
[Unreleased]: https://github.com/dcsg/eventception/compare/v0.0.1...HEAD
1 change: 1 addition & 0 deletions lib/eventception.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'eventception/event'
require 'eventception/listener_handler'
require 'eventception/dispatcher'
require 'eventception/immutable_dispatcher'
require 'eventception/base_subscriber'
require 'eventception/version'
54 changes: 54 additions & 0 deletions lib/eventception/dispatcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ def listeners?

# Gets all listeners for the specific event sorted by descending priority.
#
# == Parameters:
# event_name::
# The name of the event
#
# == Returns:
# The event listeners for the specific event sorted by descending priority.
#
Expand All @@ -104,6 +108,10 @@ def listeners_for(event_name:)

# Checks whether are any registered listeners for the specific event.
#
# == Parameters:
# event_name::
# The name of the event
#
# == Returns:
# Boolean
#
Expand All @@ -118,16 +126,34 @@ def listeners_for?(event_name:)
# The event to listen on
# listener::
# The listener
# listener_method::
# The name of the method to be executed in the listener
# priority::
# The higher this value, the earlier an event listener will be triggered in the chain (defaults to 0)
#
# == Returns:
# Nil
#
def add_listener(event_name:, listener:, listener_method:, priority: 0)
event_listeners[event_name][priority] << ListenerHandler.new(listener, listener_method)
sorted.delete(event_name)

nil
end

# Removes an event listener from the specified events.
#
# == Parameters:
# event_name::
# The event to listen on
# listener::
# The listener
# listener_method::
# The name of the method to be executed in the listener
#
# == Returns:
# Nil
#
def remove_listener(event_name:, listener:, listener_method:)
return unless listeners_for?(event_name: event_name)

Expand All @@ -140,6 +166,8 @@ def remove_listener(event_name:, listener:, listener_method:)
end

event_listeners.delete(event_name) if listener_for_event.empty?

nil
end

# Add an event subscriber.
Expand All @@ -150,6 +178,9 @@ def remove_listener(event_name:, listener:, listener_method:)
# subscriber::
# The subscriber
#
# == Returns:
# Nil
#
def add_subscriber(subscriber:)
subscriber.subscribed_events.each do |event_subscribed|
add_listener(
Expand All @@ -159,8 +190,21 @@ def add_subscriber(subscriber:)
priority: event_subscribed[:priority] || 0,
)
end

nil
end

# Removes an event subscriber.
#
# The subscriber is asked for all the events he is interested in and added as a listener for these events.
#
# == Parameters:
# subscriber::
# The subscriber
#
# == Returns:
# Nil
#
def remove_subscriber(subscriber:)
subscriber.subscribed_events.each do |event_subscribed|
remove_listener(
Expand All @@ -169,6 +213,8 @@ def remove_subscriber(subscriber:)
listener_method: event_subscribed.fetch(:listener_method),
)
end

nil
end

protected
Expand All @@ -183,6 +229,9 @@ def remove_subscriber(subscriber:)
# event::
# The event
#
# == Returns:
# Nil
#
def do_dispatch(listeners:, event:)
listeners.each do |_priority, priority_listeners|
priority_listeners.each do |listener_handler|
Expand All @@ -203,8 +252,13 @@ def do_dispatch(listeners:, event:)
# event_name::
# The event name
#
# == Returns:
# Nil
#
def sort_listeners(event_name)
sorted[event_name] = event_listeners[event_name].sort_by { |key, _| -key }.to_h

nil
end
end
end
122 changes: 122 additions & 0 deletions lib/eventception/immutable_dispatcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# frozen_string_literal: true

# Eventception: A lightweight and simple Ruby Event System
# Copyright (C) 2017 Daniel Gomes <danielcesargomes@gmail.com>
#
# This file is part of Eventception.
#
# Eventception is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Eventception is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Eventception. If not, see <http://www.gnu.org/licenses/>.

# @author Daniel Gomes <danielcesargomes@gmail.com>
module Eventception
class ImmutableDispatcher
private

attr_reader :dispatcher

public

# Creates an unmodifiable proxy for an event dispatcher.
#
# == Parameters:
# listeners::
# Array of event listeners
# subscribers::
# Array of event subscribers
#
# == Returns:
# Nil
#
def initialize(listeners: [], subscribers: [])
@dispatcher = Eventception::Dispatcher.new

listeners.each do |listener|
dispatcher.add_listener(
event_name: listener.fetch(:event_name),
listener: listener.fetch(:listener),
listener_method: listener.fetch(:listener_method),
priority: listener.fetch(:priority, 0),
)
end

subscribers.each { |subscriber| dispatcher.add_subscriber(subscriber: subscriber) }

nil
end

# Dispatches an event to all registered listeners.
#
# == Parameters:
# event_name::
# The name of the event to dispatch. The name of
# the event is the name of the method that is
# invoked on listeners.
# event::
# The event to pass to the event handlers/listeners
# If not supplied, an empty Event instance is created.
#
# == Returns:
# The Event.
#
def dispatch(event_name:, event: Eventception::Event.new)
dispatcher.dispatch(event_name: event_name, event: event)

event
end

# Gets all listeners sorted by descending priority.
#
# == Returns:
# All event listeners sorted by event_name and descending priority.
#
def listeners
dispatcher.listeners
end

# Checks whether are any registered listeners.
#
# == Returns:
# Boolean
#
def listeners?
dispatcher.listeners?
end

# Gets all listeners for the specific event sorted by descending priority.
#
# == Parameters:
# event_name::
# The name of the event
#
# == Returns:
# The event listeners for the specific event sorted by descending priority.
#
def listeners_for(event_name:)
dispatcher.listeners_for(event_name: event_name)
end

# Checks whether are any registered listeners for the specific event.
#
# == Parameters:
# event_name::
# The name of the event
#
# == Returns:
# Boolean
#
def listeners_for?(event_name:)
dispatcher.listeners_for?(event_name: event_name)
end
end
end
61 changes: 61 additions & 0 deletions spec/eventception/immutable_dispatcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Eventception: A lightweight and simple Ruby Event System
# Copyright (C) 2017 Daniel Gomes <danielcesargomes@gmail.com>
#
# This file is part of Eventception.
#
# Eventception is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Eventception is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Eventception. If not, see <http://www.gnu.org/licenses/>.

require 'spec_helper'
require 'support/listener'
require 'support/subscriber'
require 'support/test_event'

# @author Daniel Gomes <danielcesargomes@gmail.com>
describe Eventception::ImmutableDispatcher do
subject(:dispatcher) { described_class.new(listeners: listeners, subscribers: subscribers) }

let(:listener) { Eventception::Support::Listener.new }
let(:listener_method) { 'on_before' }
let(:listeners) { [] }
let(:subscriber) { Eventception::Support::Subscriber.new }
let(:subscribers) { [] }
let(:event_name) { :on_after }
let(:priority) { 0 }

describe '#initialize' do
context 'when one listener is provided' do
let(:listeners) {
[
{event_name: event_name, listener: listener, listener_method: listener_method, priority: priority},
]
}

it 'then a listener is registered' do
expect(dispatcher.listeners?).to be true
end

it 'then it have one listener registered' do
expect(dispatcher.listeners.size).to eq 1
end

it 'then a listener for the event \'on_before\' is registered' do
expect(dispatcher.listeners_for?(event_name: event_name)).to be true
end

it 'then no listeners are registered for an invalid event name' do
expect(dispatcher.listeners_for?(event_name: :invalid)).to be false
end
end
end
end