Permalink
Browse files

Added Express 3 support

  • Loading branch information...
1 parent 76c778b commit 296ede6d1fc5a6dad13b22eb950c7bb28585101b @shimaore shimaore committed Jun 18, 2012
Showing with 114 additions and 99 deletions.
  1. +5 −5 package.json
  2. +100 −91 src/zappa.coffee
  3. +2 −2 tests/support/client.coffee
  4. +7 −1 tests/views.coffee
View
@@ -1,24 +1,24 @@
{
"name": "zappajs",
"description": "CoffeeScript minimalist interface to express, socket.io and others",
- "version": "0.3.9",
+ "version": "0.4.0",
"author": "Stephane Alnet <stephane@shimaore.net>",
"homepage": "http://zappajs.github.com/zappajs/",
"repository": {"type": "git", "url": "git://github.com/zappajs/zappajs.git"},
"dependencies": {
- "express": "2.5.9",
+ "express": "3.0.x",
"socket.io": "0.9.x",
"coffeecup": "0.3.x",
- "node-uuid": "1.2.0",
"uglify-js": "1.1.x"
},
"devDependencies": {
"jsdom": "0.2.14",
"underscore": "1.2.2",
"request": "2.2.5",
"socket.io-client": "0.9.6",
- "eco": "1.1.0-rc-1",
- "jade": "0.18.0",
+ "consolidate": "0.3.0",
+ "eco": "1.1.0-rc-3",
+ "jade": "0.26.1",
"stylus": "0.19.3",
"less": ">=1.3.0"
},
View
@@ -9,7 +9,6 @@ codename = 'Overture to a Holiday in Berlin'
log = console.log
fs = require 'fs'
path = require 'path'
-uuid = require 'node-uuid'
express = require 'express'
socketio = require 'socket.io'
jquery = fs.readFileSync(__dirname + '/../vendor/jquery-1.7.2.min.js').toString()
@@ -51,60 +50,10 @@ copy_data_to = (recipient, sources) ->
for k, v of obj
recipient[k] = v unless recipient[k]
-# Keep inline views at the module level and namespaced by app id
-# so that the monkeypatched express can look them up.
-views = {}
-
-# Monkeypatch express to support lookup of inline templates. Such is life.
-express.View.prototype.__defineGetter__ 'exists', ->
- # Path given by zappa: /path/to/appid/foo.bar.
-
- # Try appid/foo.bar in memory.
- p = @path.replace @root + '/', ''
- id = p.split('/')[0]
- return true if views[p]
-
- # Try appid/foo in memory.
- p = p.replace(path.extname(p), '')
- return true if views[p]
-
- # Try /path/to/foo.bar in filesystem (normal express behaviour).
- p = @path.replace id + '/', ''
- try
- fs.statSync(p)
- return true
- catch err
- p = @path
- try
- fs.statSync(p)
- return true
- catch err
- return false
-
-express.View.prototype.__defineGetter__ 'contents', ->
- # Path given by zappa: /path/to/appid/foo.bar.
-
- # Try appid/foo.bar in memory.
- p = @path.replace @root + '/', ''
- id = p.split('/')[0]
- return views[p] if views[p]
-
- # Try appid/foo in memory.
- p = p.replace(path.extname(p), '')
- return views[p] if views[p]
-
- # Try /path/to/foo.bar in filesystem (normal express behaviour).
- p = @path.replace id + '/', ''
- try
- fs.readFileSync p, 'utf8'
- catch err
- p = @path
- fs.readFileSync p, 'utf8'
-
# Takes in a function and builds express/socket.io apps based on the rules contained in it.
zappa.app = (func,options) ->
- context = {id: uuid(), zappa, express}
-
+ context = {zappa, express}
+
context.root = path.dirname(module.parent.filename)
# Storage for user-provided stuff.
@@ -113,22 +62,43 @@ zappa.app = (func,options) ->
helpers = {}
postrenders = {}
+ app = context.app = express()
if options.https?
- app = context.app = express.createServer options.https
+ app.server = require('https').createServer options.https, app
else
- app = context.app = express.createServer()
- io = if options.disable_io then null else context.io = socketio.listen(app)
+ app.server = require('http').createServer app
+ io = if options.disable_io then null else context.io = socketio.listen(app.server)
# Reference to the zappa client, the value will be set later.
client = null
# Tracks if the zappa middleware is already mounted (`@use 'zappa'`).
zappa_used = no
+ # Force view-cache (so that we can populate it).
+ unless options.export_views
+ app.enable 'view cache'
+
+ # Provide register (as in Express 2)
+ compilers = {}
+ register = (ext,obj) ->
+ if ext[0] isnt '.'
+ ext = '.' + ext
+ compile = obj.compile
+ if not compile
+ throw new Error "register #{ext} must provide a .compile"
+ # Register the compiler so that context.view may use it.
+ compilers[ext] = compile
+ # Register it with Express natively.
+ app.engine ext, (path,options,next) ->
+ src = fs.readFileSync(path,options.encoding ? 'utf8')
+ template = compile src, options
+ next null, template options
+
# Zappa's default settings.
app.set 'view engine', 'coffee'
- app.register '.coffee', zappa.adapter require('coffeecup').adapters.express,
- blacklist: ['format', 'autoescape', 'locals', 'hardcode', 'cache']
+ register '.coffee', zappa.adapter require('coffeecup').adapters.express,
+ blacklist: ['format', 'autoescape', 'locals', 'hardcode', 'cache']
# Sets default view dir to @root (`path.dirname(module.parent.filename)`).
app.set 'views', path.join(context.root, '/views')
@@ -195,11 +165,37 @@ zappa.app = (func,options) ->
context.view = (obj) ->
for k, v of obj
- views["#{context.id}/#{k}"] = v
+ ext = path.extname(k)
+ if not ext
+ ext = '.' + app.get 'view engine'
+ kl = k + ext
+
+ if options.export_views
+ # Support both foo.bar and foo
+ loc = path.join( app.get('views'), options.export_views, k )
+ fs.writeFileSync loc, v
+ if kl
+ loc = path.join( app.get('views'), options.export_views, kl )
+ fs.writeFileSync loc, v
+ else
+ compile = compilers[ext]
+ if not compile?
+ compile = compilers[ext] = require(ext.slice 1).compile
+ if not compile?
+ throw new Error "Cannot find a compiler for #{ext}"
+ r =
+ render: (options,next) ->
+ template = compile v, options
+ next null, template options
+
+ # Support both foo.bar and foo
+ app.cache[k] = r
+ if kl
+ app.cache[kl] = r
context.register = (obj) ->
for k, v of obj
- app.register '.' + k, v
+ register '.' + k, v
context.set = (obj) ->
for k, v of obj
@@ -330,41 +326,52 @@ zappa.app = (func,options) ->
for k, v of arguments[0]
render.apply @, [k, v]
- render = (args...) ->
- # Adds the app id to the view name so that the monkeypatched
- # express.View.exists and express.View.contents can lookup
- # this app's inline templates.
- args[0] = context.id + '/' + args[0]
-
- # Make sure the second arg is an object.
- args[1] ?= {}
- args.splice 1, 0, {} if typeof args[1] is 'function'
-
- if app.settings['databag']
- args[1].params = data
+ render = (name,opts,next) ->
+ # Default callback to send
+ next ?= (err,str) ->
+ if err then return req.next err
+ res.send.call res, str
- # Don't change layout: false
- unless args[1].layout is false
- # Use the default layout if one isn't given, or layout: true
- if args[1].layout is true or not args[1].layout?
- args[1].layout = 'layout'
+ # Make sure the second arg is an object.
+ if typeof opts is 'function'
+ next = opts
+ opts = {}
- # Don't add id if it's there already
- if args[1].layout.split('/')[0] is not context.id
- args[1].layout = context.id + '/' + args[1].layout
+ if app.settings['databag']
+ opts.params = data
- if args[1].postrender?
- # Apply postrender before sending response.
- res.render args[0], args[1], (err, str) ->
+ apply_layout = (str,fn) ->
+ # Don't change layout: false
+ if opts.layout is false
+ fn null, str
+ else
+ # Use the default layout if one isn't given, or layout: true
+ if opts.layout is true or not opts.layout?
+ opts.layout = 'layout'
+ opts.body = str
+ res.render.call res, opts.layout, opts, fn
+
+ postrender = (str,fn) ->
+ if opts.postrender?
+ # Apply postrender before sending response.
jsdom.env html: str, src: [jquery], done: (err, window) ->
+ if err then return fn err
ctx.window = window
- rendered = postrenders[args[1].postrender].apply(ctx, [window.$, ctx])
+ rendered = postrenders[opts.postrender].apply(ctx, [window.$, ctx])
doctype = (window.document.doctype or '') + "\n"
- res.send doctype + window.document.documentElement.outerHTML
- else
- # Just forward params to express.
- res.render.apply res, args
+ fn null, doctype + window.document.documentElement.outerHTML
+ else
+ fn null, str
+
+ report = (err) ->
+ return false if not err
+ next err
+ return true
+
+ res.render.call res, name, opts, (err,str) ->
+ report(err) or apply_layout str, (err,str) ->
+ report(err) or postrender str, next
apply_helpers ctx
@@ -506,11 +513,13 @@ zappa.run = ->
zapp = zappa.app(root_function,options)
app = zapp.app
- if host then app.listen port, host
- else app.listen port
+ if host
+ app.server.listen port, host
+ else
+ app.server.listen port
log 'Express server listening on port %d in %s mode',
- app.address()?.port, app.settings.env
+ app.server.address()?.port, app.settings.env
log "Zappa #{zappa.version} \"#{codename}\" orchestrating the show"
@@ -17,8 +17,8 @@ class Client
@app = arg
check = =>
try
- @host = @app.address().address
- @port = @app.address().port
+ @host = @app.server.address().address
+ @port = @app.server.address().port
catch err
process.nextTick check
check()
View
@@ -83,13 +83,16 @@ port = 15600
c = t.client(zapp.app)
c.get '/', (err, res) ->
- t.equal 1, res.body, '<!DOCTYPE html><html><head><title>CoffeeKup file layout</title></head><body><h2>CoffeeKup file template: bar</h2></body></html>'
+ # Express 3 no longer supports layout.
+ # t.equal 1, res.body, '<!DOCTYPE html><html><head><title>CoffeeKup file layout</title></head><body><h2>CoffeeKup file template: bar</h2></body></html>'
+ t.equal 1, res.body, '<h2>CoffeeKup file template: bar</h2>'
'eco, inline': (t) ->
t.expect 1
t.wait 3000
zapp = zappa port++, ->
+ @app.engine 'eco', require('consolidate').eco
@set 'view engine': 'eco'
@get '/': ->
@@ -106,6 +109,7 @@ port = 15600
t.wait 3000
zapp = zappa port++, ->
+ @app.engine 'eco', require('consolidate').eco
@set 'view engine': 'eco'
@get '/': ->
@@ -132,6 +136,7 @@ port = 15600
t.wait 3000
zapp = zappa port++, ->
+ @app.engine 'eco', require('consolidate').eco
@set 'view engine': 'eco'
@get '/': ->
@@ -146,6 +151,7 @@ port = 15600
t.wait 3000
zapp = zappa port++, ->
+ @app.engine 'eco', require('consolidate').eco
@set 'view engine': 'eco'
@get '/': ->

0 comments on commit 296ede6

Please sign in to comment.