Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

add new API

  • Loading branch information...
commit 310069a4107d84814478396cf3c94a8f29d99f58 1 parent 9c0132a
@maccman authored
View
8 bin/ace
@@ -23,8 +23,14 @@ if ( !filename ) {
}
filename = path.resolve(filename);
+process.chdir(path.dirname(filename))
var app = new App;
global.app = app;
require(filename);
-strata.run(app);
+
+strata.run(app, {
+ host: app.config.host,
+ port: app.config.port,
+ socket: app.config.socket
+});
View
55 lib/app.js
@@ -1,27 +1,42 @@
(function() {
- var App, context, fibers, method, methods, strata,
+ var App, context, fibers, fs, method, methods, path, static, strata,
__hasProp = Object.prototype.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; },
__slice = Array.prototype.slice;
+ fs = require('fs');
+
+ path = require('path');
+
strata = require('strata');
fibers = require('./fibers');
context = require('./context');
+ static = require('./static');
+
App = (function(_super) {
__extends(App, _super);
+ App.prototype.config = {
+ static: true,
+ sessions: true,
+ port: 1982,
+ bind: '0.0.0.0',
+ root: process.cwd(),
+ views: './views',
+ public: './public'
+ };
+
function App() {
- App.__super__.constructor.apply(this, arguments);
+ App.__super__.constructor.call(this);
this.pool = new fibers.Pool;
this.router = new strata.Router;
this.use(strata.commonLogger);
this.use(strata.contentType, 'text/html');
this.use(strata.contentLength);
- this.run(this.router);
}
App.prototype.route = function(pattern, app, methods) {
@@ -30,6 +45,40 @@
return this.router.route(pattern, app, methods);
};
+ App.prototype.set = function(key, value) {
+ var k, v, _results;
+ if (typeof key === 'object') {
+ _results = [];
+ for (k in key) {
+ v = key[k];
+ _results.push(this.set(k, v));
+ }
+ return _results;
+ } else {
+ return this.config[key] = value;
+ }
+ };
+
+ App.prototype.addSessions = function() {
+ var options;
+ options = {};
+ if (typeof this.config.sessions === 'object') options = this.config.sessions;
+ return this.use(strata.sessionCookie, options);
+ };
+
+ App.prototype.addStatic = function() {
+ if (fs.existsSync(this.config.public)) {
+ return this.use(static, this.config.public, ['index.html']);
+ }
+ };
+
+ App.prototype.toApp = function() {
+ if (this.config.sessions) this.addSessions();
+ if (this.config.static) this.addStatic();
+ this.run(this.router);
+ return App.__super__.toApp.apply(this, arguments);
+ };
+
return App;
})(strata.Builder);
View
10 lib/context.js
@@ -27,7 +27,7 @@
this.served = true;
if (Array.isArray(response)) {
return this.callback.apply(this, response);
- } else if (response.body != null) {
+ } else if ((response != null ? response.body : void 0) != null) {
return this.callback(response.status || 200, response.headers || {}, response.body || '');
} else {
return this.callback(200, {}, response || '');
@@ -54,6 +54,14 @@
return this.env.route;
});
+ Context.prototype.__defineGetter__('session', function() {
+ return this.env.session;
+ });
+
+ Context.prototype.__defineSetter__('session', function(value) {
+ return this.env.session = value;
+ });
+
Context.wrap = function(app) {
return function(env, callback) {
var context, result;
View
12 lib/helpers.js
@@ -1,10 +1,12 @@
(function() {
- var context, fs, head, path, sendFile;
+ var context, fs, head, path, redirect, sendFile, strata;
fs = require('fs');
path = require('path');
+ strata = require('strata');
+
context = require('./context');
sendFile = function(file, options) {
@@ -34,9 +36,15 @@
return [status, {}, body];
};
+ redirect = function(url) {
+ strata.redirect(this.env, this.callback, url);
+ return this.served = true;
+ };
+
context.include({
sendFile: sendFile,
- head: head
+ head: head,
+ redirect: redirect
});
module.exports = {
View
21 lib/index.js
@@ -1,17 +1,26 @@
(function() {
- var App, context;
+ var App, context, helpers, name, _i, _len, _ref;
require('./ext');
- require('./helpers');
-
App = require('./app');
context = require('./context');
- module.exports({
+ helpers = require('./helpers');
+
+ _ref = ['coffee', 'eco', 'less', 'mustache'];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ name = _ref[_i];
+ try {
+ require("./templates/" + name);
+ } catch (_error) {}
+ }
+
+ module.exports = {
App: App,
- context: context
- });
+ context: context,
+ helpers: helpers
+ };
}).call(this);
View
68 lib/static.js
@@ -0,0 +1,68 @@
+(function() {
+ var exists, fs, mime, path, sendFile, strata, utils;
+
+ path = require('path');
+
+ fs = require('fs');
+
+ mime = require('mime');
+
+ strata = require('./index');
+
+ utils = strata.utils;
+
+ exists = function(path) {
+ var fiber;
+ fiber = Fiber.current;
+ path.exists(path, function(exists) {
+ return fiber.run(exists);
+ });
+ return yield();
+ };
+
+ sendFile = function(callback, path, stats) {
+ return callback(200, {
+ 'Content-Type': mime.lookup(path),
+ 'Content-Length': stats.size.toString(),
+ 'Last-Modified': stats.mtime.toUTCString()
+ }, fs.createReadStream(path));
+ };
+
+ module.exports = function(app, root, index) {
+ if (typeof root !== 'string') throw new strata.Error('Invalid root directory');
+ if (!path.existsSync(root)) {
+ throw new strata.Error("Directory " + root + " does not exist");
+ }
+ if (!fs.statSync(root).isDirectory()) {
+ throw new strata.Error("" + root + " is not a directory");
+ }
+ if (index && typeof index === 'string') index = [index];
+ return function(env, callback) {
+ var fullPath, indexPath, pathInfo, stats, _i, _len;
+ if (env.requestMethod !== 'GET') return app(env, callback);
+ pathInfo = unescape(env.pathInfo);
+ if (pathInfo.indexOf('..') !== -1) return utils.forbidden(env, callback);
+ fullPath = path.join(root, pathInfo);
+ exists = fs.existsSync(fullPath);
+ if (!exists) return app(env, callback);
+ stats = fs.statSync(fullPath);
+ if (stats.isFile()) {
+ return sendFile(callback, fullPath, stats);
+ } else if (stats.isDirectory() && index) {
+ for (_i = 0, _len = index.length; _i < _len; _i++) {
+ indexPath = index[_i];
+ indexPath = path.join(fullPath, indexPath);
+ exists = fs.existsSync(indexPath);
+ if (exists) {
+ sendFile(callback, indexPath, stats);
+ break;
+ }
+ }
+ return app(env, callback);
+ } else {
+ return app(env, callback);
+ }
+ };
+ };
+
+}).call(this);
View
35 lib/templates/coffee.js
@@ -0,0 +1,35 @@
+(function() {
+ var coffee, context, fs, path, view;
+
+ path = require('path');
+
+ fs = require('fs');
+
+ context = require('../context');
+
+ coffee = require('coffee-script');
+
+ view = function(template, context) {
+ var fiber;
+ fiber = Fiber.current;
+ fs.readFile(template, 'utf8', function(err, data) {
+ var headers, result;
+ if (err) fiber.throwInto(err);
+ headers = {
+ 'Content-Type': 'text/javascript'
+ };
+ result = coffee.compile(data);
+ return fiber.run([200, headers, result]);
+ });
+ return yield();
+ };
+
+ context.include({
+ coffee: view
+ });
+
+ module.exports = {
+ coffee: view
+ };
+
+}).call(this);
View
35 lib/templates/eco.js
@@ -0,0 +1,35 @@
+(function() {
+ var context, eco, fs, path, view;
+
+ path = require('path');
+
+ fs = require('fs');
+
+ context = require('../context');
+
+ eco = require('eco');
+
+ view = function(template, context) {
+ var fiber;
+ fiber = Fiber.current;
+ fs.readFile(template, 'utf8', function(err, data) {
+ var headers, result;
+ if (err) fiber.throwInto(err);
+ headers = {
+ 'Content-Type': 'text/html'
+ };
+ result = eco.render(data, projects);
+ return fiber.run([200, headers, result]);
+ });
+ return yield();
+ };
+
+ context.include({
+ eco: view
+ });
+
+ module.exports = {
+ eco: view
+ };
+
+}).call(this);
View
37 lib/templates/less.js
@@ -0,0 +1,37 @@
+(function() {
+ var context, fs, less, path, view;
+
+ path = require('path');
+
+ fs = require('fs');
+
+ context = require('../context');
+
+ less = require('less');
+
+ view = function(template, context) {
+ var fiber;
+ fiber = Fiber.current;
+ fs.readFile(template, 'utf8', function(err, data) {
+ var headers;
+ if (err) fiber.throwInto(err);
+ headers = {
+ 'Content-Type': 'text/css'
+ };
+ return less.render(data, function(err, css) {
+ if (err) fiber.throwInto(err);
+ return fiber.run([200, headers, css]);
+ });
+ });
+ return yield();
+ };
+
+ context.include({
+ less: view
+ });
+
+ module.exports = {
+ less: view
+ };
+
+}).call(this);
View
33 lib/templates/mustache.js
@@ -0,0 +1,33 @@
+(function() {
+ var Mu, context, mustache, path;
+
+ path = require('path');
+
+ context = require('../context');
+
+ Mu = require('mu');
+
+ mustache = function(template, context) {
+ var fiber;
+ fiber = Fiber.current;
+ Mu.compile(template, function(err, parsed) {
+ var headers;
+ if (err) fiber.throwInto(err);
+ headers = {
+ 'Transfer-Encoding': 'chunked',
+ 'Content-Type': 'text/html'
+ };
+ return fiber.run([200, headers, Mu.render(template, context)]);
+ });
+ return yield();
+ };
+
+ context.include({
+ mustache: mustache
+ });
+
+ module.exports = {
+ mustache: mustache
+ };
+
+}).call(this);
View
40 lib/templates/stylus.js
@@ -0,0 +1,40 @@
+(function() {
+ var context, fs, path, stylus, view;
+
+ path = require('path');
+
+ fs = require('fs');
+
+ context = require('../context');
+
+ stylus = require('stylus');
+
+ view = function(template, context) {
+ var fiber;
+ fiber = Fiber.current;
+ fs.readFile(template, 'utf8', function(err, data) {
+ var headers;
+ if (err) fiber.throwInto(err);
+ headers = {
+ 'Content-Type': 'text/css'
+ };
+ return stylus.render(data, {
+ filename: template
+ }, function(err, css) {
+ if (err) fiber.throwInto(err);
+ return fiber.run([200, headers, css]);
+ });
+ });
+ return yield();
+ };
+
+ context.include({
+ stylus: view,
+ styl: view
+ });
+
+ module.exports = {
+ stylus: view
+ };
+
+}).call(this);
View
3  package.json
@@ -11,6 +11,7 @@
"bin": { "ace": "./bin/ace" },
"dependencies": {
"strata": "git://github.com/maccman/strata.git",
- "fibers": "git://github.com/laverdet/node-fibers.git#master"
+ "fibers": "git://github.com/laverdet/node-fibers.git#master",
+ "mime": "1.2.4"
}
}
View
37 src/app.coffee
@@ -1,22 +1,55 @@
+fs = require('fs')
+path = require('path')
strata = require('strata')
fibers = require('./fibers')
context = require('./context')
+static = require('./static')
class App extends strata.Builder
+ config:
+ static: true
+ sessions: true
+ port: 1982
+ bind: '0.0.0.0'
+ root: process.cwd()
+ views: './views'
+ public: './public'
+
constructor: ->
- super
+ super()
@pool = new fibers.Pool
@router = new strata.Router
@use(strata.commonLogger)
@use(strata.contentType, 'text/html')
@use(strata.contentLength)
- @run(@router)
route: (pattern, app, methods) ->
app = context.wrap(app)
app = @pool.wrap(app)
@router.route(pattern, app, methods)
+ set: (key, value) ->
+ if typeof key is 'object'
+ @set(k, v) for k, v of key
+ else
+ @config[key] = value
+
+ addSessions: ->
+ options = {}
+ if typeof @config.sessions is 'object'
+ options = @config.sessions
+ @use(strata.sessionCookie, options)
+
+ addStatic: ->
+ if fs.existsSync(@config.public)
+ @use(static, @config.public, ['index.html'])
+
+ toApp: ->
+ @addSessions() if @config.sessions
+ @addStatic() if @config.static
+ @run(@router)
+ super
+
methods =
get: ['GET', 'HEAD'],
post: 'POST',
View
4 src/context.coffee
@@ -14,7 +14,7 @@ class Context
if Array.isArray(response)
@callback(response...)
- else if response.body?
+ else if response?.body?
@callback(
response.status or 200,
response.headers or {},
@@ -28,6 +28,8 @@ class Context
@::__defineGetter__ 'query', -> @request.query.bind(@request).wait()
@::__defineGetter__ 'body', -> @request.body.bind(@request).wait()
@::__defineGetter__ 'route', -> @env.route
+ @::__defineGetter__ 'session', -> @env.session
+ @::__defineSetter__ 'session', (value) -> @env.session = value
@wrap: (app) ->
(env, callback) ->
View
10 src/index.coffee
@@ -1,9 +1,13 @@
require('./ext')
-require('./helpers')
App = require('./app')
context = require('./context')
+helpers = require('./helpers')
-module.exports
+for name in ['coffee', 'eco', 'less', 'mustache']
+ try require("./templates/#{name}")
+
+module.exports =
App: App
- context: context
+ context: context
+ helpers: helpers
View
55 src/static.coffee
@@ -0,0 +1,55 @@
+path = require('path')
+fs = require('fs')
+mime = require('mime')
+strata = require('./index')
+utils = strata.utils
+
+exists = (path) ->
+ fiber = Fiber.current
+ path.exists path, (exists) ->
+ fiber.run(exists)
+ yield()
+
+sendFile = (callback, path, stats) ->
+ callback 200,
+ 'Content-Type': mime.lookup(path)
+ 'Content-Length': stats.size.toString()
+ 'Last-Modified': stats.mtime.toUTCString()
+ , fs.createReadStream(path)
+
+module.exports = (app, root, index) ->
+ throw new strata.Error('Invalid root directory') if typeof root isnt 'string'
+ throw new strata.Error("Directory #{root} does not exist") unless path.existsSync(root)
+ throw new strata.Error("#{root} is not a directory") unless fs.statSync(root).isDirectory()
+ index = [ index ] if index and typeof index is 'string'
+
+ (env, callback) ->
+ unless env.requestMethod is 'GET'
+ return app(env, callback)
+
+ pathInfo = unescape(env.pathInfo)
+
+ unless pathInfo.indexOf('..') is -1
+ return utils.forbidden(env, callback)
+
+ fullPath = path.join(root, pathInfo)
+
+ exists = fs.existsSync(fullPath)
+ return app(env, callback) unless exists
+
+ stats = fs.statSync(fullPath)
+
+ if stats.isFile()
+ sendFile(callback, fullPath, stats)
+
+ else if stats.isDirectory() and index
+ for indexPath in index
+ indexPath = path.join(fullPath, indexPath)
+ exists = fs.existsSync indexPath
+ if exists
+ sendFile callback, indexPath, stats
+ break
+ app(env, callback)
+
+ else
+ app(env, callback)
View
20 src/templates/coffee.coffee
@@ -0,0 +1,20 @@
+path = require('path')
+fs = require('fs')
+context = require('../context')
+coffee = require('coffee-script')
+
+view = (template, context) ->
+ fiber = Fiber.current
+ fs.readFile template, 'utf8', (err, data) ->
+ fiber.throwInto(err) if err
+
+ headers = {'Content-Type': 'text/javascript'}
+ result = coffee.compile(data)
+ fiber.run([200, headers, result])
+ yield()
+
+context.include
+ coffee: view
+
+module.exports =
+ coffee: view
View
20 src/templates/eco.coffee
@@ -0,0 +1,20 @@
+path = require('path')
+fs = require('fs')
+context = require('../context')
+eco = require('eco')
+
+view = (template, context) ->
+ fiber = Fiber.current
+ fs.readFile template, 'utf8', (err, data) ->
+ fiber.throwInto(err) if err
+
+ headers = {'Content-Type': 'text/html'}
+ result = eco.render(data, projects)
+ fiber.run([200, headers, result])
+ yield()
+
+context.include
+ eco: view
+
+module.exports =
+ eco: view
View
21 src/templates/less.coffee
@@ -0,0 +1,21 @@
+path = require('path')
+fs = require('fs')
+context = require('../context')
+less = require('less')
+
+view = (template, context) ->
+ fiber = Fiber.current
+ fs.readFile template, 'utf8', (err, data) ->
+ fiber.throwInto(err) if err
+
+ headers = {'Content-Type': 'text/css'}
+ less.render data, (err, css) ->
+ fiber.throwInto(err) if err
+ fiber.run([200, headers, css])
+ yield()
+
+context.include
+ less: view
+
+module.exports =
+ less: view
View
2  src/templates/mustache.coffee
@@ -7,7 +7,7 @@ mustache = (template, context) ->
Mu.compile template, (err, parsed) ->
fiber.throwInto(err) if err
- headers = {'Transfer-Encoding': 'chunked'}
+ headers = {'Transfer-Encoding': 'chunked', 'Content-Type': 'text/html'}
fiber.run([200, headers, Mu.render(template, context)])
yield()
View
22 src/templates/stylus.coffee
@@ -0,0 +1,22 @@
+path = require('path')
+fs = require('fs')
+context = require('../context')
+stylus = require('stylus')
+
+view = (template, context) ->
+ fiber = Fiber.current
+ fs.readFile template, 'utf8', (err, data) ->
+ fiber.throwInto(err) if err
+
+ headers = {'Content-Type': 'text/css'}
+ stylus.render data, {filename: template}, (err, css) ->
+ fiber.throwInto(err) if err
+ fiber.run([200, headers, css])
+ yield()
+
+context.include
+ stylus: view
+ styl: view
+
+module.exports =
+ stylus: view
View
24 test/index.coffee
@@ -5,14 +5,26 @@ Project = sequelize.define('Project', {
description: Sequelize.TEXT
})
-app.pool.size = 800
-
-# TODO CRUD with auth
+# TODO CRUD with auth. Think about:
+# - picking up local npm deps
+#
+# app.set sessions: true
+# app.set static: true
+#
+# app.error type, ->
app.get '/users/:name', ->
"Hi #{@route.name}"
-app.get '/', ->
+app.get '/sessions', ->
+ res = @session.test
+ @session.test = 'works!'
+ res
+
+app.get '/redirect', ->
+ @redirect 'http://google.com'
+
+app.get '/projects', ->
project = Project.build(
name: @params.name
)
@@ -20,4 +32,8 @@ app.get '/', ->
project.save().wait()
# @sleep(200)
+ # @response [200, {}, '']
+ # @response ''
+ # @response ->
+ # @head 200
"Saved project: #{project.id}"
View
1  test/public/test.html
@@ -0,0 +1 @@
+<h1>TEST</h1>
Please sign in to comment.
Something went wrong with that request. Please try again.