forked from tildeio/router.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1c854c7
Showing
13 changed files
with
3,975 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
{ | ||
"predef": [ | ||
"console", | ||
"Router", | ||
"require", | ||
"equal", | ||
"test", | ||
"testBoth", | ||
"testWithDefault", | ||
"raises", | ||
"deepEqual", | ||
"start", | ||
"stop", | ||
"ok", | ||
"strictEqual", | ||
"module", | ||
"expect", | ||
], | ||
|
||
"node" : false, | ||
"browser" : true, | ||
|
||
"boss" : true, | ||
"curly": false, | ||
"debug": false, | ||
"devel": false, | ||
"eqeqeq": true, | ||
"evil": true, | ||
"forin": false, | ||
"immed": false, | ||
"laxbreak": false, | ||
"newcap": true, | ||
"noarg": true, | ||
"noempty": false, | ||
"nonew": false, | ||
"nomen": false, | ||
"onevar": false, | ||
"plusplus": false, | ||
"regexp": false, | ||
"undef": true, | ||
"sub": true, | ||
"strict": false, | ||
"white": false, | ||
"eqnull": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
# About | ||
|
||
`router.js` is a lightweight JavaScript library that builds on | ||
`route-recognizer` to provide an API for handling routes. | ||
|
||
In keeping with the Unix philosophy, it is a modular library | ||
that does one thing and does it well. | ||
|
||
# Usage | ||
|
||
Create a new router: | ||
|
||
```javascript | ||
var router = new Router(); | ||
``` | ||
|
||
Add a simple new route description: | ||
|
||
```javascript | ||
router.map(function(match) { | ||
match("/posts/:id").to("showPost"); | ||
match("/posts").to("postIndex"); | ||
match("/posts/new").to("newPost"); | ||
}); | ||
``` | ||
|
||
Add your handlers: | ||
|
||
```javascript | ||
router.handlers.showPost = { | ||
deserialize: function(params) { | ||
return App.Post.find(params.id); | ||
}, | ||
|
||
setup: function(post) { | ||
// render a template with the post | ||
} | ||
}; | ||
|
||
router.handlers.postIndex = { | ||
deserialize: function(params) { | ||
return App.Post.findAll(); | ||
}, | ||
|
||
setup: function(posts) { | ||
// render a template with the posts | ||
} | ||
}; | ||
|
||
router.handlers.newPost = { | ||
setup: function(post) { | ||
// render a template with the post | ||
} | ||
}; | ||
``` | ||
|
||
Use another modular library to listen for URL changes, and | ||
tell the router to handle a URL: | ||
|
||
```javascript | ||
urlWatcher.onUpdate(function(url) { | ||
router.handleURL(url); | ||
}); | ||
``` | ||
|
||
The router will parse the URL for parameters and then pass | ||
the parameters into the handler's `deserialize` method. It | ||
will then pass the return value of `deserialize` into the | ||
`setup` method. These two steps are broken apart to support | ||
async loading via **promises** (see below). | ||
|
||
To transition into the state represented by a handler without | ||
changing the URL, use `router.transitionTo`: | ||
|
||
```javascript | ||
router.transitionTo('showPost', post); | ||
``` | ||
|
||
If you pass an extra parameter to `transitionTo`, as above, | ||
the router will pass it to the handler's `serialize` | ||
method to extract the parameters. Let's flesh out the | ||
`showPost` handler: | ||
|
||
```javascript | ||
router.handlers.showPost = { | ||
// when coming in from a URL, convert parameters into | ||
// an object | ||
deserialize: function(params) { | ||
return App.Post.find(params.id); | ||
}, | ||
|
||
// when coming in from `transitionTo`, convert an | ||
// object into parameters | ||
serialize: function(object) { | ||
return { id: post.id }; | ||
}, | ||
|
||
setup: function(post) { | ||
// render a template with the post | ||
} | ||
}; | ||
``` | ||
|
||
If you enter a state represented by a handler through a | ||
URL: | ||
|
||
* the handler will convert the URL's parameters into an | ||
object, and pass it in to setup | ||
* the URL is already up to date | ||
|
||
If you enter a state via `transitionTo`: | ||
|
||
* the handler will convert the object into params, and | ||
update the URL. | ||
* the object is already available to pass into `setup` | ||
|
||
This means that you can be sure that your application's | ||
top-level objects will always be in sync with the URL, | ||
no matter whether you are extracting the object from the | ||
URL or if you already have the object. | ||
|
||
# Asynchronous Loading | ||
|
||
When extracting an object from the parameters, you may | ||
need to make a request to the server before the object | ||
is ready. | ||
|
||
You can easily achieve this by returning a **promise** | ||
from your `deserialize` method. Because jQuery's Ajax | ||
methods already return promises, this is easy! | ||
|
||
```javascript | ||
router.handlers.showPost = { | ||
deserialize: function(params) { | ||
return $.getJSON("/posts/" + params.id).then(function(json) { | ||
return new App.Post(json.post); | ||
}); | ||
}, | ||
|
||
serialize: function(post) { | ||
return { id: post.get('id') }; | ||
}, | ||
|
||
setup: function(post) { | ||
// receives the App.Post instance | ||
} | ||
}; | ||
``` | ||
|
||
You can register a `loading` handler for `router.js` to | ||
call while it waits for promises to resolve: | ||
|
||
```javascript | ||
router.handlers.loading = { | ||
// no deserialize or serialize because this is not | ||
// a handler for a URL | ||
|
||
setup: function() { | ||
// show a loading UI | ||
} | ||
} | ||
``` | ||
|
||
# Nesting | ||
|
||
You can nest routes, and each level of nesting can have | ||
its own handler. | ||
|
||
If you move from one child of a parent route to another, | ||
the parent will not be set up again unless it deserializes | ||
to a different object. | ||
|
||
Consider a master-detail view. | ||
|
||
```javascript | ||
router.map(function(match) { | ||
match("/posts").to("posts", function(match) { | ||
match("/").to("postIndex"); | ||
match("/:id").to("showPost"); | ||
}); | ||
}); | ||
|
||
router.handlers.posts = { | ||
deserialize: function() { | ||
return $.getJSON("/posts").then(function(json) { | ||
return App.Post.loadPosts(json.posts); | ||
}); | ||
}, | ||
|
||
// no serialize needed because there are no | ||
// dynamic segments | ||
|
||
setup: function(posts) { | ||
var postsView = new App.PostsView(posts); | ||
$("#master").append(postsView.el); | ||
} | ||
}; | ||
|
||
router.handlers.postIndex = { | ||
setup: function() { | ||
$("#detail").hide(); | ||
} | ||
}; | ||
|
||
router.handlers.showPost = { | ||
deserialize: function(params) { | ||
return $.getJSON("/posts/" + params.id, function(json) { | ||
return new App.Post(json.post); | ||
}); | ||
} | ||
}; | ||
|
||
router.handlers.loading = { | ||
setup: function() { | ||
$("#content").hide(); | ||
$("#loading").show(); | ||
}, | ||
|
||
exit: function() { | ||
$("#loading").hide(); | ||
$("#content").show(); | ||
} | ||
}; | ||
``` | ||
|
||
You can also use nesting to build nested UIs, setting up the | ||
outer view when entering the handler for the outer route, | ||
and setting up the inner view when entering the handler for | ||
the inner route. | ||
|
||
Routes at any nested level can deserialize parameters into a | ||
promise. The router will remain in the `loading` state until | ||
all promises are resolved. If a parent state deserializes | ||
the parameters into a promise, that promise will be resolved | ||
before a child route is handled. | ||
|
||
# More to Come | ||
|
||
`router.js` is functional today. I plan to add more features | ||
before a first official release: | ||
|
||
* A `failure` handler if any of the promises are rejected | ||
* The ability to dispatch events to the current handler | ||
or parent handlers. | ||
|
||
`router.js` will be the basis for the router in Ember.js. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
directory "dist" | ||
|
||
def replace_debug(file) | ||
content = File.read(file) | ||
|
||
content.gsub!(%r{^ *// DEBUG GROUP (.*) *$}, 'console.group(\1);') | ||
content.gsub!(%r{^ *// END DEBUG GROUP *$}, 'console.groupEnd();') | ||
content.gsub!(%r{^( *)// DEBUG (.*) *$}, '\1debug(\2);') | ||
content.gsub!(%r{^ */\*\* IF DEBUG\n}, "") | ||
content.gsub!(%r{ *END IF \*\*/\n}, "") | ||
|
||
content | ||
end | ||
|
||
file "dist/router.debug.js" => ["dist", "lib/router.js"] do | ||
router = replace_debug("lib/router.js") | ||
|
||
File.open("dist/router.debug.js", "w") do |file| | ||
file.puts router | ||
end | ||
end | ||
|
||
file "dist/router.js" => ["dist", "lib/router.js"] do | ||
File.open("dist/router.js", "w") do |file| | ||
file.puts File.read("lib/router.js"); | ||
end | ||
end | ||
|
||
task :debug => "dist/router.debug.js" | ||
task :build => "dist/router.js" | ||
|
||
task :release => [:debug, :build] | ||
|
||
task :test, :debug do |task, args| | ||
if args["debug"] | ||
sh "open tests/index.debug.html" | ||
else | ||
sh "open tests/index.html" | ||
end | ||
end | ||
|
||
task :test => :release |
Oops, something went wrong.