Comparar commits

...

41 Commits

Autor SHA1 Mensagem Data
visionmedia 2f6dfbc165 Release 0.4.0 2010-02-11 17:02:30 -08:00
visionmedia 49cb53d735 Merge branch 'route-http-client-security' 2010-02-11 16:36:47 -08:00
visionmedia 54f1a51a10 Throwing error when routes are added at runtime
Since it doubles as an http client, without this
someone could arbitrarily create routes.. haha not good!
2010-02-11 16:36:38 -08:00
visionmedia c6a2674c2b Merge branch 'http' 2010-02-11 16:15:42 -08:00
visionmedia 282a10ec83 RESTful route functions double as HTTP clients. Closes #69
For example:
  get("http://google.com").addCallback(function(content){
    puts(content)
  })
2010-02-11 16:15:37 -08:00
visionmedia 50276a06df Merge branch 'http-client' 2010-02-11 16:11:00 -08:00
visionmedia f452250f88 Added high level restful http client 2010-02-11 16:10:43 -08:00
visionmedia 9d44e237a5 Added status code string to error 2010-02-11 15:30:16 -08:00
visionmedia 9f0e5899c2 Fixed Host header issue 2010-02-11 15:11:06 -08:00
visionmedia e351a02a06 Merge branch 'flash' 2010-02-11 14:22:59 -08:00
visionmedia cd167ec777 Added flash() example to sample upload app 2010-02-11 14:22:55 -08:00
visionmedia 3290412477 Updated haml 2010-02-11 14:02:26 -08:00
visionmedia acf0128fb4 Merge branch 'view-context' 2010-02-11 13:57:33 -08:00
visionmedia e91ee22a89 Defaulting render() context to the current Request. Closes #197 2010-02-11 13:57:26 -08:00
visionmedia 45ef08cf99 Release 0.3.0 2010-02-11 07:12:08 -08:00
visionmedia baa7d12ed6 Updated haml / sass submodules. Closes #200 2010-02-11 07:09:15 -08:00
visionmedia 822de581b3 flash() returns null when no flashes are available. Closes #198 2010-02-09 16:34:19 -08:00
visionmedia 3863a76fc8 Merge branch 'flash' 2010-02-09 08:41:32 -08:00
visionmedia 9727fac291 Added flash support. Closes #64 2010-02-09 08:41:28 -08:00
visionmedia 5cadbcbbd7 Start flash specs 2010-02-07 21:30:28 -08:00
visionmedia 8ee0294672 Started Flash support 2010-02-07 21:29:15 -08:00
visionmedia 99789c3182 Bump Aaron up as a contributor 2010-02-07 21:11:29 -08:00
visionmedia 406a7f4fc7 Merge branch 'integration' 2010-02-07 21:02:23 -08:00
visionmedia 5a11f82e0e Docs 2010-02-07 21:02:21 -08:00
Aaron Heckmann 33eca37ec9 added accepts support for media groups
Example: this.accepts('html') will now return true when Accepts header contains 'text/*'.
Support for */* was not added since it seems a bit too blunt in the real world.
2010-02-06 23:20:42 -05:00
Aaron Heckmann fbd9cdd11e updated accepts comments 2010-02-06 22:36:05 -05:00
Aaron Heckmann 6ec6657512 accepts now allows multiple args. fixes #117 2010-02-06 21:10:43 -05:00
visionmedia 8e91d2039a Started high level HTTP api 2010-02-05 15:16:48 -08:00
visionmedia 1879648be7 Merge branch 'plugin-halt' 2010-02-05 13:56:11 -08:00
visionmedia 490770171d Hooks only exporting before()/after() 2010-02-05 13:56:07 -08:00
visionmedia 621063cc18 Added support for plugins to halt. Closes #189 2010-02-05 13:54:50 -08:00
visionmedia 821defc11b Hook callbacks exported 2010-02-05 13:52:54 -08:00
visionmedia dbc1709e0e Added failing before() hook halt spec 2010-02-05 13:43:20 -08:00
visionmedia 4d1bda0601 Removed Route#run(). Closes #188 2010-02-05 13:39:52 -08:00
visionmedia 1a9a3674c2 Fixed broken specs due to use(Cookie) missing 2010-02-05 13:08:58 -08:00
visionmedia 99b7e74422 Added alternate layout support. Closes #119
BAM~! lol would have been sooner i just have been focusing
on larger things like sessions
2010-02-05 13:08:28 -08:00
visionmedia add0a43c40 Merge branch 'integration' 2010-02-05 09:04:14 -08:00
visionmedia 3dc7c6a254 Merge branch 'dev' of git://github.com/aheckmann/express into integration 2010-02-05 09:04:08 -08:00
visionmedia e645123fbd Release 0.2.1 2010-02-05 09:03:55 -08:00
Aaron Heckmann 4b104db212 Merge commit 'express/master' into integration 2010-02-05 11:50:12 -05:00
Aaron Heckmann e823e31550 remove unneeded require call 2010-02-05 11:10:01 -05:00
23 arquivos alterados com 311 adições e 42 exclusões
+30
Ver Arquivo
@@ -1,4 +1,34 @@
0.4.0 / 2010-02-11
==================
* Added flash() example to sample upload app
* Added high level restful http client module (express/http)
* Changed; RESTful route functions double as HTTP clients. Closes #69
* Changed; throwing error when routes are added at runtime
* Changed; defaulting render() context to the current Request. Closes #197
* Updated haml submodule
0.3.0 / 2010-02-11
==================
* Updated haml / sass submodules. Closes #200
* Added flash message support. Closes #64
* Added accepts() now allows multiple args. fixes #117
* Added support for plugins to halt. Closes #189
* Added alternate layout support. Closes #119
* Removed Route#run(). Closes #188
* Fixed broken specs due to use(Cookie) missing
0.2.1 / 2010-02-05
==================
* Added "plot" format option for Profiler (for gnuplot processing)
* Added request number to Profiler plugin
* Fixed binary encoding for multi-part file uploads, was previously defaulting to UTF8
* Fixed issue with routes not firing when not files are present. Closes #184
* Fixed process.Promise -> events.Promise
0.2.0 / 2010-02-03
==================
+1 -1
Ver Arquivo
@@ -115,9 +115,9 @@ Express is currently being developed with node --version:
## Contributors
* TJ Holowaychuk (visionmedia) <tj@vision-media.ca>
* Aaron Heckmann (aheckmann) <aaron.heckmann+github@gmail.com>
* Ciaran Jessup (ciaranj) <ciaranj@gmail.com>
* Gareth Jones (csausdev) <gareth.jones@sensis.com.au>
* Aaron Heckmann (aheckmann) <aaron.heckmann+github@gmail.com>
## License
+2
Ver Arquivo
@@ -15,6 +15,8 @@ configure(function(){
set('root', __dirname)
})
require('express/http')
var messages = [],
utils = require('express/utils')
+11 -2
Ver Arquivo
@@ -7,6 +7,9 @@ configure(function(){
use(MethodOverride)
use(ContentLength)
use(CommonLogger)
use(Cookie)
use(Session)
use(Flash)
set('root', __dirname)
})
@@ -15,12 +18,18 @@ get('/', function(){
})
get('/upload', function(){
this.render('upload.haml.html')
this.render('upload.haml.html', {
locals: {
flashes: this.flash('info')
}
})
})
post('/upload', function(){
var self = this
$(this.param('images')).each(function(image){
puts('uploaded ' + image.filename + ' to ' + image.tempfile)
puts(image.filename + ' -> ' + image.tempfile)
self.flash('info', 'Uploaded ' + image.filename)
})
this.redirect('/upload')
})
+7 -1
Ver Arquivo
@@ -5,4 +5,10 @@
%script{ src: '/public/javascripts/app.js' }
%link{ rel: 'stylesheet', href: '/style.css' }
%body
#wrapper= body
#wrapper
%h1 Upload
:if flashes
%ul.messages.info
:each msg in flashes
%li= msg
.body= body
+10 -1
Ver Arquivo
@@ -56,4 +56,13 @@ form
.panel
:float left
:width 100%
:margin-bottom 15px
:margin-bottom 15px
.messages
:margin 0
:padding 0
:border 1px solid #eee
=box-shadow 2px 2px 5px #eee
li
:padding 5px 10px
:list-style none
-1
Ver Arquivo
@@ -1,4 +1,3 @@
%h1 Upload
:if typeof images !== 'undefined'
.images
:each img in images
-1
Ver Arquivo
@@ -1,5 +1,4 @@
var path = require('path')
require.paths.unshift(__dirname + '/support/js-oo/lib')
require.paths.unshift(__dirname + '/support/ejs/lib')
require.paths.unshift(__dirname + '/support/haml/lib')
+4 -15
Ver Arquivo
@@ -44,19 +44,6 @@ Route = Class({
this.fn = fn
},
/**
* Execute this route's #fn with _args_,
* against _context_ or GLOBAL.
*
* @param {array} args
* @return {mixed}
* @api private
*/
run: function(args, context) {
return this.fn.apply(context || GLOBAL, args)
},
/**
* Normalize _path_. When a RegExp it is simply returned,
* otherwise a string is converted to a regular expression
@@ -122,7 +109,7 @@ Router = Class({
route: function(){
var route = this.matchingRoute()
if (route)
return route.run(this.request.captures.slice(1), this.request)
return route.fn.apply(this.request, this.request.captures.slice(1))
else if (this.request.accepts('html') && set('helpful 404'))
this.request.halt(404, require('express/pages/not-found').render(this.request))
else
@@ -214,6 +201,7 @@ Server = Class({
run: function(port, host, backlog){
var self = this
this.running = true
if (host !== undefined) this.host = host
if (port !== undefined) this.port = port
if (backlog !== undefined) this.backlog = backlog
@@ -282,6 +270,7 @@ Server = Class({
route: function(request, response){
request = new Request(request, response)
request.trigger('request')
if (request.response.finished) return
try {
if (typeof (body = (new Router(request)).route()) == 'string')
request.halt(200, body)
@@ -302,7 +291,7 @@ Server = Class({
// --- Express
Express = {
version: '0.2.0',
version: '0.4.0',
config: [],
routes: [],
plugins: [],
+12 -1
Ver Arquivo
@@ -1,6 +1,12 @@
// Express - DSL - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var http = require('express/http')
/**
* Return a routing function for _method_.
*
@@ -13,7 +19,12 @@ function route(method) {
return function(path, options, fn){
if (options instanceof Function)
fn = options, options = {}
Express.routes.push(new Route(method, path, fn, options))
if (path.indexOf('http://') === 0)
return http[method].apply(this, arguments)
else if (!Express.server.running)
Express.routes.push(new Route(method, path, fn, options))
else
throw new Error('cannot create route ' + method.toUpperCase() + " `" + path + "' at runtime")
}
}
+79
Ver Arquivo
@@ -0,0 +1,79 @@
// Express - HTTP - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var http = require('http'),
events = require('events'),
parse = require('url').parse,
queryString = require('querystring')
/**
* Request using the given _method_, _url_
* followed by optional _headers_, and _data_.
*
* @param {string} method
* @param {string} url
* @param {hash} headers
* @param {hash} data
* @param {Promise} promise
* @return {Promise}
* @api private
*/
function request(method, url, headers, data, promise) {
var buf = '',
promise = promise || new events.Promise,
url = parse(url),
path = url.pathname || '/',
search = url.search || '',
hash = url.hash || '',
port = url.port || 80,
headers = process.mixin(headers, { host: url.hostname }),
client = http.createClient(port, url.hostname)
client.addListener('error', function(e){
promise.emitError(new Error("client failed to " + method + " `" + url.href + "'"))
})
if (data) {
data = queryString.stringify(data)
headers['content-length'] = data.length
headers['content-type'] = 'application/x-www-form-urlencoded'
}
var request = client.request(method, path + search + hash, headers)
if (data) request.sendBody(data)
request.finish(function(response){
if (response.statusCode < 200 || response.statusCode >= 400)
promise.emitError(new Error('request failed with status ' + response.statusCode + ' "' + http.STATUS_CODES[response.statusCode] + '"'))
else if (response.statusCode >= 300 && response.statusCode < 400)
request(method, response.headers.location, headers, data, promise)
else {
response.setBodyEncoding('utf8')
response
.addListener('body', function(chunk){ buf += chunk })
.addListener('complete', function(){ promise.emitSuccess(buf, response) })
}
})
return promise
}
/**
* Return HTTP Client function for the given _method_,
* which optionally may _allowData_ to be passed.
*
* @param {string} method
* @param {bool} allowData
* @return {function}
* @api private
*/
function client(method, allowData) {
return function(url, headers, data) {
if (allowData) data = data || {}
return request(method.toUpperCase(), url, headers, data)
}
}
// --- Public API
exports.get = exports.view = client('get')
exports.post = exports.create = client('post', true)
exports.put = exports.update = client('put', true)
exports.del = exports.destroy = client('delete', true)
+1
Ver Arquivo
@@ -2,6 +2,7 @@
// Express - Plugins - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
process.mixin(require('express/plugins/hooks'))
process.mixin(require('express/plugins/flash'))
process.mixin(require('express/plugins/cache'))
process.mixin(require('express/plugins/cookie'))
process.mixin(require('express/plugins/session'))
+51
Ver Arquivo
@@ -0,0 +1,51 @@
// Express - Flash - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
exports.Flash = Plugin.extend({
extend: {
/**
* Initialize extensions.
*/
init: function(){
Request.include({
/**
* Get / set flash _key_ and _val_.
*
* When a flash _key_ and _val_ are present,
* it will persist in the session until outputted.
* The _val_ pushed is returned.
*
* Example:
*
* this.flash('info', 'email sent')
* this.flash('info', 'email received')
* this.flash('info')
* // => ['email sent', 'email received']
*
* this.flash('info')
* // => null
*
* @param {string} key
* @param {string} val
* @return {string}
* @api public
*/
flash: function(key, val) {
if (!this.session.flash) this.session.flash = {}
if (!(key in this.session.flash)) this.session.flash[key] = []
if (val)
return this.session.flash[key].push(val), val
else if (key) {
var vals = this.session.flash[key]
delete this.session.flash[key]
if (vals.length) return vals
}
}
})
}
}
})
+7 -7
Ver Arquivo
@@ -1,8 +1,7 @@
// Express - Hooks - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var before = [],
after = []
exports.callbacks = { before: [], after: [] }
/**
* Add a _fn_ to be excuted before a request.
@@ -12,7 +11,7 @@ var before = [],
*/
exports.before = function(fn) {
before.push(fn)
exports.callbacks.before.push(fn)
}
/**
@@ -23,7 +22,7 @@ exports.before = function(fn) {
*/
exports.after = function(fn) {
after.push(fn)
exports.callbacks.after.push(fn)
}
// --- Hooks
@@ -36,7 +35,8 @@ exports.Hooks = Plugin.extend({
*/
init: function() {
process.mixin(GLOBAL, exports)
process.mixin(GLOBAL, { before: exports.before,
after: exports.after })
}
},
@@ -49,7 +49,7 @@ exports.Hooks = Plugin.extend({
*/
request: function(event) {
$(before).each(function(fn){
$(exports.callbacks.before).each(function(fn){
fn.call(event.request, event.request)
})
},
@@ -59,7 +59,7 @@ exports.Hooks = Plugin.extend({
*/
response: function(event) {
$(after).each(function(fn){
$(exports.callbacks.after).each(function(fn){
fn.call(event.request, event.request)
})
}
+6 -4
Ver Arquivo
@@ -51,9 +51,10 @@ exports.View = Plugin.extend({
*
* Options:
*
* - layout: Whether or not to use a layout. Defaults to true
* - context: Most engines support an evaluation context (the 'this' keyword)
* - layout: The layout to use, none when falsey. Defaults to 'layout'
* - locals: Most engines support a hash of local variable names / values.
* - context: Most engines support an evaluation context (the 'this' keyword).
* Defaults to the current Request instance.
*
* @param {string} view
* @param {hash} options
@@ -67,12 +68,13 @@ exports.View = Plugin.extend({
path = set('views') + '/' + view,
type = path.split('.').slice(-2)[0],
ext = utils.extname(path),
layout = options.layout === undefined ? true : options.layout
layout = options.layout === undefined ? 'layout' : options.layout
options.context = options.context || this
self.contentType(ext)
function render(content) {
content = engine[type].render(content, options)
if (layout)
self.render('layout.' + type + '.' + ext, process.mixin(true, options, {
self.render(layout + '.' + type + '.' + ext, process.mixin(true, options, {
layout: false,
locals: {
body: content
+17 -5
Ver Arquivo
@@ -113,19 +113,31 @@ exports.Request = Class({
/**
* Check if Accept header includes the mime type
* for the given _path_, which calls mime.type().
* for any of the given paths, which calls mime.type().
*
* When no Accept header is present true will be
* returned as stated in the HTTP specification.
*
* @param {string} path
* Example:
*
* this.accepts('png')
* this.accepts('png', 'jpg', 'gif')
* this.accepts('image.png')
* this.accepts('path/to/image.png')
*
* @param {mixed} ...
* @return {bool}
* @api public
*/
accepts: function(path) {
return this.header('accept') ?
this.header('accept').indexOf(mime.type(path)) !== -1 :
accepts: function() {
var accept = this.header('accept')
return accept ?
$(arguments).any(function(path){
var type = mime.type(path)
return accept.indexOf(type) !== -1 ||
accept.indexOf(type.split('/')[0]+'/*') !== -1
}) :
true
},
+1
Ver Arquivo
@@ -43,6 +43,7 @@ specs = {
'plugins.hooks',
'plugins.cookie',
'plugins.session',
'plugins.flash',
],
dependant: [
'element-collection'
+33
Ver Arquivo
@@ -0,0 +1,33 @@
describe 'Express'
before_each
reset()
use(require('express/plugins/cookie').Cookie)
use(require('express/plugins/session').Session)
use(require('express/plugins/flash').Flash)
Session.store.clear()
end
describe 'Flash'
describe 'flash()'
it 'should push a flash message'
var headers = { headers: { cookie: 'sid=123' }}
post('/', function(){ return this.flash('info', 'email sent') })
get('/', function(){ return this.flash('info', 'email received') })
get('/info', function(){ return this.flash('info').join(', ') })
get('/messages', function(){ return this.flash('info') || 'empty' })
post('/', headers).body.should.eql 'email sent'
get('/', headers).body.should.eql 'email received'
get('/info', headers).body.should.eql 'email sent, email received'
get('/messages').body.should.eql 'empty'
// TODO: seperate once segfault is fixed...
end
it 'should return the message pushed'
get('/', function(){ return this.flash('info', 'email sent') })
get('/').body.should.eql 'email sent'
end
end
end
end
+13 -1
Ver Arquivo
@@ -2,7 +2,10 @@
describe 'Express'
before_each
reset()
use(require('express/plugins/hooks').Hooks)
hooks = require('express/plugins/hooks')
use(hooks.Hooks)
hooks.callbacks.before = []
hooks.callbacks.after = []
end
describe 'Hooks'
@@ -17,6 +20,15 @@ describe 'Express'
get('/user', function(){})
get('/user').body.should.eql 'foobar'
end
it 'should be able to halt the request'
GLOBAL.before(function(){
this.halt(404, 'woo!')
})
get('/user', function(){ return 'fail' })
get('/user').status.should.eql 404
get('/user').body.should.eql 'woo!'
end
end
describe 'after()'
+1
Ver Arquivo
@@ -2,6 +2,7 @@
describe 'Express'
before_each
reset()
use(require('express/plugins/cookie').Cookie)
use(Session = require('express/plugins/session').Session)
Session.store.clear()
end
+23
Ver Arquivo
@@ -58,6 +58,29 @@ describe 'Express'
get('/user', { headers: { accept: null }}).body.should.eql 'true'
end
end
describe 'should allow multiple arguments'
it 'should return true if any mime type is present'
get('/user', function(){ return this.accepts('jpeg', 'png').toString() })
get('/user', { headers: { accept: 'image/gif,image/png' }}).body.should.eql 'true'
end
it 'should return false if none of the mime types are present'
get('/user', function(){ return this.accepts('jpeg', 'png').toString() })
get('/user', { headers: { accept: 'text/plain,text/html' }}).body.should.eql 'false'
end
end
describe 'when a media type range was sent'
it 'should return true if the group media type matches'
get('/user', function(){ return this.accepts('html').toString() })
get('/user', { headers: { accept: 'text/plain,text/*' }}).body.should.eql 'true'
end
it 'should return false if the group media type does not match'
get('/user', function(){ return this.accepts('ogg').toString() })
get('/user', { headers: { accept: 'text/plain,text/*' }}).body.should.eql 'false'
end
end
end
describe '#halt()'