Skip to content

Commit

Permalink
Move the HierarchicalEventBus to a separate library to be able to r…
Browse files Browse the repository at this point in the history
…emove `dart:mirrors` from normal `EventBus`

Users of the hierarchical event bus must import `event_bus_hierarchical.dart` and replace the use of the factory constructor `EventBus.hierarchical()` with the `HierarchicalEventBus` constructor.
  • Loading branch information
marcojakob committed May 3, 2015
2 parents 7780324 + 15042bc commit 6a3c6cc
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 92 deletions.
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.buildlog
.DS_Store
.idea
.pub/
build/
packages
.project
pubspec.lock
pubspec.lock
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## Version 0.4.0 (2015-05-03)

* BREAKING CHANGE: Moved the `HierarchicalEventBus` to a separate library to
be able to remove `dart:mirrors` from the normal `EventBus`.
Users of the hierarchical event bus must import `event_bus_hierarchical.dart`
and replace the use of the factory constructor `EventBus.hierarchical()` with
the `HierarchicalEventBus` constructor.


## Version 0.3.0 (2014-09-08)

* BREAKING CHANGE: Changed and simplified the EventBus API. We can now dispatch
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,13 @@ EventBus eventBus = new EventBus();
You can alternatively use the `HierarchicalEventBus` that filters events by
event class **including** its subclasses.

*Note that the hierarchical event bus uses `dart:mirrors` which support in
dart2js is experimental.*

```dart
EventBus eventBus = new EventBus.hierarchical();
import 'package:event_bus/event_bus_hierarchical.dart';
EventBus eventBus = new HierarchicalEventBus();
```

**Note:** *The default constructor will create an asynchronous event bus. To
Expand Down
97 changes: 23 additions & 74 deletions lib/event_bus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,115 +2,64 @@ library event_bus;

import 'dart:async';

@MirrorsUsed(symbols: '*') // Do not keep any names.
import 'dart:mirrors';

