Skip to content

Commit

Permalink
Add Immutable Dispatcher (#13)
Browse files Browse the repository at this point in the history
* Add Immutable Dispatcher

* Update Changelog

* code review fixes
  • Loading branch information
dcsg authored Jul 14, 2017
1 parent e8f384a commit 661f196
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 4 deletions.
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

0 comments on commit 661f196

Please sign in to comment.