Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Start of a getting started guide

  • Loading branch information...
commit dee1f15b5294a375db9a2f8216808c95587b0a46 1 parent 1f32ba3
@carllerche carllerche authored
View
197 doc/getting_started.md
@@ -0,0 +1,197 @@
+# Getting Started with Momentum
+
+## Introduction
+
+Writing performant network applications is not easy task. (Describe the
+problem...)
+
+Momentum is an attempt to improve the state of the art in terms of both
+performance and ease of use. (Say more...)
+
+## About this guide
+
+This guide is a quick tutoria to help you get started writing network
+applications with Momentum. It should take no more than 20 minutes to
+go through.
+
+This guide is written for Momentum 0.3.0.
+
+## Prerequisites
+
+Before continuing, make sure that you have the following:
+
+* About 20 minutes of free time.
+* A working [clojure](http://www.clojure.org/) environment with
+ [leiningen](https://github.com/technomancy/leiningen). On OS X, this
+ can easily be done with [Homebrew](https://github.com/mxcl/homebrew) by
+ running `brew install leiningen`.
+
+## A simple echo server
+
+The first application that we will be writing is a very simple TCP echo
+server. Every message that the client sends the server will be echoed
+back to the client. Let's get started.
+
+### Setup
+
+First, generate a new leiningen project:
+
+ $ lein new echo
+ Created new project in: /Users/carllerche/Code/echo
+ Look over project.clj and start coding in echo/core.clj
+
+Next, open project.clj and the momentum dependency.
+
+```clojure
+(defproject echo "1.0.0-SNAPSHOT"
+ :description "FIXME: write description"
+ :dependencies [[org.clojure/clojure "1.3.0"]
+ [io.tilde.momentum/momentum "0.3.0"]])
+```
+
+Install momentum by running: `lein deps`. Everything should be ready
+good to go.
+
+### Code
+
+Open `src/echo/core.clj` and add the following code. Don't worry, all
+the specifics will be explained shortly.
+
+```clojure
+(ns echo.core
+ (:require
+ [momentum.net.server :as server])
+ (:gen-class))
+
+(defn -main
+ [& args]
+ (server/start
+ (fn [downstream env]
+ (fn [event value]
+ (when (= event :message)
+ (downstream :message value)))))
+
+ (println "Running echo server..."))
+```
+
+Now, start the server by running: `lein run -m echo.core`. Once "Running
+echo server..." is displayed in the terminal, the server is running on
+port 4040. Let's try it out with good ol' telnet.
+
+```
+$ telnet localhost 4040
+Trying ::1...
+Connected to localhost.
+Escape character is '^]'.
+Hello
+Hello
+ZOMG
+ZOMG
+Stop it!
+Stop it!
+^]
+telnet> quit
+Connection closed.
+```
+
+As expected, everything that I type, the server echos back. The telnet
+session is exited by first pressing the escale character (in this case
+CTRL-]), then by typing in `quit`.
+
+Let's go back to the code we wrote and break it down.
+
+```clojure
+(ns echo.core
+ (:require
+ [momentum.net.server :as server])
+ (:gen-class))
+```
+
+The first thing that we did was to require the momentum.net.server
+namespace, which contains the start function that we use. All functions
+related to dealing with raw network servers (TCP and UDP) are in this
+namespace.
+
+Next, we start the server by invoking `server/start` and passing the
+application as the argument.
+
+### The application
+
+A momentum application consists of three things. A bind function, an
+upstream function, and a downstream function.
+
+The bind function is invoked by the server once per "session." A session
+can mean different things in different situations. In the case of the
+raw TCP server, a session is one TCP connection. In the case of an HTTP
+server, a session might be one HTTP exchange (a single request and
+response pair).
+
+Bind functions have two arguments. The first argument is the downstream
+function for that session. The second is the environment map for that
+session. The environment map is mostly just a way to pass arbitrary data
+around from the server, through middleware (which will be introduced
+later), up to the application.
+
+Bind functions must return the upstream function for that session.
+
+Both the upstream and the downstream functions have two arguments. The
+first is an event type, the second is an event value. These functions
+are how events get passed around. The downstream function is used by the
+application to send events, such as messages to write to the socket,
+downstream (towards the server). The upstream function is used by the
+server to send events, up to the application.
+
+![Basic event flow](img/basic-event-flow.png)
+
+So, back to the code...
+
+```clojure
+(server/start
+ (fn [downstream env] ;; Bind function
+ (fn [event value] ;; Upstream function
+ (when (= event :message)
+ (downstream :message value)))))
+```
+
+When a connection is opened with the server, momentum will invoke the
+bind function, passing in a downstream function that, when invoked, will
+write messages directly back to the client. Whenever a message is
+received from the client, momentum will dispatch that event using the
+upstream function. Our application will get the event and echo it back
+to the client.
+
+Note: It is not legal to invoke the downstream function until the
+upstream function receives an event.
+
+### Event lifecycle
+
+The lifecycle begins with an :open event sent upstream. It is not legal
+to invoke the downstream function until the application receives an
+event.
+
+The events that can be sent upstream.
+
+* `:open` - The connection is opened. The value is a map with some
+ information regarding the socket.
+* `:close` - The connection is closed. The value is nil.
+* `:message`- A message is received. The value is a buffer representing
+ the message.
+* `:pause` - The socket's write buffer is full, the application should
+ stop sending :message events. The value is nil.
+* `:resume` - The socket's write buffer is drained, the application may
+ resume sending :message events. The value is nil.
+* `:abort` - Something went wrong. The value is the exception that
+ caused the event.
+
+The events that can be sent downstream.
+
+* `:close` - The socket should be closed.
+* `:message` - A message to send to the client. The value is a buffer
+ representing the message.
+* `:pause` - The application is overloaded, the server should stop
+ sending :message events. The value is nil.
+* `:resume` - The application is ready to receive further :message
+ events. The value is nil.
+* `:abort` - Something went wrong, the connection should be terminated.
+ The value is the exception that caused the event.
+
View
643 doc/graffle/basic-event-flow.graffle
@@ -0,0 +1,643 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>ApplicationVersion</key>
+ <array>
+ <string>com.omnigroup.OmniGraffle</string>
+ <string>138.33.0.157554</string>
+ </array>
+ <key>AutoAdjust</key>
+ <true/>
+ <key>BackgroundGraphic</key>
+ <dict>
+ <key>Bounds</key>
+ <string>{{0, 0}, {576, 733}}</string>
+ <key>Class</key>
+ <string>SolidGraphic</string>
+ <key>ID</key>
+ <integer>2</integer>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ </dict>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>CreationDate</key>
+ <string>2012-03-05 15:12:13 +0000</string>
+ <key>Creator</key>
+ <string>Carl Lerche</string>
+ <key>DisplayScale</key>
+ <string>1 0/72 in = 1 0/72 in</string>
+ <key>GraphDocumentVersion</key>
+ <integer>8</integer>
+ <key>GraphicsList</key>
+ <array>
+ <dict>
+ <key>Bounds</key>
+ <string>{{345, 253}, {97, 22}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>ID</key>
+ <integer>14</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320
+{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs36 \cf0 Socket.write}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{132, 253}, {94, 22}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>ID</key>
+ <integer>13</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320
+{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs36 \cf0 Socket.read}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>12</integer>
+ <key>Points</key>
+ <array>
+ <string>{326.50003, 223}</string>
+ <string>{328, 279}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>LineType</key>
+ <integer>1</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>3</integer>
+ <key>Info</key>
+ <integer>12</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>3</integer>
+ <key>Info</key>
+ <integer>14</integer>
+ </dict>
+ <key>ID</key>
+ <integer>9</integer>
+ <key>Points</key>
+ <array>
+ <string>{243, 278}</string>
+ <string>{243.49998, 223}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>LineType</key>
+ <integer>1</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{150, 128}, {76, 22}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>ID</key>
+ <integer>8</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320
+{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs36 \cf0 Upstream}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{345, 128}, {99, 22}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>ID</key>
+ <integer>7</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320
+{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs36 \cf0 Downstream}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>3</integer>
+ <key>Info</key>
+ <integer>2</integer>
+ </dict>
+ <key>ID</key>
+ <integer>6</integer>
+ <key>Points</key>
+ <array>
+ <string>{243, 101}</string>
+ <string>{243.5, 180}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>0</string>
+ <key>LineType</key>
+ <integer>1</integer>
+ <key>TailArrow</key>
+ <string>FilledArrow</string>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>4</integer>
+ <key>Info</key>
+ <integer>12</integer>
+ </dict>
+ <key>ID</key>
+ <integer>5</integer>
+ <key>Points</key>
+ <array>
+ <string>{326.5, 180}</string>
+ <string>{326.50003, 98}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>0</string>
+ <key>LineType</key>
+ <integer>1</integer>
+ <key>TailArrow</key>
+ <string>FilledArrow</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>3</integer>
+ <key>Info</key>
+ <integer>4</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{160.5, 55}, {249, 43}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>4</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{-0.73960024, -1.1094004}</string>
+ <string>{-0.42163706, -1.2649111}</string>
+ <string>{0, -1.3333334}</string>
+ <string>{0.42163691, -1.2649111}</string>
+ <string>{0.73960036, -1.1094004}</string>
+ <string>{1.1094004, -0.73960036}</string>
+ <string>{1.2649111, -0.42163691}</string>
+ <string>{1.3333334, 0}</string>
+ <string>{1.2649109, 0.42163718}</string>
+ <string>{1.1094004, 0.73960036}</string>
+ <string>{0.73960024, 1.1094004}</string>
+ <string>{0.4216373, 1.2649109}</string>
+ <string>{0, 1.3333333}</string>
+ <string>{-0.4216373, 1.2649109}</string>
+ <string>{-0.73960024, 1.1094004}</string>
+ <string>{-1.1094, 0.73960066}</string>
+ <string>{-1.2649113, 0.4216364}</string>
+ <string>{-1.3333334, -6.357829e-07}</string>
+ <string>{-1.2649109, -0.42163748}</string>
+ <string>{-1.1094005, -0.73960018}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>7</real>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320
+{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\b\fs40 \cf0 Application}</string>
+ </dict>
+ <key>TextRelativeArea</key>
+ <string>{{0, 0}, {1, 1}}</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{160.5, 180}, {249, 43}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>3</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{-0.73960024, -1.1094004}</string>
+ <string>{-0.42163706, -1.2649111}</string>
+ <string>{0, -1.3333334}</string>
+ <string>{0.42163691, -1.2649111}</string>
+ <string>{0.73960036, -1.1094004}</string>
+ <string>{1.1094002, -0.73960048}</string>
+ <string>{1.2649108, -0.42163774}</string>
+ <string>{1.3333334, 0}</string>
+ <string>{1.2649113, 0.4216367}</string>
+ <string>{1.1094002, 0.73960048}</string>
+ <string>{0.73960018, 1.1094005}</string>
+ <string>{0.42163718, 1.2649109}</string>
+ <string>{0, 1.3333334}</string>
+ <string>{-0.42163718, 1.2649109}</string>
+ <string>{-0.73960018, 1.1094005}</string>
+ <string>{-1.1094002, 0.73960048}</string>
+ <string>{-1.2649113, 0.4216367}</string>
+ <string>{-1.3333334, -1.2715658e-06}</string>
+ <string>{-1.2649108, -0.42163774}</string>
+ <string>{-1.1094002, -0.73960048}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>7</real>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320
+{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\b\fs40 \cf0 Server}</string>
+ </dict>
+ </dict>
+ </array>
+ <key>GridInfo</key>
+ <dict/>
+ <key>GuidesLocked</key>
+ <string>NO</string>
+ <key>GuidesVisible</key>
+ <string>YES</string>
+ <key>HPages</key>
+ <integer>1</integer>
+ <key>ImageCounter</key>
+ <integer>1</integer>
+ <key>KeepToScale</key>
+ <false/>
+ <key>Layers</key>
+ <array>
+ <dict>
+ <key>Lock</key>
+ <string>NO</string>
+ <key>Name</key>
+ <string>Layer 1</string>
+ <key>Print</key>
+ <string>YES</string>
+ <key>View</key>
+ <string>YES</string>
+ </dict>
+ </array>
+ <key>LayoutInfo</key>
+ <dict>
+ <key>Animate</key>
+ <string>NO</string>
+ <key>circoMinDist</key>
+ <real>18</real>
+ <key>circoSeparation</key>
+ <real>0.0</real>
+ <key>layoutEngine</key>
+ <string>dot</string>
+ <key>neatoSeparation</key>
+ <real>0.0</real>
+ <key>twopiSeparation</key>
+ <real>0.0</real>
+ </dict>
+ <key>LinksVisible</key>
+ <string>NO</string>
+ <key>MagnetsVisible</key>
+ <string>NO</string>
+ <key>MasterSheets</key>
+ <array/>
+ <key>ModificationDate</key>
+ <string>2012-03-05 15:25:16 +0000</string>
+ <key>Modifier</key>
+ <string>Carl Lerche</string>
+ <key>NotesVisible</key>
+ <string>NO</string>
+ <key>Orientation</key>
+ <integer>2</integer>
+ <key>OriginVisible</key>
+ <string>NO</string>
+ <key>PageBreaks</key>
+ <string>YES</string>
+ <key>PrintInfo</key>
+ <dict>
+ <key>NSBottomMargin</key>
+ <array>
+ <string>float</string>
+ <string>41</string>
+ </array>
+ <key>NSHorizonalPagination</key>
+ <array>
+ <string>int</string>
+ <string>0</string>
+ </array>
+ <key>NSLeftMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSPaperSize</key>
+ <array>
+ <string>coded</string>
+ <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAx7X05TU2l6ZT1mZn2WgWQCgRgDhg==</string>
+ </array>
+ <key>NSPrintReverseOrientation</key>
+ <array>
+ <string>int</string>
+ <string>0</string>
+ </array>
+ <key>NSRightMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSTopMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ </dict>
+ <key>PrintOnePage</key>
+ <false/>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>RowAlign</key>
+ <integer>1</integer>
+ <key>RowSpacing</key>
+ <real>36</real>
+ <key>SheetTitle</key>
+ <string>Canvas 1</string>
+ <key>SmartAlignmentGuidesActive</key>
+ <string>YES</string>
+ <key>SmartDistanceGuidesActive</key>
+ <string>YES</string>
+ <key>UniqueID</key>
+ <integer>1</integer>
+ <key>UseEntirePage</key>
+ <false/>
+ <key>VPages</key>
+ <integer>1</integer>
+ <key>WindowInfo</key>
+ <dict>
+ <key>CurrentSheet</key>
+ <integer>0</integer>
+ <key>ExpandedCanvases</key>
+ <array>
+ <dict>
+ <key>name</key>
+ <string>Canvas 1</string>
+ </dict>
+ </array>
+ <key>Frame</key>
+ <string>{{225, 4}, {845, 874}}</string>
+ <key>ListView</key>
+ <true/>
+ <key>OutlineWidth</key>
+ <integer>142</integer>
+ <key>RightSidebar</key>
+ <false/>
+ <key>ShowRuler</key>
+ <true/>
+ <key>Sidebar</key>
+ <true/>
+ <key>SidebarWidth</key>
+ <integer>120</integer>
+ <key>VisibleRegion</key>
+ <string>{{-67, 0}, {710, 719}}</string>
+ <key>Zoom</key>
+ <real>1</real>
+ <key>ZoomValues</key>
+ <array>
+ <array>
+ <string>Canvas 1</string>
+ <real>1</real>
+ <real>1</real>
+ </array>
+ </array>
+ </dict>
+ <key>saveQuickLookFiles</key>
+ <string>YES</string>
+</dict>
+</plist>
View
BIN  doc/img/basic-event-flow.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Please sign in to comment.
Something went wrong with that request. Please try again.