Skip to content

Instantly share code, notes, and snippets.

@willscripted
Created July 8, 2014 17:30
Show Gist options
  • Save willscripted/1bca5f9f2b9611ea7143 to your computer and use it in GitHub Desktop.
Save willscripted/1bca5f9f2b9611ea7143 to your computer and use it in GitHub Desktop.
Tanner look at this router

Quick thoughts

  • Routes can be more than one level
  • This doesn't handle params (i don't have a use-case yet)
  • You don't need to include the getCurrentUser method
  • Can always do it way backbone recommends too, see logout
  • Q is a pretty heavy, might swap it out for any A+ maybe this one
class IndexPage
constructor: (opts) ->
@view = new RactiveView(el: opts.el)
# Ask the router for current user,
# if success, set user
# if fail, redirect to login
opts.router.getCurrentUser().then (user) =>
# Success
@view.set('user', user)
, ->
opts.router.navigate('login', {trigger: true})
# Pages must have close method
close: ->
@view.teardown()
Router.addRoute("", IndexPage)
Router.addRoute("login", LoginPage)
var router = new Router()
Backbone.history.start()
# Cause e'rrything wants specs, even gists
define [
'backbone'
'index/router'
], (Backbone, Router) ->
describe "Router", ->
class OtherRouter extends Router
class Thing
close: ->
it "exists", ->
expect(Router).to.be.defined
beforeEach ->
@route = "things"
@Page = sinon.stub()
@Page.name = "SomeClassName"
@Page::close = (->)
@Page.returns(new Thing)
Router.addRoute.call(OtherRouter, @route, @Page)
describe ".addRoute", ->
it "adds a route to the routes", ->
expect(OtherRouter::routes).to.include.keys(@route)
it "adds handler to the prototype", ->
handler = OtherRouter::routes[@route]
expect(OtherRouter::[handler]).be.defined
describe "handler", ->
beforeEach ->
@router = new OtherRouter
Backbone.history.start(silent:true)
@router.navigate(@route, {trigger: true})
@opts = @Page.args[0][0]
afterEach ->
Backbone.history.stop()
window.location.hash = ""
it "sets the current page", ->
expect(@router.currentPage).to.be.defined
it "creates a new page", ->
expect(@Page.called).to.be.true
it "constructs with a router", ->
expect(@opts.router).to.eq(@router)
it "constructs with an el", ->
expect(@opts.el).to.be.defined
describe "new page", ->
beforeEach ->
@router = new OtherRouter
Backbone.history.start(silent:true)
@router.navigate(@route, {trigger: true})
afterEach ->
Backbone.history.stop()
it "closes the last page before opening the next", ->
# Little tricky, 2nd navigate must be a different, valid route
# If this fails, ensure stats is valid route
spy = sinon.spy(@router.currentPage, "close")
@router.navigate("stats", {trigger: true})
expect(spy.called).to.be.true
describe "#logout", ->
beforeEach ->
@router = new OtherRouter
Backbone.history.start(silent:true)
requests = @requests = []
@xhr = sinon.useFakeXMLHttpRequest()
@xhr.onCreate = (xhr) -> requests.push(xhr)
@locationReplace = sinon.stub(window.location, "replace")
afterEach ->
@xhr.restore()
Backbone.history.stop()
window.location.hash = ""
@locationReplace.restore()
it "deletes the session", ->
@router.navigate("logout", {trigger: true})
expect(@requests).to.have.length(1)
req = @requests[0]
expect(req.method).to.eql("DELETE")
expect(req.url).to.eql("/v1/session")
it "routes to /", ->
@router.navigate("logout", {trigger: true})
@requests[0].respond(204, {})
expect(@locationReplace.called).to.be.true
expect(@locationReplace.firstCall.args[0]).to.eq("/")
describe "#getCurrentUser", ->
context "user is logged in", ->
user = {id: "some-id"}
beforeEach ->
@server = sinon.fakeServer.create()
@server.respondWith('/v1/me', [200, {"Content-Type": "application/json"}, JSON.stringify(user) ])
@router = new Router()
it "promise resolves with the user", (done) ->
@router.getCurrentUser().then((u) =>
expect(u.id).to.equal(user.id)
done()
).done()
@server.respond()
context "user is not logged in", ->
beforeEach ->
@server = sinon.fakeServer.create()
@server.respondWith('/v1/me', [401, {"Content-Type": "application/json"}, JSON.stringify({errors: "Unauthorized"}) ])
@router = new Router()
it "fails if no user is authenticated", ->
spy = sinon.spy()
@router.getCurrentUser()
.fail(spy)
.fin( -> expect(spy.called).to.be.true )
.done()
@server.respond()
describe "#logout", ->
xit "logs the user out"
xit "navigates to root"
define [
'backbone'
'lodash'
'superagent'
'q'
], (
Backbone,
_,
request,
Q,
) ->
# Router
# Allows sub-classes to add routes without muddying router
# logic. The router passes itself, and the element to render on,
# in the options option.
class Router extends Backbone.Router
routes:
"logout" : "logout"
getCurrentUser: ->
def = Q.defer()
request
.get('/v1/me')
.end (resp) =>
if resp.error.status == 401
def.reject(new Error("No user signed in."))
else
def.resolve(resp.body)
return def.promise
execute: ->
if @currentPage?
if @currentPage.close?
@currentPage.close()
else
console.warn('Page has no `close` method!')
super
logout: ->
request
.del('/v1/session')
.end ->
window.location.replace('/')
Router.addRoute = (path, page) ->
throw new Error("Page must have a close method") unless page::close
handlerName = _.uniqueId("handler")
this::routes[path] = handlerName
this::[handlerName] = () ->
@currentPage = new page({el: $('.page'), router: this})
return Router
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment