• Stars
    star
    461
  • Rank 95,028 (Top 2 %)
  • Language
    CoffeeScript
  • Created almost 13 years ago
  • Updated over 11 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Sinatra for Node

Ace is Sinatra for Node; a simple web-server written in CoffeeScript with a straightforward API.

Every request is wrapped in a Node Fiber, allowing you to program in a synchronous manner without callbacks, but with all the advantages of an asynchronous web-server.

app.put '/posts/:id', ->
  @post = Post.find([email protected]).wait()
  @post.updateAttributes(
    name: @params.name,
    body: @params.body
  ).wait()
  @redirect @post

Ace is built on top of the rock solid Strata HTTP framework.

##Examples

You can find an example blog app, including authentication and updating posts, in Ace's examples directory.

##Usage

Node >= v0.7.3 is required, as well as npm. Ace will run on older versions of Node, but will crash under heavy load due to a bug in V8 (now fixed).

To install, run:

npm install -g git://github.com/maccman/ace.git

To generate a new app, run:

ace new myapp
cd myapp
npm install .

To serve up an app, run:

ace

##Routing

In Ace, a route is a HTTP method paired with a URL matching pattern. For example:

app.get '/users', ->
  'Hello World'

Anything returned from a routing callback is set as the response body.

You can also specify a routing pattern, which is available in the callback under the @route object.

app.get '/users/:name', ->
  "Hello #{@route.name}"

POST, PUT and DELETE callbacks are also available, using the post, put and del methods respectively:

app.post '/users', ->
  @user = User.create(
    name: @params.name
  ).wait()
  @redirect "/users/#{@user.id}"

app.put '/users/:id', ->
  @user = User.find([email protected]).wait()
  @user.updateAttributes(
    name: @params.name
  ).wait()
  @redirect "/users/#{@user.id}"

app.del '/user/:id', ->
  @user = User.find([email protected]).wait()
  @user.destroy().wait()
  @redirect "/users"

##Parameters

URL encoded forms, multipart uploads and JSON parameters are available via the @params object:

app.post '/posts', ->
  @post = Post.create(
    name: @params.name,
    body: @params.body
  ).wait()

  @redirect "/posts/#{@post.id}"

##Request

You can access request information using the @request object.

app.get '/request', ->
  result =
    protocol:     @request.protocol
    method:       @request.method
    remoteAddr:   @request.remoteAddr
    pathInfo:     @request.pathInfo
    contentType:  @request.contentType
    xhr:          @request.xhr
    host:         @request.host

  @json result

For more information, see request.js.

You can access the full request body via @body:

app.get '/body', ->
  "You sent: #{@body}"

You can check to see what the request accepts in response:

app.get '/users', ->
  @users = User.all().wait()

  if @accepts('application/json')
    @jsonp @users
  else
    @eco 'users/list'

