Permalink
Browse files

Initial set of docs

  • Loading branch information...
1 parent e3069e4 commit 406a0bd40c4d934e020469b923fad6330512ed4c @igorw committed Nov 3, 2012
Showing with 252 additions and 0 deletions.
  1. +28 −0 doc/00-intro.md
  2. +77 −0 doc/01-api.md
  3. +147 −0 doc/02-plugin-system.md
View
@@ -0,0 +1,28 @@
+# Introduction
+
+Événement is is French and means "event". The événement library aims to
+provide a simple way of subscribing to events and notifying those subscribers
+whenever an event occurs.
+
+The API that it exposes is almost a direct port of the EventEmitter API found
+in node.js. It also includes an "EventEmitter". There are some minor
+differences however.
+
+The EventEmitter is an implementation of the publish-subscribe pattern, which
+is a generalized version of the observer pattern. The observer pattern
+specifies an observable subject, which observers can register themselves to.
+Once something interesting happens, the subject notifies its observers.
+
+Pub/sub takes the same idea but encapsulates the observation logic inside a
+separate object which manages all of its subscribers or listeners. Subscribers
+are bound to an event name, and will only receive notifications of the events
+they subscribed to.
+
+**TLDR: What does evenement do, in short? It provides a mapping from event
+names to a list of listener functions and triggers each listener for a given
+event when it is emitted.**
+
+Why do we do this, you ask? To achieve decoupling.
+
+It allows you to design a system where the core will emit events, and modules
+are able to subscribe to these events. And respond to them.
View
@@ -0,0 +1,77 @@
+# API
+
+The API that événement exposes is defined by the
+`Evenement\EventEmitterInterface`. The interface is useful if you want to
+define an interface that extends the emitter and implicitly defines certain
+events to be emitted, or if you want to type hint an `EventEmitter` to be
+passed to a method without coupling to the specific implementation.
+
+## on($event, callable $listener)
+
+Allows you to subscribe to an event.
+
+Example:
+
+ $emitter->on('user.created', function (User $user) use ($logger) {
+ $logger->log(sprintf("User '%s' was created.", $user->getLogin()));
+ });
+
+Since the listener can be any callable, you could also use an instance method
+instead of the anonymous function:
+
+ $loggerSubscriber = new LoggerSubscriber($logger);
+ $emitter->on('user.created', array($loggerSubscriber, 'onUserCreated'));
+
+This has the benefit that listener does not even need to know that the emitter
+exists.
+
+You can also accept more than one parameter for the listener:
+
+ $emitter->on('numbers_added', function ($result, $a, $b) {});
+
+## once($event, callable $listener)
+
+Convenience method that adds a listener which is guaranteed to only be called
+once.
+
+Example:
+
+ $conn->once('connected', function () use ($conn, $data) {
+ $conn->send($data);
+ });
+
+## emit($event, array $arguments = [])
+
+Emit an event, which will call all listeners.
+
+Example:
+
+ $conn->emit('data', array($data));
+
+The second argument to emit is an array of listener arguments. This is how you
+specify more args:
+
+ $result = $a + $b;
+ $emitter->emit('numbers_added', array($result, $a, $b));
+
+## listeners($event)
+
+Allows you to inspect the listeners attached to an event. Particularly useful
+to check if there are any listeners at all.
+
+Example:
+
+ $e = new \RuntimeException('Everything is broken!');
+ if (0 === count($emitter->listeners('error'))) {
+ throw $e;
+ }
+
+## removeListener($event, callable $listener)
+
+Remove a specific listener for a specific event.
+
+## removeAllListeners($event = null)
+
+Remove all listeners for a specific event or all listeners alltogether. This
+is useful for long-running processes, where you want to remove listeners in
+order to allow them to get garbage collected.
View
@@ -0,0 +1,147 @@
+# Example: Plugin system
+
+In this example I will show you how to create a generic plugin system with
+événement where plugins can alter the behaviour of the app. The app is a blog.
+Boring, I know. By using the EventEmitter it will be easy to extend this blog
+with additional functionality without modifying the core system.
+
+The blog is quite basic. Users are able to create blog posts when they log in.
+The users are stored in a static config file, so there is no sign up process.
+Once logged in they get a "new post" link which gives them a form where they
+can create a new blog post with plain HTML. That will store the post in a
+document database. The index lists all blog post titles by date descending.
+Clicking on the post title will take you to the full post.
+
+## Plugin structure
+
+The goal of the plugin system is to allow features to be added to the blog
+without modifying any core files of the blog.
+
+The plugins are managed through a config file, `plugins.json`. This JSON file
+contains a JSON-encoded list of class-names for plugin classes. This allows
+you to enable and disable plugins in a central location. The initial
+`plugins.json` is just an empty array:
+
+ []
+
+A plugin class must implement the `PluginInterface`:
+
+ interface PluginInterface
+ {
+ function attachEvents(EventEmitterInterface $emitter);
+ }
+
+The `attachEvents` method allows the plugin to attach any events to the
+emitter. For example:
+
+ class FooPlugin implements PluginInterface
+ {
+ public function attachEvents(EventEmitterInterface $emitter)
+ {
+ $emitter->on('foo', function () {
+ echo 'bar!';
+ });
+ }
+ }
+
+The blog system creates an emitter instance and loads the plugins:
+
+ $emitter = new EventEmitter();
+
+ $pluginClasses = json_decode(file_get_contents('plugins.json'), true);
+ foreach ($pluginClasses as $pluginClass) {
+ $plugin = new $pluginClass();
+ $pluginClass->attachEvents($emitter);
+ }
+
+This is the base system. There are no plugins yet, and there are no events yet
+either. That's because I don't know which extension points will be needed. I
+will add them on demand.
+
+## Feature: Markdown
+
+Writing blog posts in HTML sucks! Wouldn't it be great if I could write them
+in a nice format such as markdown, and have that be converted to HTML for me?
+
+This feature will need two extension points. I need to be able to mark posts
+as markdown, and I need to be able to hook into the rendering of the post body
+and convert it from markdown to HTML. So the blog needs two new events:
+`post.create` and `post.render`.
+
+In the code that creates the post, I'll insert the `post.create` event:
+
+ class PostEvent
+ {
+ public $post;
+
+ public function __construct(array $post)
+ {
+ $this->post = $post;
+ }
+ }
+
+ $post = createPostFromRequest($_POST);
+
+ $event = new PostEvent($post);
+ $emitter->emit('post.create', array($event));
+ $post = $event->post;
+
+ $db->save('post', $post);
+
+This shows that you can wrap a value in an event object to make it mutable,
+allowing listeners to change it.
+
+The same thing for the `post.render` event:
+
+ public function renderPostBody(array $post)
+ {
+ $emitter = $this->emitter;
+
+ $event = new PostEvent($post);
+ $emitter->emit('post.render', array($event));
+ $post = $event->post;
+
+ return $post['body'];
+ }
+
+ <h1><?= $post['title'] %></h1>
+ <p><?= renderPostBody($post) %></p>
+
+Ok, the events are in place. It's time to create the first plugin, woohoo! I
+will call this the `MarkdownPlugin`, so here's `plugins.json`:
+
+ [
+ "MarkdownPlugin"
+ ]
+
+The `MarkdownPlugin` class will be autoloaded, so I don't have to worry about
+including any files. I just have to worry about implementing the plugin class.
+The `markdown` function represents a markdown to HTML converter.
+
+ class MarkdownPlugin implements PluginInterface
+ {
+ public function attachEvents(EventEmitterInterface $emitter)
+ {
+ $emitter->on('post.create', function (PostEvent $event) {
+ $event->post['format'] = 'markdown';
+ });
+
+ $emitter->on('post.render', function (PostEvent $event) {
+ if (isset($event->post['format']) && 'markdown' === $event->post['format']) {
+ $event->post['body'] = markdown($event->post['body']);
+ }
+ });
+ }
+ }
+
+There you go, the blog now renders posts as markdown. But all of the previous
+posts before the addition of the markdown plugin are still rendered correctly
+as raw HTML.
+
+## Feature: Comments
+
+TODO
+
+## Feature: Comment spam control
+
+TODO

0 comments on commit 406a0bd

Please sign in to comment.