Skip to content
Newer
Older
100644 351 lines (249 sloc) 21.8 KB
22d0aaf @raganwald break into two parts
raganwald authored
1 Misadventure, Part I: Overview
67b48c2 @raganwald a TON of work
raganwald authored
2 ===
3ebaa20 @raganwald misadventure under construction
raganwald authored
3
22d0aaf @raganwald break into two parts
raganwald authored
4 *Misadventure is a little game written on top of Faux and Backbone.js*
3ebaa20 @raganwald misadventure under construction
raganwald authored
5
6 **introduction**
7
3292cde @raganwald better links on each page
raganwald authored
8 [Misadventure][play] is a little game in the style of [Adventure][a]. Misadventure is written in JavaScript and runs entirely in the browser. Misadventure is written in standard Model-View-Controller style, making heavy use of the [Faux][f] and [Backbone.js][b] libraries. In this series of posts I will give you a tour of Misadventure's [code][source], showing how it uses Faux to structure its routes and templates as well as how it uses Backbone.js to organize its models and interactive view code.
22d0aaf @raganwald break into two parts
raganwald authored
9
56fcd95 @raganwald Misadventure Part IV
raganwald authored
10 This is Part I, wherein we look at the game and its controller. In [Part II][pii], we'll start our examination of controller methods with a look at `controller.wake()`. In [Part III][piii], we'll look at a controller method that wires a model, a collection, and a view up to a template. In [Part IV][piv], we'll do a double-take and talk about loading classes.
3ebaa20 @raganwald misadventure under construction
raganwald authored
11
2802b8b @raganwald pub!
raganwald authored
12 Misadventure
13 ---
3ebaa20 @raganwald misadventure under construction
raganwald authored
14
15 Open [http://unspace.github.com/misadventure][play] in your browser.
16
17 Misadventure starts inauspiciously:
18
1228985 @raganwald better wake pics?
raganwald authored
19 <a target="_blank" href="http://min.us/mvkEt6y#1"><img src="http://i.min.us/jeaApo.png" border="0"/></a>
3ebaa20 @raganwald misadventure under construction
raganwald authored
20
10e7fcd @raganwald code overview
raganwald authored
21 You notice that a *fragment* of [#/wake][wake] has been added to the URL, but the base URL hasn't changed. Since Single Page Interface architecture is all the rage these days, you already know that this means that the contents of the page are being loaded into the DOM without refreshing the entire page. That requires much less bandwidth and scales faster than rendering pages from the server.
22
23 Let's get back to using the game. You have two options. If you experiment, you discover that closing your eyes doesn't appear to do anything at this point, but if you stand up and look around, you'll get something like this:
3ebaa20 @raganwald misadventure under construction
raganwald authored
24
25 <a target="_blank" href="http://min.us/mvkEt6y#2"><img src="http://i.min.us/jefdsa.png" border="0"/></a>
26
27 The fragment has changed again, now it's [#/42492610216140747/7624672284554068][l1]. You can move North by clicking the link or pressing the up arrow on the keyboard. Things appear to be the same, but the fragment has changed again, now it's [#/42492610216140747/5682321739861935][l2]. Moving North one more time takes you to what appears to be a different page:
28
29 <a target="_blank" href="http://min.us/mvkEt6y#3"><img src="http://i.min.us/jeflO8.png" border="0"/></a>
30
3292cde @raganwald better links on each page
raganwald authored
31 And the fragment has changed yet again, now it's [#/42492610216140747/3916709493533819][l3]. We just moved North. What happens if we move South? Let's investigate. First, each possible move is a standard HTML link. There's no magic JavaScript. Let's look at the link to move South. It's the same URL, but the fragment looks familiar: [#/42492610216140747/5682321739861935][l2]. That's interesting, it's the exact same fragment that we were on a move ago.
3ebaa20 @raganwald misadventure under construction
raganwald authored
32
33 And looking at the page, we see that the link to move South is colored <font color='red'>red</font>. There's no special magic going on. It's a standard link and it goes to a place that's still in the browser's history, so it is styled differently, just like any other link. We'll see how that works later, but let's try something else.
34
35 Use the back button or back command in your browser. It takes you to your previous location, just as if each location int he maze is a standard web page with its own unique link. That's because each location does have a unique link and behaves exactly like a standard web page. This means that all of the things you or any user expects from a page in a browser work here. You can navigate forward and back, bookmark locations, even mail links to friends.
36
37 Did you notice that in this document we're using links for the fragments? If you travel to the game through the links, you wind up in exactly the same maze in exactly the same location. The links are *stable*. We'll come back to how that works when we look at the code, but it's a key point, so we'll emphasize it:
38
39 > Everything you see in Misadventure has a unique URL and works with your browser's existing mechanisms like bookmarks or navigating backwards and forwards. The URLs are stable and are not tied to a temporary "session."
40
10e7fcd @raganwald code overview
raganwald authored
41 You can continue to navigate your way through the corn maze. Try using an arrow key instead of clicking a link. With patience and a little knowledge of how to recursively search a tree, you may eventually find your way to the exit and leave the corn maze:
3ebaa20 @raganwald misadventure under construction
raganwald authored
42
43 <a target="_blank" href="http://min.us/mvkEt6y#4"><img src="http://i.min.us/jbJZZ8.png" border="0"/></a>
44
10e7fcd @raganwald code overview
raganwald authored
45 And by now you will not be surprised to discover that the final page has a fragment too: [#/42492610216140747/bed][bed]. Do you want to play again? Simply click [close your eyes][wake], and you'll find yourself playing in a brand new maze, with all different fragments. Have fun and when you come back, we'll take a look at the code that makes this work.
46
47 **summary of what we've learned from misadventure's user experience**
48
49 Before we look at the code, here's a quick summary of what we've seen:
50
51 1. Every "page" has its own unique URL
52 2. Pages all have the same base URL, but the fragments change
53 3. The DOM is being refreshed in the browser
54 4. The URLs are stable and can be bookmarked or shared with other users
55 5. All standard navigation elements (e.g. back, forwards, links) work in standard ways
56 6. Misadventure also supports non-standard navigation in the form of arrow keys
57
58 Now we'll see how Misadventure uses Faux and Backbone.js to make this happen.
59
2802b8b @raganwald pub!
raganwald authored
60 i can haz codes?
61 ---
10e7fcd @raganwald code overview
raganwald authored
62
63 Misadventure is organized in a tree:
64
65 <a target="_blank" href="http://min.us/mveGGAQ"><img src="http://i.min.us/jeaE9S.png" border="0"/></a>
66
67 Ignoring `README.md`, `docs`, `images`, and `stylesheets`, we're going to look at [index.html][index] and the two most important directories in the project: [javascripts][js] and [haml][haml].
68
69 [index]: http://github.com/unspace/misadventure/tree/master/index.html
70 [js]: http://github.com/unspace/misadventure/tree/master/javascripts
71 [haml]: http://github.com/unspace/misadventure/tree/master/haml
72
73 `index.html` contains the web page you see when you open Misadventure for the first time. Here's the source:
74
75 <!DOCTYPE html>
76 <html lang="en">
77 <head>
78 <title>Misadventure</title>
79 <meta charset="utf-8">
80 <link href="./stylesheets/application.css" rel="stylesheet">
81 <!-- hard requirements for Faux -->
82 <script src="./javascripts/vendor/jquery.1.4.2.js"></script>
56fcd95 @raganwald Misadventure Part IV
raganwald authored
83 <script src="./javascripts/vendor/async.js"></script>
10e7fcd @raganwald code overview
raganwald authored
84 <script src="./javascripts/vendor/documentcloud/underscore.js"></script>
85 <script src="./javascripts/vendor/documentcloud/backbone.js"></script>
86 <script src="./javascripts/vendor/haml-js.js"></script>
87 <!-- fixes for using haml-js with IE -->
88 <script src="./javascripts/vendor/ie.js"></script>
89 <!-- Faux -->
90 <script src="./javascripts/vendor/faux.js"></script>
91 <!-- other libraries we happen to like -->
92 <script src="./javascripts/vendor/seedrandom.js"></script>
93 <script src="./javascripts/vendor/functional/to-function.js"></script>
94 <script src="./javascripts/vendor/jquery.combinators.js"></script>
95 <script src="./javascripts/vendor/jquery.predicates.js"></script>
56fcd95 @raganwald Misadventure Part IV
raganwald authored
96 <!--misadventure's controller -->
10e7fcd @raganwald code overview
raganwald authored
97 <script src="./javascripts/controller.js"></script>
98 </head>
99
100 <body>
101
102 <div id="container">
103 <h1>Misadventure</h1>
104 <div class="content"></div>
105
106 </div>
107
108 <small id="get-code">Read about Misadventure and browse the code on <a href="http://github.com/unspace/misadventure">Github</a>.</small>
109
110 </body>
111
112 <!-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
113 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
114 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
115 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
116 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
117 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
118 THE SOFTWARE.
119
120 To the extent possible under law, Unspace Interactive has waived all copyright
121 and related or neighboring rights to the software, except for those portions
122 that are otherwise licensed.
123
124 This work is published from Canada. -->
125
126 </html>
127
128 A few points of interest:
129
130 1. We include jQuery, Underscore, Backbone.js, amd haml-js before we include Faux;
56fcd95 @raganwald Misadventure Part IV
raganwald authored
131 2. The only Misadventure file we load is [controller.js][cjs];
10e7fcd @raganwald code overview
raganwald authored
132 3. There's a very interesting DOM element, `<div class="content"></div>`.
133
134 Looking in the [haml][haml] directory, we see three [Haml][haml-lang] templates: `wake.haml`, `location.haml`, and `bed.haml`. We're going to see where those are used shortly.
135
67b48c2 @raganwald a TON of work
raganwald authored
136 controller.js
137 ---
10e7fcd @raganwald code overview
raganwald authored
138
139 Let's look at [controller.js][cjs]. It's the last file to be loaded, and it starts the application for us. Eliding the comments, we have:
140
141 ;(function () {
142
143 var controller = new Faux.Controller({
67b48c2 @raganwald a TON of work
raganwald authored
144 save_location: true,
10e7fcd @raganwald code overview
raganwald authored
145 element_selector: '.content',
146 partial: 'haml',
147 partial_suffix: '.haml',
56fcd95 @raganwald Misadventure Part IV
raganwald authored
148 javascript: 'javascripts',
10e7fcd @raganwald code overview
raganwald authored
149 title: 'Misadventure'
150 });
151
152 controller
153
154 .begin({
155 'seed=': {
67b48c2 @raganwald a TON of work
raganwald authored
156 locations: function (locations) { return locations.seed; },
157 '': function () { return Math.random().toString().substring(2); }
10e7fcd @raganwald code overview
raganwald authored
158 },
159 'locations=': {
160 seed: function (seed) { return LocationCollection.find_or_create({ seed: seed }); }
161 }
162 })
163
67b48c2 @raganwald a TON of work
raganwald authored
164 .method('wake')
165
166 .method('bed', {
167 route: ':seed/bed'
10e7fcd @raganwald code overview
raganwald authored
168 })
169
39edbe3 @raganwald update
raganwald authored
170 .method('location', {
171 route: ':seed/:location_id'
10e7fcd @raganwald code overview
raganwald authored
172 })
39edbe3 @raganwald update
raganwald authored
173
174 .end();
10e7fcd @raganwald code overview
raganwald authored
175
176
56fcd95 @raganwald Misadventure Part IV
raganwald authored
177 $(function() {
178 controller.define_all(function () {
179 Backbone.history.start();
180 window.location.hash || controller.wake();
181 });
182 });
10e7fcd @raganwald code overview
raganwald authored
183
184 })();
185
186 The first statement creates a new instance of `Faux.Controller`. Faux controllers extend Backbone.js's [controllers][bc]. When the program is running, they parse fragments and manage the history so that invoking an URL through a link results in calling a controller method.
187
188 Faux the library is nothing more than a backbone controller that has a bunch of helpers for writing Backbone.js controller methods for us. The most important such helper is the `.method` method. Looking at `controller.js`, you can se that we call `.method` three times:
189
67b48c2 @raganwald a TON of work
raganwald authored
190 .method('wake')
10e7fcd @raganwald code overview
raganwald authored
191
67b48c2 @raganwald a TON of work
raganwald authored
192 .method('bed', { ...configuration... })
39edbe3 @raganwald update
raganwald authored
193
67b48c2 @raganwald a TON of work
raganwald authored
194 .method('location', { ...configuration... })
10e7fcd @raganwald code overview
raganwald authored
195
196 We now have enough information to explain Misadventure's basic structure:
197
198 1. There are three controller methods invoked during gameplay: `.wake(...)`, `.location(...)`, and `.bed(...)`
199 2. Each of those controller methods has a route, and Backbone.js will invoke the controller method when the browser invokes the URL.
200 3. The controller methods that Faux writes will use the templates `wake.haml`, `location.haml`, and `bed.haml` to display content in the page.
201 4. The content will be injected into the `<div class="content"></div>` DOM element on the page.
202
203 We haven't explained anything else about views, models, parsing parameters out of URL, or even defining which URLs invoke which methods yet. All of these things are driven by convention and configuration (preferring convention over configuration, of course).
204
6cbc6ee @raganwald two small fixes
raganwald authored
205 **what is a controller method?**
67b48c2 @raganwald a TON of work
raganwald authored
206
207 If you're comfortable with a web framework like Ruby on Rails, you already know what a controller method is: A controller method is the application code that services a request or performs an action. All of the controller methods defined in Misadventure display something in the page using a template.
208
3292cde @raganwald better links on each page
raganwald authored
209 These three controller methods are all associated with routes. Controller methods can also be invoked directly in JavaScript. For example, invoking `controller.location({ seed: '42492610216140747', id: '7624672284554068' })` has exactly teh same effect as directing your browser to [#/42492610216140747/7624672284554068](http://unspace.github.com/misadventure/#/42492610216140747/7624672284554068). Faux even updates the browser's location bar so that bookmarking and navigation works correctly.
67b48c2 @raganwald a TON of work
raganwald authored
210
211 If you dive deeply into Faux, you'll also discover that controller methods can be associated with events or with DOM elements, such that inserting a DOM element with a specific `id` and/or classes automatically invokes a controller method to populate it with children. Misadventure doesn't use any of these techniques. They aren't "advanced" or "complicated," but Misadventure is a small app and doesn't need to use every tool in the toolbox.
212
213 Controller methods are part of Backbone.js. Faux builds on Backbone by providing a DSL for writing controller methods, and it works with Backbone's route support. But underlying it all is a perfectly standard MVC architecture.
214
215 Now let's look at how each method is configured.
10e7fcd @raganwald code overview
raganwald authored
216
217 **in the beginning**
218
39edbe3 @raganwald update
raganwald authored
219 Faux methods are configured with objects, usually object literals. To facilitate sharing configuration between methods, Faux provides a lexical scoping mechanism, `.begin({...})` and `.end()`. `.begin({...})` introduces configuration that applies to all of the `.method(...)` calls until `.end()` is called. You can nest calls to `.begin({...})`,and we see that in controllers.js.
220
221 Faux methods are also configured by default according to certain naming conventions. We will discuss some more later, but for now the most important one is that unless otherwise specified, the name of the method is part of its route.
222
67b48c2 @raganwald a TON of work
raganwald authored
223 The code above is equivalent to:
39edbe3 @raganwald update
raganwald authored
224
225 controller
226
67b48c2 @raganwald a TON of work
raganwald authored
227 .method('wake', {
228 route: '/wake', // <- by convention, from the name
229 partial: 'haml/wake.haml', // <- by convention, from the name
230 clazz: false, // <- by convention, from the name
231 'seed=': { // <- 'inherited' from .begin(...)
232 locations: function (locations) { return locations.seed; },
233 '': function () { return Math.random().toString().substring(2); }
234 },
235 'locations=': {
236 '': function () { return LocationCollection.find_or_create(); },
237 seed: function (seed) { // <- 'inherited' from .begin(...)
238 return LocationCollection.find_or_create({ seed: seed });
39edbe3 @raganwald update
raganwald authored
239 }
67b48c2 @raganwald a TON of work
raganwald authored
240 }
241 })
39edbe3 @raganwald update
raganwald authored
242
67b48c2 @raganwald a TON of work
raganwald authored
243 .method('bed', {
244 route: '/:seed/bed',
245 partial: 'haml/bed.haml', // <- by convention, from the name
246 clazz: BedView, // <- by convention, from the name
247 'seed=': { // <- 'inherited' from .begin(...)
248 locations: function (locations) { return locations.seed; },
249 '': function () { return Math.random().toString().substring(2); }
250 },
251 'locations=': { // <- 'inherited' from .begin(...)
252 seed: function (seed) { return LocationCollection.find_or_create({ seed: seed }); }
253 }
254 })
39edbe3 @raganwald update
raganwald authored
255
67b48c2 @raganwald a TON of work
raganwald authored
256 .method('location', {
257 route: '/:seed/:location_id'
258 partial: 'haml/location.haml', // <- by convention, from the name
259 model_clazz: Location, // <- by convention, from the name
260 clazz: LocationView, // <- by convention, from the name
261 'seed=': { // <- 'inherited' from .begin(...)
262 locations: function (locations) { return locations.seed; },
263 '': function () { return Math.random().toString().substring(2); }
264 },
265 'locations=': { // <- 'inherited' from .begin(...)
266 seed: function (seed) { return LocationCollection.find_or_create({ seed: seed }); },
267 location: function (location) { return location.collection; } // <- by convention, from the name
268 },
269 'location_id': { // <- by convention, from the name
270 location: function (location) { return location.id; }
271 },
272 'location=': { // <- by convention, from the name
273 'locations location_id': function (locations, location_id) { return locations.get(location_id); }
274 }
275 })
10e7fcd @raganwald code overview
raganwald authored
276
67b48c2 @raganwald a TON of work
raganwald authored
277 Now that we know the 'expanded' configuration for each method, we can see what they do.
10e7fcd @raganwald code overview
raganwald authored
278
67b48c2 @raganwald a TON of work
raganwald authored
279 **routes and parameters**
280
281 Each of the methods we're defining will be bound to a route. Faux binds controller methods to routes unless you explicitly write `route: false` in a method's configuration. In two of the methods we've explicitly specified the route. In the third, we've let Faux infer the route from the method name.
282
283 Many routes have parameters. Faux and Backbone.js both use the same convention, namely anything that looks like a variable name prefixed by `:` is a parameter. So when the browser invokes [#/42492610216140747/bed][bed], the controller matches this against the route `/:seed/bed` and it's a match. Faux then invokes our controller method `controller.bed({ seed: '42492610216140747' })`.
284
285 *Nota Bene: Faux and Backbone differ slightly in how parameters are passed to controller methods. In plain vanilla Backbone,* `#/42492610216140747/bed` *would invoke* `controller.bed('42492610216140747')`*, whereas Faux bundles parameters into a hash of names and values.*
286
287 So now we know that there are three controller methods, that each has a route, and that when the route is invoked, parameters are parsed out and provided to the controller method in a hash. We haven't discussed all the `'seed='` configurations, we'll get to that after we explain how the application launches.
288
289 **launching the application**
3ebaa20 @raganwald misadventure under construction
raganwald authored
290
67b48c2 @raganwald a TON of work
raganwald authored
291 As you know from jQuery, this code:
292
293 $(function() {
56fcd95 @raganwald Misadventure Part IV
raganwald authored
294 controller.define_all(function () {
295 Backbone.history.start();
296 window.location.hash || controller.wake();
297 });
67b48c2 @raganwald a TON of work
raganwald authored
298 });
299
56fcd95 @raganwald Misadventure Part IV
raganwald authored
300 ...is run when the page has loaded. Becasue some definitions might take a while for Faux to process, Faux actually defers building all of the methods we defined them until you call `controller.define_all`. So we call it after the page has loaded, and we pass it a callback function to execute when it finishes with all of the definitions. Let's look at what we want to do once all of the methods are defined:
301
302 `Backbone.history.start();` initializes Backbone's support for managing URLs. The next line checks to see whether the current URL has a fragment. If it doesn't, it invokes the controller method `.wake()` to start the application in a new cornfield.
67b48c2 @raganwald a TON of work
raganwald authored
303
22d0aaf @raganwald break into two parts
raganwald authored
304 So now we know that when our application is launched by loading its URL, if we have a fragment, we let the controller sort out what to do. If we don't have a fragment, we invoke `controller.wake()` to start a new game.
67b48c2 @raganwald a TON of work
raganwald authored
305
22d0aaf @raganwald break into two parts
raganwald authored
306 Summary
67b48c2 @raganwald a TON of work
raganwald authored
307 ---
308
22d0aaf @raganwald break into two parts
raganwald authored
309 In this first post, we've seen how Misadventure behaves like a web site, with stable URLs that can be bookmarked or shared and with standard navigation like back and forwards. We've also seen that everything is done in the DOM, with no laborious refreshing of the entire page.
67b48c2 @raganwald a TON of work
raganwald authored
310
56fcd95 @raganwald Misadventure Part IV
raganwald authored
311 In [Part II][pii] of this series, we'll look at `controller.wake()` in detail, exploring how controller methods are declared using Faux's little DSL, how parameters are inferred, how templates are displayed, and we'll look at `route_to` helpers you can use in templates. In [Part III][piii], we'll look at a controller method that wires a model, a collection, and a view up to a template. Finally, in [Part IV][piv], we'll do a double-take and talk about loading classes.
67b48c2 @raganwald a TON of work
raganwald authored
312
7d4a695 @raganwald dasherize the mores
raganwald authored
313 ---
f69ce70 @raganwald Link to the book
raganwald authored
314
8c4fb37 @raganwald My recent work
raganwald authored
315 My recent work:
316
f9d4487 @raganwald allongé
raganwald authored
317 ![](http://i.minus.com/iL337yTdgFj7.png)[![JavaScript Allongé](http://i.minus.com/iW2E1A8M5UWe6.jpeg)](http://leanpub.com/javascript-allonge "JavaScript Allongé")![](http://i.minus.com/iL337yTdgFj7.png)[![CoffeeScript Ristretto](http://i.minus.com/iMmGxzIZkHSLD.jpeg)](http://leanpub.com/coffeescript-ristretto "CoffeeScript Ristretto")![](http://i.minus.com/iL337yTdgFj7.png)[![Kestrels, Quirky Birds, and Hopeless Egocentricity](http://i.minus.com/ibw1f1ARQ4bhi1.jpeg)](http://leanpub.com/combinators "Kestrels, Quirky Birds, and Hopeless Egocentricity")
4479986 @raganwald More recent work!
raganwald authored
318
f9d4487 @raganwald allongé
raganwald authored
319 * [JavaScript Allongé](http://leanpub.com/javascript-allonge), [CoffeeScript Ristretto](http://leanpub.com/coffeescript-ristretto), and my [other books](http://leanpub.com/u/raganwald).
21fcc00 @raganwald redirect to allong.es
raganwald authored
320 * [allong.es](http://allong.es), practical function combinators and decorators for JavaScript.
17be325 @raganwald revised footers
raganwald authored
321 * [Method Combinators](https://github.com/raganwald/method-combinators), a CoffeeScript/JavaScript library for writing method decorators, simply and easily.
4f7cce6 @raganwald githiub
raganwald authored
322 * [jQuery Combinators](http://github.com/raganwald/jquery-combinators), what else? A jQuery plugin for writing your own fluent, jQuery-like code.
f69ce70 @raganwald Link to the book
raganwald authored
323
16fa4bb @raganwald separated .sig
raganwald authored
324 ---
325
b2b5c4a @raganwald shuffle
raganwald authored
326 (Spot a bug or a spelling mistake? This is a Github repo, fork it and send me a pull request!)
327
7e71700 @raganwald updated links
raganwald authored
328 [Reg Braithwaite](http://braythwayt.com) | [@raganwald](http://twitter.com/raganwald)
67b48c2 @raganwald a TON of work
raganwald authored
329
330 [bc]: http://documentcloud.github.com/backbone/#Controller
331 [haml-lang]: http://haml-lang.com/
3ebaa20 @raganwald misadventure under construction
raganwald authored
332 [a]: http://www.digitalhumanities.org/dhq/vol/001/2/000009/000009.html
333 [f]: https://github.com/unspace/faux
334 [play]: http://unspace.github.com/misadventure/
335 [r]: http://weblog.jamisbuck.org/2011/1/12/maze-generation-recursive-division-algorithm
336 [j]: http://weblog.jamisbuck.org/
7e71700 @raganwald updated links
raganwald authored
337 [rb]: http://braythwayt.com
3ebaa20 @raganwald misadventure under construction
raganwald authored
338 [source]: http://github.com/unspace/misadventure
339 [docco]: https://github.com/raganwald/homoiconic/blob/master/2010/11/docco.md "A new way to think about programs"
340 [cjs]: http://unspace.github.com/misadventure/docs/controller.html
341 [s]: http://yayinternets.com/
342 [ui]: http://unspace.ca
343 [b]: http://documentcloud.github.com/backbone/
344 [wake]: http://unspace.github.com/misadventure/#/wake
345 [l1]: http://unspace.github.com/misadventure/#/42492610216140747/7624672284554068
346 [l2]: http://unspace.github.com/misadventure/#/42492610216140747/5682321739861935
347 [l3]: http://unspace.github.com/misadventure/#/42492610216140747/3916709493533819
22d0aaf @raganwald break into two parts
raganwald authored
348 [bed]: http://unspace.github.com/misadventure/#/42492610216140747/bed
9affe8e @raganwald link to iii
raganwald authored
349 [pii]: http://github.com/raganwald/homoiconic/tree/master/2011/01/misadventure_part_ii.md#readme
56fcd95 @raganwald Misadventure Part IV
raganwald authored
350 [piii]: http://github.com/raganwald/homoiconic/tree/master/2011/01/misadventure_part_iii.md#readme
351 [piv]: http://github.com/raganwald/homoiconic/tree/master/2011/02/misadventure_part_iv.md#readme
Something went wrong with that request. Please try again.