You can also look at the request format (calculated from the URL's extension). This can often give a better indication of what clients are expecting in response.

  app.get '/users', ->
    @users = User.all().wait()

    if @format is 'application/json'
      @jsonp @users
    else
      @eco 'users/list'

Finally you can access the raw @env object:

@env['Warning']

##Responses

As well as returning the response body as a string from the routing callback, you can set the response attributes directly:

app.get '/render', ->
  @headers['Transfer-Encoding'] = 'chunked'
  @contentType = 'text/html'
  @status = 200
  @body = 'my body'

You can set the @headers, @status and @body attributes to alter the request's response.

If you only need to set the status code, you can just return it directly from the routing callback. The properties @ok, @unauthorized and @notFound are aliased to their relevant status codes.

app.get '/render', ->
  # ...
  @ok

##Static

By default, if a folder called public exists under the app root, its contents will be served up statically. You can configure the path of this folder like so:

app.set public: './public'

You can add static assets like stylesheets and images to the public folder.

##Templates

Ace includes support for rendering CoffeeScript, Eco, EJS, Less, Mustache and Stylus templates. Simply install the relevant module and the templates will be available to render.

For example, install the eco module and the @eco function will be available to you.

app.get '/users/:name', ->
  @name = @route.name
  @eco 'user/show'

The @eco function takes a path of the Eco template. By default, this should be located under a folder named ./views. The template is rendered in the current context, so you can pass variables to them by setting them locally.

If a file exists under ./views/layout.*, then it'll be used as the application's default layout. You can specify a different layout with the layout option.

app.get '/users', ->
  @users = User.all().wait()
  @mustache 'user/list', layout: 'basic'

##JSON

You can serve up JSON and JSONP with the @json and @jsonp helpers respectively.

app.get '/users', ->
  @json {status: 'ok'}

app.get '/users', ->
  @users = User.all().wait()
  @jsonp @users

By default @jsonp uses the @params.callback parameter as the name of its wrapper function.

##Fibers

Every request in Ace is wrapped in a Fiber. This means you can do away with the callback spaghetti that Node applications often descend it. Rather than use callbacks, you can simply pause the current fiber. When the callback returns, the fibers execution continues from where it left off.

In practice, Ace provides a couple of utility functions for pausing asynchronous functions. Ace adds a wait() function to EventEmitter. This transforms asynchronous calls on libraries such as Sequelize.

For example, save() is usually an asynchronous call which requires a callback. Here we can just call save().wait() and use a synchronous style.

app.get '/projects', ->
  project = Project.build(
    name: @params.name
  )

  project.save().wait()

  @sleep(2000)

  "Saved project: #{project.id}"

This fiber technique also means we can implement functionality like sleep() in JavaScript, as in the previous example.

You can make an existing asynchronous function fiber enabled, by wrapping it with Function::wait().

syncExists = fs.exists.bind(fs).wait

if syncExists('./path/to/file')
  @sendFile('./path/to/file)

Fibers are pooled, and by default there's a limit of 100 fibers in the pool. This means that you can serve up to 100 connections simultaneously. After the pool limit is reached, requests are queued. You can increase the pool size like so:

app.pool.size = 250

##Cookies & Session

Sessions are enabled by default in Ace. You can set and retrieve data stored in the session by using the @session object:

app.get '/login', ->
  user = User.find(email: @params.email).wait()
  @session.user_id = user.id
  @redirect '/'

You can retrieve cookies via the @cookie object, and set them with @response.setCookie(name, value);

app.get '/login', ->
  token = @cookies.rememberMe
  # ...

##Filters

Ace supports 'before' filters, callbacks that are executed before route handlers.

app.before ->
  # Before filter

By default before filters are always executed. You can specify conditions to limit that, such as routes.

app.before '/users*', ->

The previous filter will be executed before any routes matching /users* are.

As well as a route, you can specify a object to match the request against:

app.before method: 'POST', ->
  ensureLogin()

Finally you can specify a conditional function that'll be passed the request's env, and should return a boolean indicating whether the filter should be executed or not.

app.before conditionFunction, ->

If a filter changes the response status to anything other than 200, then execution will halt.

app.before ->
  if @request.method isnt 'GET' and [email protected]
    @head 401

##Context

You can add extra properties to the routing callback context using context.include():

app.context.include
  loggedIn: -> [email protected]_id

app.before '/admin*', ->
  if @loggedIn()
    @ok
  else
    @redirect '/login'

The context includes a few utilities methods by default:

@redirect(url)
@sendFile(path)
@head(status)

@basicAuth (user, pass) ->
  user is 'foo' and pass is 'bar'

##Configuration

Ace includes some sensible default settings, but you can override them using @set, passing in an object of names to values.

@app.set static:   true       # Serve up file statically from public
         sessions: true       # Enable sessions
         port:     1982       # Server port number
         bind:     '0.0.0.0'  # Bind to host
         views:    './views'  # Path to 'views' dir
         public:   './public' # Path to 'public' dir
         layout:   'layout'   # Name of application's default layout
         logging:  true       # Enable logging

Settings are available on the @settings object:

if app.settings.logging is true
  console.log('Logging is enabled')

##Middleware

Middleware sits on the request stack, and gets executed before any of the routes. Using middleware, you can alter the request object such as HTTP headers or the request's path.

Ace sits on top of Strata, so you can use any of the middleware that comes with the framework, or create your own.

For example, we can use Ace's methodOverride middleware, enabling us to override the request's HTTP method with a _method parameter.

strata = require('ace').strata
app.use strata.methodOverride

This means we can use HTML forms to send requests other than GET or POST ones, keeping our application RESTful:

<form action="<%= @post.url() %>" method="post">
  <input type="hidden" name="_method" value="delete">
  <button>Delete</button>
</form>

For more information on creating your own middleware, see Strata's docs.

##Credits

Ace was built by Alex MacCaw and Michael Jackson.

More Repositories

1

juggernaut

[DEPRECATED] Realtime server push with node.js, WebSockets and Comet
JavaScript
1,626
star
2

monocle

Link and news sharing
Ruby
1,453
star
3

abba

A/B testing framework
Ruby
1,351
star
4

holla

Holla! - Rich JavaScript Application
JavaScript
1,069
star
5

jquery.magicmove

Animate DOM transitions.
JavaScript
644
star
6

bowline

Ruby/JS GUI and Binding framework (deprecated)
Ruby
637
star
7

stylo

Spine/CoffeeScript example GUI designer
JavaScript
525
star
8

saasy

Rails SaaS and SSO solution
Ruby
523
star
9

gfx

CSS3 3D animation library
JavaScript
511
star
10

nestful

Simple Ruby HTTP/REST client with a sane API
Ruby
505
star
11

supermodel

Ruby in-memory models
Ruby
367
star
12

acts_as_recommendable

Collaborative Filtering for Rails
Ruby
325
star
13

book-assets

Files for the O'Reilly book JavaScript Web Applications
JavaScript
310
star
14

go

go
Ruby
258
star
15

trevi

An opinionated Sinatra app generator
Ruby
251
star
16

flarevideo

HTML5 & Flash Video Player
JavaScript
243
star
17

spine.todos

A Backbone alternative idea
JavaScript
239
star
18

hermes

Messaging re-invented
Ruby
206
star
19

motivation

New Chrome tab page showing your age
JavaScript
197
star
20

juggernaut_plugin

Realtime Rails
JavaScript
195
star
21

spine.contacts

Spine demo contact manager
CoffeeScript
182
star
22

sprockets-commonjs

Adds CommonJS support to Sprockets
Ruby
179
star
23

macgap-rb

Generator for MacGap
Objective-C
159
star
24

sinatra-blog

A example Sinatra blog
Ruby
157
star
25

headsup

A simple Heads Up display
Ruby
145
star
26

catapult

A Sprockets/Rack build tool
Ruby
139
star
27

remail

RESTful email for Rails
Ruby
138
star
28

spine.rails3

Sample app demonstrating Spine and Rails integration
Ruby
130
star
29

bowline-twitter

Bowline Twitter client
JavaScript
112
star
30

wysiwyg

CoffeeScript
104
star
31

sinatra-pubsub

Push & Streaming for Sinatra.
Ruby
99
star
32

push-mac

Objective-C
94
star
33

stitch-rb

Stitch ported to Ruby
Ruby
93
star
34

dhash

Compare image similarity with a dhash
Ruby
93
star
35

101-school

AI generated courses
TypeScript
92
star
36

ichabod

Headless JavaScript testing with WebKit
JavaScript
85
star
37

colorcanvas

JavaScript
83
star
38

roauth

*Simple* Ruby OAuth library
Ruby
82
star
39

push

Ruby
81
star
40

juggernaut_gem

Realtime Rails
Ruby
79
star
41

spine.mobile

Spine Mobile Framework
CoffeeScript
77
star
42

super.js

Simple JavaScript framework for building RIAs (with jQuery)
JavaScript
64
star
43

oped

Email based diary
Ruby
57
star
44

remail-engine

RESTful email for Rails - see http://github.com/maccman/remail
Python
48
star
45

syncro

Synchronize state across remote clients.
Ruby
47
star
46

spine.mobile.currency

Spine Mobile currency convertor example
CoffeeScript
46
star
47

sourcemap

Ruby library for using Source Maps
Ruby
43
star
48

superapp

JavaScript state machine and class abstraction for building RIAs (deprecated! - use http://github.com/maccman/super.js)
JavaScript
31
star
49

sprockets-source-url

Adds @sourceURL support to Sprockets
Ruby
30
star
50

spine.infinite

Infinite scrolling with Spine & Rails
JavaScript
29
star
51

bowline-desktop

wxWidgets/Ruby/Webkit framework for Bowline apps
C++
28
star
52

ymockup

UI mockups using HTML and CSS
JavaScript
28
star
53

supermodel-js

SuperModel in JavaScript (deprecated! - use http://github.com/maccman/super.js)
JavaScript
26
star
54

zombies

A Facebook/Spine game.
Ruby
24
star
55

quora2

Redesigning Quora's interface, turning it into a JavaScript web application powered by Spine.
JavaScript
21
star
56

spine.mobile.contacts

Example Spine Mobile App
CoffeeScript
20
star
57

humanapi

Ruby
20
star
58

request-profile

API to access autocomplete data
JavaScript
19
star
59

gwo

Rails plugin integrating Google Web Optimizer for AB tests
Ruby
18
star
60

cft

CoffeeScript
17
star
61

jquery.upload.js

Upload files using Ajax
JavaScript
17
star
62

spine.realtime

Realtime Spine app with Rails
Ruby
15
star
63

rbyte

Byte compile Ruby 1.9.1 files and "require" support for loading compiled files.
Ruby
14
star
64

phonegap

Gem for building PhoneGap apps
Shell
13
star
65

segment-hooks

Trigger arbitrary JavaScript from Segment.com events
JavaScript
13
star
66

spine.mobile.workout

Spine Mobile Workouts Example
CoffeeScript
11
star
67

restful_email

AppEngine that provides a RESTful interface to sending emails (decrep - use http://github.com/maccman/remail)
Python
10
star
68

csbook

CoffeeScript
10
star
69

statster

Merb Web Analytics
Ruby
9
star
70

rack-modulr

Use CommonJS modules in your Rack/Rails applications
9
star
71

blossom

Demonstration chat app
JavaScript
9
star
72

dtwitter

Distributed Twitter
9
star
73

canonical

Rails plugin providing helper for canonical URLs
8
star
74

jquery.drop.js

jQuery lib abstracting the drag/drop API
JavaScript
8
star
75

alexmaccaw

Portfolio
Ruby
8
star
76

omniauth-humanapi

OmniAuth strategy for HumanAPI.
Ruby
8
star
77

the-managers-handbook

JavaScript
8
star
78

syncro-js

JavaScript library for Syncro
JavaScript
8
star
79

less-rb

Less using ExecJS
Ruby
7
star
80

spine.tutorials

Spine tutorials (DEPRECATED - use http://spinejs.com)
JavaScript
6
star
81

bowline-bundler

Specialized version of the Bundler gem for Bowline apps
Ruby
6
star
82

serveup

JavaScript
5
star
83

gdata

Recent clone of http://code.google.com/p/gdata-ruby-util (with Ruby 1.9 support)
Ruby
5
star
84

package-jquery

JavaScript
5
star
85

package-jquery-ui

JavaScript
4
star
86

jquery.tmpl

jQuery.tmpl for Hem
JavaScript
4
star
87

node-twitter-stream

Twitter Streaming API Library for Node.js
JavaScript
4
star
88

counterman

Ruby
4
star
89

monocle-assets

Ruby
4
star
90

jlink

jQuery data binding library - bind objects to HTML elements
JavaScript
4
star
91

cloudflare-r2-edge

TypeScript
4
star
92

super.todos

Port of Backbone.js Todos to Super
JavaScript
4
star
93

jeco

jQuery extension to eco
CoffeeScript
3
star
94

invoices

Test Spine app
JavaScript
3
star
95

like-detector

TypeScript
3
star
96

bp-p2p

Browser Plus P2P
Ruby
3
star
97

renoir

Simple Canvas physics engine using Verlet Integration
JavaScript
3
star
98

hnv2

Hacker News V2
CoffeeScript
3
star
99

elb-nginx-vhosts

Shell
2
star
100

socialmod

Ruby/Python/PHP client libs
PHP
2
star