index.coffee | |
---|---|
Ease is an extensive wrapper for
It takes care of setting important default values, as well as loading configuration, middleware and most importantly define RESTful default routes. | |
Application StructureAn Ease application follows a certain convention on where different parts can be found. All paths are relative to the application root directory.
However, all these paths are configurable. | |
Minimalistic exampleapp.coffee
resources/posts.coffee | |
Configuration | |
LicenseCopyright (c) 2011 Brainsware Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | express = require 'express'
path = require 'path'
fs = require 'fs'
file = require 'file'
module.exports = class Ease
constructor: (@config = {}, @root = path.dirname(module.parent.filename)) -> |
Workaround: @server_options returns undefined if SSL is not configured. Express' createServer doesn't like options being undefined. Or empty. Or null. | if @config.ssl?
@app = express.createServer @server_options()
else
@app = express.createServer()
@configure()
@register_middleware()
@register_resources() |
Sets values according to the config file.
| configure: ->
@app.set 'basepath', @config.base_path or '/'
@app.set 'views', @config.views_path or path.join @root, 'views'
@app.set 'view engine', @config.view_engine or 'jade' |
Registers middleware for development environment, enabling output of exceptions incl. stack. | development: =>
@app.use express.errorHandler
dumpExceptions: true
showStack: true
@app.use express.logger format: 'dev' |
Registers necessary middleware. | register_middleware: ->
@app.use express.bodyParser()
@app.use express.cookieParser() |
Register session middleware - if configured:
For possible session stores, see the Connect Wiki | if @config.sessions?
@app.use express.session { secret: @config.sessions.secret, store: @config.sessions.store }
@app.use express.methodOverride()
@app.use @app.router
@app.use express.static @config.assets_path or path.join(@root, 'public') |
Register all additional middleware present in the array
| if @config.middleware?
for middleware in @config.middleware
@app.use middleware
@app.configure 'development', @development |
Fires up the server and starts listening on | listen: ->
@app.listen(@config.port || 4000) |
Checks whether SSL is enabled in the config and returns
necessary options for SSL is enabled by setting | server_options: ->
return undefined unless @config.ssl?
{
key: fs.readFileSync @config.ssl.key
cert: fs.readFileSync @config.ssl.cert
} |
Find and register all available resources in
| register_resources: ->
@controllers ||= []
@config.resources_path ||= path.join @root, 'resources'
file.walkSync @config.resources_path, (start, directories, files) =>
for filename in files when filename.match /\.coffee$/
@register_controller(start, filename, @get_relative_path(start, filename)) |
Register routes for a specific resource controller | register_controller: (start, filename, relative_path) ->
route = @route_from_path(relative_path)
@controllers[relative_path] = new require(path.join(start, filename))
for method, fn of @controllers[relative_path].prototype
@register_route method, route, @proxy.bind(@controllers[relative_path], fn, method, relative_path) |
Additionally, the value of | @register_route method, '/', @proxy.bind(@controllers[relative_path], fn, method, relative_path) if relative_path is @config.base_resource
proxy: (fn, method, relative_path, request, response) ->
request.params['format'] ||= 'html' |
Throw 404 error if controller's @respond_to is defined but doesn't include the requested format. | if @respond_to?
return response.send(404) unless request.params.format in @respond_to
fn request, response |
Register a single route. This method uses Note: if you name a method other than | register_route: (method, route, fn) ->
http_method = @deduct_http_method(method)
@app[http_method] @deduct_route(method, route), fn if http_method? |
Find out what HTTP method given method responds to
| deduct_http_method: (method) ->
switch method
when 'index', 'show', 'new', 'edit' then 'get'
when 'create' then 'post'
when 'update' then 'put'
when 'destroy' then 'del' |
Generates the relative path between the value of | get_relative_path: (start, filename) ->
file.path.relativePath(@config.resources_path, path.join(start, path.basename(filename, '.coffee'))) |
Appends
| deduct_route: (method, route) ->
new_route = switch method
when 'index', 'create' then route
when 'show', 'update', 'destroy' then path.join route, ':id'
when 'new' then path.join route, 'new'
when 'edit' then path.join route, ':id', 'edit'
new_route + '.:format?' |
Turns a relative path into an actual URI:
Note: the :id part for the last resources in path
are appended later ( | route_from_path: (relative_path) ->
route = '/'
splitted_path = relative_path.split '/'
for index, route_part of splitted_path |
NOTE: index would be a string, splitted_path.length is a number.
CoffeeScript automagically transforms | index = parseInt(index)
route = if splitted_path.length == 1 or index == (splitted_path.length - 1)
path.join route, route_part
else if path.extname(route_part) is '.namespace'
path.join route, path.basename(route_part, '.namespace')
else
path.join route, route_part, ":#{route_part}_id"
route
|