/**
* Dispatches events to listeners using the Dart [Stream] API. The [EventBus]
* Dispatches events to listeners using the Dart [Stream] API. The [EventBus]
* enables decoupled applications. It allows objects to interact without
* requiring to explicitly define listeners and keeping track of them.
*
*
* Not all events should be broadcasted through the [EventBus] but only those of
* general interest.
*
* Events are normal Dart objects. By specifying a class, listeners can
* filter events. Such a filter will return
*
* Events are normal Dart objects. By specifying a class, listeners can
* filter events. Such a filter will return
* specifying a class.
*/
class EventBus {

/// Controller for the event bus stream.

StreamController _streamController;


/// Controller for the event bus stream.
StreamController get streamController => _streamController;

/**
* Creates an [EventBus].
*
*
* If [sync] is true, events are passed directly to the stream's listeners
* during an [fire] call. If false (the default), the event will be passed to
* the listeners at a later time, after the code creating the event has
* during an [fire] call. If false (the default), the event will be passed to
* the listeners at a later time, after the code creating the event has
* completed.
*/
EventBus({bool sync: false}) {
_streamController = new StreamController.broadcast(sync: sync);
}

/**
* Creats a [HierarchicalEventBus].
*
* It filters events by an event class **including** its subclasses.
*
* Note: This currently only works with classes **extending** other classes
* and not with **implementing** an interface. We might have to wait for
* https://code.google.com/p/dart/issues/detail?id=20756 to enable interfaces.
*
* If [sync] is true, events are passed directly to the stream's listeners
* during an [fire] call. If false (the default), the event will be passed to
* the listeners at a later time, after the code creating the event has
* completed.
*/
factory EventBus.hierarchical({bool sync: false}) {
return new HierarchicalEventBus(sync: sync);
}


/**
* Listens for events of [eventType].
*
* Listens for events of [eventType].
*
* The returned [Stream] is a broadcast stream so multiple subscriptions are
* allowed.
*
*
* Each listener is handled independently, and if they pause, only the pausing
* listener is affected. A paused listener will buffer events internally until
* unpaused or canceled. So it's usually better to just cancel and later
* unpaused or canceled. So it's usually better to just cancel and later
* subscribe again (avoids memory leak).
*/
Stream on([Type eventType]) {
return _streamController.stream.where((event) => eventType == null ||
return streamController.stream.where((event) => eventType == null ||
event.runtimeType == eventType);
}
/**

/**
* Fires a new event on the event bus with the specified [event].
*/
void fire(event) {
_streamController.add(event);
streamController.add(event);
}

/**
* Destroy this [EventBus]. This is generally only in a testing context.
*/
void destroy() {
_streamController.close();
}
}

/**
* A [HierarchicalEventBus] that filters events by event class **including**
* its subclasses.
*/
class HierarchicalEventBus extends EventBus {

/**
* Creates a [HierarchicalEventBus].
*
* If [sync] is true, events are passed directly to the stream's listeners
* during an [fire] call. If false (the default), the event will be passed to
* the listeners at a later time, after the code creating the event has completed.
*/
HierarchicalEventBus({bool sync: false}) : super(sync: sync);

/**
* Listens for events of [eventType] and of all subclasses of [eventType].
*
* The returned [Stream] is a broadcast stream so multiple subscriptions are
* allowed.
*
* Each listener is handled independently, and if they pause, only the pausing
* listener is affected. A paused listener will buffer events internally until
* unpaused or canceled. So it's usually better to just cancel and later
* subscribe again (avoids memory leak).
*/
Stream on([Type eventType]) {
return _streamController.stream.where((event) => eventType == null ||
reflect(event).type.isSubclassOf(reflectClass(eventType)));
}
}
46 changes: 46 additions & 0 deletions lib/event_bus_hierarchical.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
library event_bus.hierarchical;

import 'dart:async';

@MirrorsUsed(symbols: '*') // Do not keep any names.
import 'dart:mirrors';

import 'event_bus.dart';
export 'event_bus.dart';

/**
* A [HierarchicalEventBus] that filters events by event class **including**
* its subclasses.
*
* Note: This currently only works with classes **extending** other classes
* and not with **implementing** an interface. We might have to wait for
* https://code.google.com/p/dart/issues/detail?id=20756 to enable interfaces.
*/
class HierarchicalEventBus extends EventBus {

/**
* Creates a [HierarchicalEventBus].
*
* If [sync] is true, events are passed directly to the stream's listeners
* during an [fire] call. If false (the default), the event will be passed to
* the listeners at a later time, after the code creating the event has
* completed.
*/
HierarchicalEventBus({bool sync: false}) : super(sync: sync);

/**
* Listens for events of [eventType] and of all subclasses of [eventType].
*
* The returned [Stream] is a broadcast stream so multiple subscriptions are
* allowed.
*
* Each listener is handled independently, and if they pause, only the pausing
* listener is affected. A paused listener will buffer events internally until
* unpaused or canceled. So it's usually better to just cancel and later
* subscribe again (avoids memory leak).
*/
Stream on([Type eventType]) {
return streamController.stream.where((event) => eventType == null ||
reflect(event).type.isSubclassOf(reflectClass(eventType)));
}
}
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: event_bus
version: 0.3.0+3
version: 0.4.0
author: Marco Jakob <[email protected]>
description: A simple Event Bus using Dart Streams for decoupling applications
homepage: http://code.makery.ch/library/dart-event-bus
documentation: http://www.dartdocs.org/documentation/event_bus/latest/
dev_dependencies:
browser: '>=0.10.0 <0.11.0'
logging: '>=0.9.0 <0.10.0'
logging: '>=0.10.0 <0.11.0'
unittest: '>=0.11.0 <0.12.0'
4 changes: 2 additions & 2 deletions test/all_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ library all_tests;
import 'package:unittest/html_enhanced_config.dart';

import 'event_bus_test.dart' as eventBusTest;
import 'hierarchical_event_bus_test.dart' as hierarchicalEventBusTest;
import 'event_bus_hierarchical_test.dart' as hierarchicalEventBusTest;

main() {
useHtmlEnhancedConfiguration();

eventBusTest.main();
hierarchicalEventBusTest.main();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,53 @@ library hierarchical_event_bus_test;
import 'dart:async';
import 'package:unittest/unittest.dart';

import 'package:event_bus/event_bus.dart';
import 'package:event_bus/event_bus_hierarchical.dart';

class EventA extends SuperEvent {
String text;

EventA(this.text);
}

class EventB extends SuperEvent {
String text;

EventB(this.text);
}

class SuperEvent {
}

main() {

group('[HierarchicalEventBus]', () {

test('Listen on same class', () {
// given
EventBus eventBus = new EventBus.hierarchical();
EventBus eventBus = new HierarchicalEventBus();
Future f = eventBus.on(EventA).toList();

// when
eventBus.fire(new EventA('a1'));
eventBus.fire(new EventB('b1'));
eventBus.destroy();

// then
return f.then((List events) {
expect(events.length, 1);
});
});

test('Listen on superclass', () {
// given
EventBus eventBus = new EventBus.hierarchical();
EventBus eventBus = new HierarchicalEventBus();
Future f = eventBus.on(SuperEvent).toList();

// when
eventBus.fire(new EventA('a1'));
eventBus.fire(new EventB('b1'));
eventBus.destroy();

// then
return f.then((List events) {
expect(events.length, 2);
Expand Down

0 comments on commit 6a3c6cc

Please sign in to comment.