Skip to content

Commit

Permalink
Simplify the EventBus API: Allow any Dart object as an event (see issue
Browse files Browse the repository at this point in the history
  • Loading branch information
marcojakob committed Sep 8, 2014
1 parent 2700e14 commit a6ab765
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 426 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## Version 0.3.0 (2014-09-08)

* BREAKING CHANGE: Changed and simplified the EventBus API. We can now dispatch
any Dart object as an event. Before, we had to create an EventType for every
type of event we wanted to fire. Now we can use any class as an event.
Listeners can (optionally) filter events by that class.
* Added a way to create a **hierarchical event bus** that filters events by
class and their subclasses. 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.
* BREAKING CHANGE: Default to async mode instead of sync. This now matches the
of the Dart streams.
* BREAKING CHANGE: Removed LoggingEventBus. Reason is that logging can easily
be implemented with a event listener that listens for all events and logs
them.


## Version 0.2.5 (2014-09-03)

* Update example.
Expand Down
70 changes: 48 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
Event Bus
================
# Event Bus

A simple Event Bus using Dart [Streams](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:async.Stream)
for decoupling applications.


## Demo

See [Dart Event Bus in action](http://code.makery.ch/dart/event-bus/).
See [Dart Event Bus in action](http://code.makery.ch/library/dart-event-bus/).

All examples are also available in the `example` directory on GitHub.
All examples are also available in the [example directory on GitHub](https://github.com/marcojakob/dart-event-bus/tree/master/example).


## Event Bus Pattern
Expand Down Expand Up @@ -42,16 +41,25 @@ By communication through an **Event Bus**, the coupling is reduced.
## Usage

### 1. Define Events

Any Dart class can be used as an event.

```dart
import 'package:event_bus/event_bus.dart';
final EventType<User> userLoggedInEvent = new EventType<User>();
final EventType<Order> newOrderEvent = new EventType<Order>();
class UserLoggedInEvent {
User user;
UserLoggedInEvent(this.user);
}
class NewOrderEvent {
Order order;
NewOrderEvent(this.text);
}
```

Note: The generic type of the event (`User` and `Order` in this case) is the
type of data that will be provided when the event is fired.


### 2. Create Event Bus

Expand All @@ -64,40 +72,58 @@ set up to group a specific set of events.
EventBus eventBus = new EventBus();
```

This will instantiate the default implementation of `EventBus` which is
`SimpleEventBus`. You may provide your own `EventBus` by either extending
`SimpleEventBus` or implementing `EventBus`.
You can alternatively use the `HierarchicalEventBus` that filters events by
event class **including** its subclasses.

```dart
EventBus eventBus = new EventBus.hierarchical();
```


### 3. Register Listeners

Register listeners that will be called whenever the event is fired.
Register listeners for a **specific events**:

```dart
eventBus.on(userLoggedInEvent).listen((User user) {
print(user.name);
eventBus.on(UserLoggedInEvent).listen((UserLoggedInEvent event) {
print(event.user);
});
```

`EventBus` uses Dart [Streams](http://api.dartlang.org/docs/releases/latest/dart_async/Stream.html)
Register listeners for **all events**:

```dart
eventBus.on().listen((event) {
// Print the runtime type. Such a set up could be used for logging.
print(event.runtimeType);
});
```


#### About Dart Streams

`EventBus` uses Dart [Streams](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:async.Stream)
as its underlying mechanism to keep track of listeners. You may use all
functionality available by the [Stream](http://api.dartlang.org/docs/releases/latest/dart_async/Stream.html)
API. One example is the use of [StreamSubscriptions](http://api.dartlang.org/docs/releases/latest/dart_async/StreamSubscription.html)
functionality available by the [Stream](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:async.Stream)
API. One example is the use of [StreamSubscriptions](https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:async.StreamSubscription)
to later unsubscribe from the events.

```dart
StreamSubscription<User> subscription = eventBus.on(userLoggedInEvent).listen((User user) {
print(user.name);
StreamSubscription loginSubscription = eventBus.on(UserLoggedInEvent).listen((UserLoggedInEvent event) {
print(event.user);
});
subscription.cancel();
loginSubscription.cancel();
```


### 4. Fire Events

Finally, we need to fire an event.

```dart
eventBus.fire(userLoggedInEvent, new User('Mickey'));
User myUser = new User('Mickey');
eventBus.fire(new UserLoggedInEvent(myUser));
```


Expand Down
24 changes: 14 additions & 10 deletions example/events.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
/**
* This is an example of how to set up events used by the event bus.
* This is an example of how to set up the [EventBus] and its events.
*/
library events;

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

EventBus _eventBus;

/// The global [EventBus] object.
EventBus get eventBus => _eventBus;
EventBus eventBus = new EventBus();

/// Event A.
class MyEventA {
String text;

MyEventA(this.text);
}

/// Initializes the global [EventBus] object. Should only be called once!
void init(EventBus eventBus) {
_eventBus = eventBus;
/// Event B.
class MyEventB {
String text;

MyEventB(this.text);
}

final EventType<String> textUpdateA = new EventType<String>('textUpdateA');
final EventType<String> textUpdateB = new EventType<String>('textUpdateB');
36 changes: 3 additions & 33 deletions example/example.css
Original file line number Diff line number Diff line change
@@ -1,46 +1,16 @@

body {
background-color: #F8F8F8;
font-family: 'Open Sans', sans-serif;
font-size: 14px;
font-weight: normal;
line-height: 1.2em;
margin: 15px;
}

h1, p {
color: #333;
}

#example-container {
width: 620px;
margin-bottom: 20px;
}

#example-container .listener {
width: 300px;
margin: 5px 5px 20px 5px;
padding: 10px;
float: left;
position: relative;
border: 1px solid #ccc;
background-color: #fff;
-webkit-box-sizing:border-box;
-khtml-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}

#example-container .listener * {
margin-top: 5px;
margin-top: 5px;
}

#example-container .listener textarea {
display: block;
width: 270px;
height: 100px;
}

#example-container .event {
display: block;
width: 270px;
height: 100px;
}
28 changes: 13 additions & 15 deletions example/example.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import 'dart:html';

import 'dart:async';
import 'events.dart' as events;
import 'package:intl/intl.dart';
import 'events.dart';
import 'package:logging/logging.dart';

int counterA = 1;
int counterB = 1;

final _log = new Logger('event_bus_example');

void main() {
// Init logging.
initLogging();

// Initialize the global event bus.
events.init(new events.LoggingEventBus());
// Log all events.
eventBus.on().listen((event) => _log.finest('event fired: ${event.runtimeType}'));


// Initialize the listener boxes.
Listener listener1 = new Listener(querySelector('#listener-1'));
Expand All @@ -29,28 +31,24 @@ void main() {
// -------------------------------------------------
// Fire Event A
// -------------------------------------------------
events.eventBus.fire(events.textUpdateA, 'Received Event A [$counterA]');
eventBus.fire(new MyEventA('Received Event A [$counterA]'));
fireLabelA.text = '--> fired [$counterA]';
counterA++;
fireButtonA.text = 'Fire Event A [$counterA]';
});
fireButtonB.onClick.listen((_) {
// -------------------------------------------------
// Fire Event B
// -------------------------------------------------
events.eventBus.fire(events.textUpdateB, 'Received Event B [$counterB]');
eventBus.fire(new MyEventB('Received Event B [$counterB]'));
fireLabelB.text = '--> fired [$counterB]';
counterB++;
fireButtonB.text = 'Fire Event B [$counterB]';
});
}

initLogging() {
DateFormat dateFormat = new DateFormat('yyyy.mm.dd HH:mm:ss.SSS');

// Print output to console.
Logger.root.onRecord.listen((LogRecord r) {
print('${dateFormat.format(r.time)}\t${r.loggerName}\t[${r.level.name}]:\t${r.message}');
print('${r.time}\t${r.loggerName}\t[${r.level.name}]:\t${r.message}');
});

// Root logger level.
Expand Down Expand Up @@ -81,8 +79,8 @@ class Listener {
// -------------------------------------------------
// Listen for Event A
// -------------------------------------------------
subscription = events.eventBus.on(events.textUpdateA).listen((String text) {
appendOuput(text);
subscription = eventBus.on(MyEventA).listen((event) {
appendOuput(event.text);
});
appendOuput('---');
appendOuput('Listening for Event A');
Expand All @@ -97,8 +95,8 @@ class Listener {
// -------------------------------------------------
// Listen for Event B
// -------------------------------------------------
subscription = events.eventBus.on(events.textUpdateB).listen((String text) {
appendOuput(text);
subscription = eventBus.on(MyEventB).listen((MyEventB event) {
appendOuput(event.text);
});
appendOuput('---');
appendOuput('Listening for Event B');
Expand Down
Loading

0 comments on commit a6ab765

Please sign in to comment.