v0.2.x “Peaches en Regalia”
Zappa was conceived almost a year ago as a crazy experimentalist hack to explore possibilities in the then forming and rapidly changing Node.js/CoffeeScript “platform”. Since then this “platform” matured a great deal, and it’s high time zappa is consolidated into something really usable, and more in tune with today’s node development ecosystem.
So the 0.1.x series is hereby retroactively codenamed “Jazz from Hell”, and work shall begin on 0.2.x “Peaches en Regalia”.
An initial beta for 0.2.0 is already available. To check it out, just run in your project directory:
npm install zappa@0.2.0beta
Or to follow the bleeding edge:
git clone git@github.com:mauricemach/zappa.git && cd zappa
cake build
npm link
Then in your project:
npm link zappa
In this first release, the main themes are:
Documentation
Three important pieces of documentation are introduced with this release. They’re all under heavy development!
-
Annotated source generated by docco can be found at
/docs/zappa.html. -
The API reference can be found at
/docs/reference.md. -
A quick migration guide from 0.1.x can be found at
/docs/migration.md.
Examples were also updated and added (see /examples).
A website will follow, before the final 0.2.0 release.
Fixing performance
Peaches is a rewrite from scratch, with the main goal from the beginning of not getting in the way of express’ performance. It’s 100% free of the with keyword, so it’s also kosher, FDA approved, carbon-neutral and all that.
The rough benchmark that accompanies the beta points to speed being now very close to that of “raw” express:
MacBook Air 3,2 (2 gigs RAM) ab -n 2000 -c 50
zappa: 2038.49 req/s express: 2075.79 req/s
This for rendering a simple jade template and layout in production mode.
But then, there are lies, damn lies, and benchmarks. Suggestions and contributions to more meaningful measurements are very welcome.
Getting out of your way
In peaches, zappa takes inspiration from the way CoffeeScript enhances JavaScript, providing alternative, progressive levels of syntactic sugar over the standard APIs, using abstractions more sparingly.
A zappa application should be closer to a summarized version of a “normal” node app. The underlying libraries’ implementations are used whenever possible, and familiar terms from them are preferred.
If you can do it with “raw” express/socket.io, you should be able to do it when using zappa too, only faster.
-
There is no zappa command. You just put your zappa code inside
require('zappa') ->blocks and compile/run it with the standardcoffee/nodecommands. -
Consequently, you can use all node debugging/development/deployment tools and deploy to all hosting environments with no special steps involved (in Jazz,
zappa -c app.coffee). -
A much better substitute for
zappa -wisnpm install run -g/runjs app.coffee. -
You specify port/host as in
require('zappa') 80, 'domain.com', ->. -
To run multiple apps, just use different zappa blocks on different ports.
-
require('zappa') ->is just an alias torequire('zappa').run ->. -
Both return an object with express
appand socket.ioioas attributes. -
There’s also
require('zappa').app ->which does the same but doesn’t callapp.listen. -
You can have code in the “normal” scope and zappa apps in their blocks coexisting in the same file.
-
You can even use zappa in JavaScript directly if you’re feeling masochist (see examples).
-
You have direct access to express
appand socket.ioioobjects at all scopes. -
In addition, there are sweeter shortcuts to most of their interfaces:
use,set,enable,disable,configure, etc. These are not only shorter names, but also can be used in additional ways (see reference). -
renderuses the express implementation, zappa only making sure that defining inline templates is still possible, as well as passing variables “automatically” through@. Arbitrary template engines, partials, file-based templates etc are all consequently supported. -
Socket.io now in 0.7.x works pretty much in the same way zappa did, client and server exchanging “events/messages” and JSON objects. So zappa just uses their implementation for this. There is no more
msg, onlyat(onwould be preferable, but that’s a CoffeeScript keyword). -
includeis implemented through the standard mechanisms of modularization (module.exportsandrequire). Just add@include = ->ormodule.exports.include = ->to your includes. -
Defaults are considerably cut down, instead you’re given very concise interfaces to define what you need (
enable 'default layout',use 'static', etc).
New features: client-side API and code sharing
All work and no play makes zappa a dull module! Even though Peaches is mainly about consolidation and cleanup, it also brings the beginnings of two important missing pieces in the zappa puzzle. A matching client-side API, and client-server code sharing.
-
What
clientdid in Jazz, taking a function and serving it as javascript, is now done bycoffee. -
client '/index.js': ->now serves code meant to be used in conjunction with/zappa/zappa.js. -
If sammy.js is available, you can define routes with
get '#/path': -> console.log 'foo'. -
If socket.io is available, you can define event handlers with
at 'server event': -> emit 'client event', foo: 'bar'. -
You can use
defandhelperswith these two. -
The idea is to have in the future all fitting features available in the client too, including
view,postrender,set,configureandenable/disable.
Sharing code
If you use shared '/foo.js' -> instead, the block of code will be both evaluated on the server and served to the client. Ex.:
shared '/shared.js': ->
def sum: (a, b) ->
a + b
if window
alert 'Running on the browser!'
else
console.log 'Running on the server!'
client '/index.js': ->
get '#/': -> console.log sum 5, 8
get '/': -> console.log sum 3, 6
Nice, now please show me some freaking code!
With all this standardization talk you’d expect zappa apps to now look a lot different from they did in Jazz. But in practice, the basic structure remains the same:
require('zappa') ->
include 'sub'
def foo: 'bar'
helper role: (name) ->
redirect '/' unless @user.role is name
get: '/': 'hi'
get: '/': ->
@foo = 'bar'
render 'index'
coffee '/index.js': ->
alert 'hi'
# socket.io code here.
at connection: ->
broadcast "#{@id} connected"
at hello: ->
emit 'reply', foo: 'bar'
view index: ->
h1 @foo
view layout: ->
doctype 5
html ->
head ->
title 'Foo'
script src: '/socket.io/socket.io.js'
script src: '/index.js'
link rel: 'stylesheet', href: '/index.css'
body @body
css '/index.css': '''
font-family: sans-serif;
'''
At sub.coffee:
# Could also be `module.exports.include`.
@include = ->
get '/sub': -> render 'sub'
view sub: ->
p 'Sub module included'
The actual big difference is what you can do now:
zappa = require 'zappa'
chat = zappa.app ->
set 'view engine': 'jade'
app.register '.jade', zappa.adapter 'jade'
io.set 'log level', 1
enable 'serve jquery'
use 'cookieParser', 'bodyParser'
configure development: ->
use errorHandler: {dumpExceptions: yes}
shared '/shared.js': ->
def sum: (a, b) -> a + b
get '/': ->
@foo = 'bar'
render 'index'
at connection: ->
broadcast 'joined', {id}
client '/index.js': ->
connect()
at joined: ->
alert "#{@id} joined us."
view index: '''
h1= foo
'''
view layout: '''
!!! 5
html
head
title A Jade template!
script(src='/socket.io/socket.io.js')
script(src='/zappa/jquery.js')
script(src='/zappa/zappa.js')
script(src='/shared.js')
script(src='/index.js')
body!= body
'''
wiki = zappa.app ->
get '/': 'wiki'
# wiki code here...
zappa 80, {chat, wiki}, ->
use express.vhost 'chat.com', chat.app
use express.vhost 'wiki.com', wiki.app
What’s next?
Before a final 0.2.0 release, at least these are still meant to be worked on:
-
Tests with zombie.js and qunit.
-
Error handling and reporting.
-
Finish fixing node’s globals in all scopes.
-
Asset merging, minification etc.
-
Where to put app-level variables shared between scopes.
-
Load socket.io on demand.
-
Socket.io’s other features (namespaces, channels, volatile messages, acknowledgements, authorization)
-
Rendering views in websocket handlers.
-
Views on the client (defining / sharing with server / rendering).
-
Rethink helpers and their relation to express view helpers (static and dynamic).
-
Allowing https servers.