Comparar commits

...

349 Commits

Autor SHA1 Mensagem Data
visionmedia d0a8bb550e Release 0.7.2 2010-03-16 08:08:17 -07:00
visionmedia f96f1423e1 Fixed GIT submodules (HAH!) 2010-03-16 08:06:26 -07:00
visionmedia 7bf17f2f61 Removed old support dir 2010-03-16 08:03:57 -07:00
visionmedia 670b6cfc15 Merge branch 'remove-kiwi' 2010-03-16 08:03:33 -07:00
visionmedia b6d2c8479c Release 0.7.1 2010-03-16 08:03:06 -07:00
visionmedia 5aaa114271 Docs 2010-03-16 08:01:51 -07:00
visionmedia e99c2791bb Docs 2010-03-16 07:58:06 -07:00
visionmedia 656d7754cd Express now using submodules again until a PM is adopted 2010-03-16 07:54:20 -07:00
visionmedia 4aaf10fbfc Moved support 2010-03-16 07:39:56 -07:00
visionmedia 85e77b77aa Added submodules 2010-03-16 07:02:49 -07:00
visionmedia f23ef09247 Chat example using millisecond conversions from ext 2010-03-15 16:00:39 -07:00
visionmedia 440d956438 Refactored Session#startReaper() with ext millisecond conversions 2010-03-15 15:59:37 -07:00
visionmedia acd2852cf3 ext >= 0.2.4 2010-03-15 15:58:02 -07:00
visionmedia 4246f43bdf ext >= 0.2.3 2010-03-15 09:57:38 -07:00
visionmedia 7d33769cd2 Revert "Removed sass / haml "dependencies""
This reverts commit 31fdba80d4.
2010-03-15 09:43:29 -07:00
visionmedia 31fdba80d4 Removed sass / haml "dependencies" 2010-03-15 09:42:15 -07:00
visionmedia 77f8e460d0 Better example app 2010-03-15 09:31:11 -07:00
visionmedia 51e51db9f7 Docs 2010-03-15 09:16:13 -07:00
visionmedia 73c506f19c Release 0.7.0 2010-03-15 09:13:18 -07:00
visionmedia 79143f3334 Chat sample app using pass() 2010-03-15 09:10:50 -07:00
visionmedia 128ba9040e Refactored Router#matchingRoute() 2010-03-15 09:09:51 -07:00
visionmedia 300cfe74ad Request#pass() accepts a pathname string 2010-03-15 09:02:44 -07:00
visionmedia f008af05bd Merge branch 'pull' 2010-03-15 08:54:57 -07:00
Aaron Heckmann 3aa870d6bd return this in pass 2010-03-15 08:50:35 -07:00
Aaron Heckmann 25e1a8c001 add pass specs 2010-03-15 08:50:26 -07:00
Aaron Heckmann 0ba3b114b0 add pass support 2010-03-15 08:50:20 -07:00
visionmedia c429e88e8e Updated --version of node tested against in readme 2010-03-15 08:42:22 -07:00
Aaron Heckmann c7a2fe8440 remove profiler from plugins.js 2010-03-15 08:41:21 -07:00
Aaron Heckmann de237e760b remove Profiler 2010-03-15 08:41:03 -07:00
visionmedia 927f5c9883 Logger copyright 2010-03-12 07:36:50 -08:00
visionmedia 88f461baf2 Logger using Date#format() 2010-03-12 07:14:57 -08:00
visionmedia 3251ae26a0 Fixed plot format 2010-03-12 07:09:56 -08:00
visionmedia e84c81633e "combined" using printf() 2010-03-12 07:09:20 -08:00
visionmedia a2ec966ac7 "common" Logger format using printf() 2010-03-12 07:06:47 -08:00
visionmedia 69660fbfda Merge branch 'integration' 2010-03-12 06:58:34 -08:00
visionmedia dcedca1a80 Removed 'sinatra' logger, added duration to 'common' log format 2010-03-12 06:58:23 -08:00
visionmedia 6455e954fc Removed CommonLogger, use Logger 2010-03-12 06:53:17 -08:00
visionmedia 646904688f No need for utils.mixin() here 2010-03-11 11:16:48 -08:00
visionmedia 62779fc972 Doc typo 2010-03-11 11:14:33 -08:00
visionmedia fdee4cde26 Sample app in readme 2010-03-11 11:14:02 -08:00
visionmedia 0c18ac5adc Added kiwi installation docs 2010-03-11 11:05:41 -08:00
visionmedia 6da4a942ca Managing dependencies with the kiwi package manager. Closes #228
Merge branch 'kiwi'
2010-03-11 11:01:54 -08:00
visionmedia 565f68d2d7 Added kiwi.seed() calls to make them available via require()
This is needed since the view plugin uses
the regular require() and not kiwi.require()
2010-03-11 11:00:47 -08:00
visionmedia a7cee4c889 Removed references to submodules 2010-03-11 10:57:26 -08:00
visionmedia 4f315f9b11 Added haml / sass dependencies 2010-03-11 10:46:08 -08:00
visionmedia 51febfec2d Actually added seed.yml :) 2010-03-10 19:23:56 -08:00
visionmedia df8bd96b2e Added support for arbitrary view engines 2010-03-10 19:23:56 -08:00
visionmedia 9da9beb342 Added seed.yml for kiwi package management support 2010-03-10 19:23:56 -08:00
visionmedia f93af823df Fixed typo 2010-03-10 19:23:51 -08:00
Aaron Heckmann 5e74723a92 add logger 2010-03-10 22:09:07 -05:00
visionmedia ec77e1acea HTTP client appends query string when method is GET. Closes #205 2010-03-10 19:01:40 -08:00
visionmedia 1afad64972 Misc refactoring to http client 2010-03-10 18:53:12 -08:00
visionmedia a3365dda07 Removed usage of RESTful route funcs as http client 2010-03-10 18:49:04 -08:00
visionmedia 4c246a4cd1 Merge branch 'async-plugins' 2010-03-10 18:34:09 -08:00
visionmedia 934ffd0731 Fixed params in show-exception page 2010-03-10 18:32:25 -08:00
visionmedia 790e2c233d Inverted plugin async support. Return true WHEN async 2010-03-10 17:06:46 -08:00
visionmedia 9e9967381c Misc refactoring 2010-03-10 17:01:40 -08:00
visionmedia 891ed08827 Request#trigger() now supports callback as 2nd or 3rd arg 2010-03-10 17:01:08 -08:00
visionmedia 55d13a6f08 Doc typo 2010-03-10 16:58:53 -08:00
visionmedia 5569ea4397 Removed custom exceptions
because they are lame
2010-03-10 16:57:51 -08:00
visionmedia 693e37459a Removed InvalidResponseBody / InvalidStatusCode
stupid anyway, just dont fuck shit up :)
2010-03-10 16:46:47 -08:00
visionmedia 90de1fa55d Refactoring routing slightly 2010-03-10 16:34:21 -08:00
visionmedia 6d6e1557ce Refactored Request#trigger() 2010-03-10 16:26:20 -08:00
visionmedia 2d84f16dc0 Hooks do not support async at the moment anyway, return true 2010-03-10 16:14:54 -08:00
visionmedia 2f98ef9f6d Strict comparisons 2010-03-10 16:12:16 -08:00
visionmedia e0c07d2385 Fixed typo 2010-03-10 16:11:38 -08:00
ciaranj 0b2413d8c0 Missed a curly brace out 2010-03-10 23:52:31 +00:00
ciaranj e92b01f813 Change the approach to be truth based
Now just enforces that a plugin returns true, if it doesn't return true then
we assume the plugin is taking care of making sure that the plugin chain continues.

A sensible enhancement to this might be to improve the request.trigger function to timeout
after a fixed period, log a warning, then carry on as normal ??

I'm not too sure of what 'Hooks.js' does so I may haveconfigured that incorrectly, just FYI
2010-03-10 23:49:38 +00:00
ciaranj 27ff13459f Merge branch 'master' of git://github.com/visionmedia/express into async_plugins
Conflicts:
	lib/express/plugins/cookie.js
	lib/express/plugins/hooks.js
	lib/express/request.js
2010-03-10 23:36:18 +00:00
ciaranj 3e80915454 Oops, still had some proto code in there 2010-03-10 23:23:37 +00:00
ciaranj 4063d2e2c4 Initial stab at async plugin execution 2010-03-10 23:16:31 +00:00
visionmedia ce9416857b Renamed params -> keys now that ext.js is fixed 2010-03-10 13:39:49 -08:00
visionmedia 575d5e8e57 Updated ext.js. Closes #227 2010-03-10 13:38:07 -08:00
visionmedia 14ceb8c046 Release 0.5.0 2010-03-10 13:17:57 -08:00
visionmedia c6c29ca505 Styling 2010-03-10 13:07:04 -08:00
visionmedia c551226504 More refactoring 2010-03-10 13:06:10 -08:00
visionmedia fcbc09543c More refactoring 2010-03-10 13:04:12 -08:00
visionmedia 6d83bb0026 Misc refactoring 2010-03-10 13:00:43 -08:00
visionmedia 0784a513c9 configure() using each() 2010-03-10 12:48:48 -08:00
visionmedia ebcdec0860 Misc refactoring 2010-03-10 12:43:40 -08:00
visionmedia 6805e4f28f Kinda updated feature list a bit 2010-03-10 12:30:50 -08:00
visionmedia 962f5fa412 Removed extname() / basename() utils. Using path module 2010-03-10 12:27:49 -08:00
visionmedia 1385988ab8 Better escape() util 2010-03-10 12:24:08 -08:00
visionmedia 6cc80f8c54 Removed toArray() util. use arguments.values 2010-03-10 12:23:45 -08:00
visionmedia d6c843962d Removed escapeRegexp() util 2010-03-10 12:22:25 -08:00
visionmedia 247e6ad9ab Removed process.mixin() dependency 2010-03-10 12:17:10 -08:00
visionmedia 364c131a4e Merge branch 'ext' 2010-03-10 12:04:38 -08:00
visionmedia b047325033 Updated ext and removed Collection 2010-03-10 12:03:26 -08:00
visionmedia 0894cfe058 Removed more Collection usage 2010-03-10 11:21:29 -08:00
visionmedia f4a9d7e70b Removed more Collection usage 2010-03-10 11:18:14 -08:00
visionmedia 593634fb5d Started removing usage of Collection 2010-03-10 11:16:26 -08:00
visionmedia 2932bf5006 make test / make init distinction 2010-03-10 11:12:12 -08:00
visionmedia 4b1c39fe41 Removed Collection specs 2010-03-10 11:10:12 -08:00
visionmedia ee91e3a139 Started removal of Collection 2010-03-10 11:08:20 -08:00
visionmedia e287b854a2 Added ext.js submodule 2010-03-09 08:31:17 -08:00
visionmedia 5458e5dd00 Misc refactoring 2010-03-09 08:07:55 -08:00
visionmedia ed30f37ca9 Fixing some comments 2010-03-09 08:02:44 -08:00
visionmedia 9e495f6bb8 Misc refactoring to multi-part handling 2010-03-09 07:57:57 -08:00
Aaron Heckmann 21566c49f0 update node version in readme
push the stream.part bugfix from previous commit
2010-03-09 07:54:50 -08:00
Aaron Heckmann 0a225c13a4 refactor run callback
add Server.error
fix bug in run callback (accepts not defined)
fix bug in stream.body event (part is undefined)
2010-03-09 07:54:46 -08:00
Aaron Heckmann 680c07a030 ensure uploaded files close before routing 2010-03-09 07:54:41 -08:00
visionmedia 8ee66dca22 Updated HTTP client. Closes #221
Merge branch 'update-http-client'
2010-03-08 18:43:38 -08:00
visionmedia 60393f07ef Removed debugging data 2010-03-08 18:43:05 -08:00
visionmedia 34418b03fb HTTP client is resolving callback function amoung other args 2010-03-08 18:42:28 -08:00
visionmedia ce2f161f08 Implemented max redirects option for http client. Closes #204 2010-03-08 18:33:23 -08:00
visionmedia d5ca1ea152 Started fixing HTTP client 2010-03-08 18:08:09 -08:00
visionmedia c3fec8225a Removed http client allowData 2010-03-08 17:51:32 -08:00
visionmedia bfff00826e Styling 2010-03-08 17:11:16 -08:00
visionmedia 61cb50b2bd Styling 2010-03-08 17:09:36 -08:00
visionmedia c8d3bfabc7 Styling 2010-03-08 17:01:35 -08:00
visionmedia 1ce212d33d Styling 2010-03-08 17:00:56 -08:00
visionmedia 3682f4f06a Styling 2010-03-08 16:57:49 -08:00
visionmedia 0f74408b0e Styling 2010-03-08 16:56:31 -08:00
visionmedia f7d7c4a8e0 Merge branch 'node-0.1.30' 2010-03-08 16:42:41 -08:00
visionmedia 0cb8d12796 Removed ElementCollection support
this will become another library
2010-03-08 16:40:23 -08:00
ciaranj 9586ecfd58 Multipart form-data / File uploading now works again
Haven't re-implemented a timeout, wasn't sure what behaviour was wanted here...should
be easy enough to do, wish I could get local tests working again :(
2010-03-09 00:13:00 +00:00
ciaranj 86217867f9 Now working on node 0.1.30 but using custom mixin 2010-03-08 22:00:09 +00:00
Aaron Heckmann 496de51a11 Common log client address instead of server address 2010-03-05 07:38:08 -08:00
visionmedia 38591d06a0 Deep copy request 2010-03-05 07:32:57 -08:00
visionmedia 91805f7da4 Shameless self promotion of ebook 2010-02-22 14:48:43 -08:00
visionmedia 7bd8340a8b Fixed Collection#clone() 2010-02-22 13:30:46 -08:00
visionmedia 4817007097 Re-throwing errors 2010-02-22 13:26:35 -08:00
visionmedia d5003feb39 Fixed mocks to match node api 2010-02-22 08:08:51 -08:00
visionmedia f03e460ca5 Started converting fs module 2010-02-22 08:05:13 -08:00
visionmedia 347c8847b0 Removed addErrback() from render()
throws with latest node
2010-02-12 10:36:23 -08:00
visionmedia 9f48b32329 Benchmarks link 2010-02-12 09:34:40 -08:00
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
visionmedia 7cdbca0dc9 We cannot use(Cookie) within Session.init()
This is dueue to Session being above Cookie in the
plugins stack, hence missing when the cookie data
gets populated.

With some re-working this can change, however
it is not very important at the time. We can
just use(Cookie) before Session.
2010-02-05 08:18:24 -08:00
visionmedia 7d5f06b048 Better use(Cookie) 2010-02-05 08:12:51 -08:00
Aaron Heckmann e823e31550 remove unneeded require call 2010-02-05 11:10:01 -05:00
visionmedia 34de3ede95 Typo 2010-02-05 08:04:08 -08:00
visionmedia 35c7317004 Binary encoding for multi-part file uploads 2010-02-04 15:19:31 -08:00
visionmedia 2a89f375f4 Merge branch 'upload' 2010-02-04 13:54:16 -08:00
visionmedia 8d06b6752a Fixed issue with routes not firing when not files are present. Closes #184 2010-02-04 13:54:11 -08:00
visionmedia dba453345a process.Promise -> events.Promise 2010-02-04 13:36:38 -08:00
visionmedia 1f76d5c7d6 Auto use(Cookie) when use(Session). Closes #183 2010-02-04 13:23:03 -08:00
visionmedia 8309537527 CommonLogger back in chat app 2010-02-04 09:47:01 -08:00
visionmedia d0f14f8488 Added "plot" format option for Profiler
This can be used to generate gnuplot graphs etc
2010-02-04 09:33:18 -08:00
visionmedia cee857af9a Added request number to Profiler plugin 2010-02-03 19:41:35 -08:00
visionmedia c8dd169ad9 Release 0.2.0 2010-02-03 19:28:31 -08:00
visionmedia 5495fbcd0c Merge branch 'upload' 2010-02-03 19:26:34 -08:00
visionmedia eb94667ec8 Refactored mergeParam() 2010-02-03 19:26:09 -08:00
visionmedia cf31355515 Added parseParam() support for name[] etc. Closes #180
PS. this is awesome! because now with WebKit (and opera?) you can have
<input type="file" name="images[]" multiple />
2010-02-03 19:24:33 -08:00
visionmedia 604c359a1c Fixed another spec 2010-02-03 18:45:39 -08:00
visionmedia dad64b8a3b Fixed specs 2010-02-03 18:45:08 -08:00
visionmedia ffa432baa2 More mergeParam() specs 2010-02-03 18:42:41 -08:00
visionmedia 5c4a356348 Added more specs 2010-02-03 18:23:31 -08:00
visionmedia ef949aec20 Specs for key[] with uploads 2010-02-03 18:20:59 -08:00
visionmedia eaea3f188d Added more mergeParam() specs 2010-02-03 18:14:25 -08:00
visionmedia 818135789b Added multiple file input for testing 2010-02-03 18:08:43 -08:00
visionmedia 2bbe573748 Removed some settings from chat app.
These can be used via EXPRESS_ENV=production
2010-02-03 17:38:55 -08:00
visionmedia 9efb15b267 Both Cache and Session option "reapInterval" may be "reapEvery". Closes #174 2010-02-03 16:59:00 -08:00
visionmedia 5ce1306b75 Merge branch 'cache-lifetime' 2010-02-03 16:57:24 -08:00
visionmedia 75c85663ad use() of the same plugin several time will always use latest options. Closes #176 2010-02-03 16:57:19 -08:00
visionmedia 8c1bcc4c47 Added expiration support to cache api with reaper. Closes #133 2010-02-03 16:27:25 -08:00
visionmedia 200d09c7bd cache Memory.Store#get() utilizing Collection 2010-02-03 16:11:01 -08:00
visionmedia ef86474830 Added cache Store.Memory#reap() 2010-02-03 16:01:25 -08:00
visionmedia e2fee9b353 Cache api using Cache instances 2010-02-03 15:57:59 -08:00
visionmedia 542cab1123 Renamed MemoryStore -> Store.Memory 2010-02-03 15:43:29 -08:00
visionmedia 315c05034f Merge branch 'abstract-session' 2010-02-03 15:33:35 -08:00
visionmedia 148d34629b Added abstract session Store. Closes #172 2010-02-03 15:33:29 -08:00
visionmedia 82891ea148 Release 0.1.0 2010-02-03 15:27:58 -08:00
visionmedia cd1aa92d07 Docs 2010-02-03 15:20:54 -08:00
visionmedia 2a4f3525d9 mime() is no longer global. Renamed to exports.type() 2010-02-03 15:16:29 -08:00
visionmedia 944f99abe0 Added online user count to chat app 2010-02-03 15:10:44 -08:00
visionmedia e083321a9d Merge branch 'session-count' 2010-02-03 15:05:07 -08:00
visionmedia a0a1a543e8 Refactored MemoryStore#reap() with Collection 2010-02-03 15:04:52 -08:00
visionmedia cf74bc655d Added MemoryStore#length() 2010-02-03 15:03:43 -08:00
visionmedia 0587278cda Consistant docs 2010-02-03 14:58:57 -08:00
visionmedia b333dccc2e Hooks (before / after) pass request as arg
To prevent confusion with the value of "this", either will work
2010-02-03 14:52:45 -08:00
visionmedia a71201bd55 Session plugin using utils.uid() 2010-02-03 14:30:04 -08:00
visionmedia cec677062b Merge 2010-02-03 14:26:00 -08:00
visionmedia 327d5b0f88 Readme 2010-02-03 14:20:46 -08:00
visionmedia 20cf8f81a2 Session options in chat sample app 2010-02-03 14:18:40 -08:00
visionmedia 4d5a1b5f4d Fixed sessions when testing 2010-02-03 14:14:31 -08:00
visionmedia 9d5a6f9412 Session docs 2010-02-03 13:51:07 -08:00
visionmedia 7e92012415 Helpers are no longer global. Renamed to utils 2010-02-03 13:14:34 -08:00
visionmedia 6ee83ba8d0 Upgraded node support. Closes #169
Merge branch 'upgrade'
2010-02-03 13:05:42 -08:00
visionmedia 70a99ade67 Removed dirname() helper 2010-02-03 13:03:05 -08:00
visionmedia 634503b8ba Utilizing __dirname 2010-02-03 13:02:32 -08:00
visionmedia 2a4cbb1174 Misc refactoring 2010-02-03 11:45:50 -08:00
visionmedia ca7d0f3e34 Upgraded JSpec 2010-02-03 11:40:33 -08:00
visionmedia 9cb2645d1c Merge branch 'fix-upload' 2010-02-03 11:35:31 -08:00
visionmedia acf0a652b7 Added "upload timeout" setting defaulting to 5 seconds 2010-02-03 11:35:23 -08:00
visionmedia 2701dfd80b Fixed incomplete file upload issue. Closes #167
Route now waits until all files have been written / closed
until executing
2010-02-03 11:24:17 -08:00
visionmedia 047499dcd1 More session work 2010-02-01 18:50:34 -08:00
visionmedia fa37e5a35f Added "session reap threshold" setting 2010-02-01 17:36:18 -08:00
visionmedia 4adbb4971f Added reaper() 2010-02-01 17:32:38 -08:00
visionmedia 8ed99f6bb8 Added session reaping 2010-02-01 17:29:23 -08:00
visionmedia fbf547d127 Added Session class 2010-02-01 17:05:16 -08:00
visionmedia f4b4c03cb5 Bulk of memory store based session support complete. 2010-02-01 16:49:47 -08:00
visionmedia 6b9a079bbf Added spec for mock sessions when testing 2010-02-01 15:49:51 -08:00
visionmedia 7238afbf4b Started Session plugin 2010-02-01 15:44:41 -08:00
visionmedia 0186659469 Fixed upload app submit button styling 2010-02-01 15:15:13 -08:00
visionmedia 50d14230f5 Merge branch 'uid' 2010-02-01 15:13:31 -08:00
visionmedia 29fe71c8f7 Implemented uid() for upload tempfiles. Closes #166 2010-02-01 15:13:27 -08:00
visionmedia 5fe033cef1 Added quick uid() helper 2010-02-01 15:10:44 -08:00
visionmedia 6490f0c193 Added aheckmann to contributor list 2010-02-01 13:25:49 -08:00
visionmedia 09ccd043f2 Throw Error 2010-01-29 07:49:11 -08:00
visionmedia 825e8b6f0c fix layout locals. Closes #163 2010-01-28 08:29:32 -08:00
visionmedia 1726dfb3b3 Sample upload app output 2010-01-26 09:01:46 -08:00
visionmedia 8a7ac0a6af Docs 2010-01-25 09:09:29 -08:00
visionmedia d66ce086d4 Merge branch 'integration' 2010-01-25 09:01:17 -08:00
visionmedia 2bbf6cb8f9 Misc refactoring 2010-01-25 09:01:09 -08:00
visionmedia 4a52e641cf Merge branch 'master' of git://github.com/aheckmann/express into integration 2010-01-25 08:59:59 -08:00
visionmedia b4c90def81 Merge branch 'integration' 2010-01-25 08:55:45 -08:00
visionmedia 50c0277e7f Merge branch 'master' of git://github.com/gjritter/express into integration 2010-01-25 08:55:37 -08:00
Greg Ritter 5a5f23fc88 Added some additional file extension to mime type mappings. 2010-01-22 21:24:27 -07:00
Aaron Heckmann ae8f9e1137 bug fix for static.send method. request.halt() didn't return appropriately
Signed-off-by: Aaron Heckmann <aaron.heckmann@gmacfs.com>
2010-01-22 21:13:18 -05:00
visionmedia 2b92e4053b Merge branch 'fix-cookies' 2010-01-22 11:58:34 -08:00
visionmedia 8717e2e970 Fixed cookie paths, defaulting to / now 2010-01-22 11:58:31 -08:00
visionmedia d0a9b0cb81 Updated libxmljs support. Closes #156
Merge branch 'libxmljs'
2010-01-22 10:55:37 -08:00
visionmedia 844eb5f7e9 Updated libxmljs submodule 2010-01-22 10:55:05 -08:00
visionmedia 41913fd8b9 Fixed two failures due to libxmljs update 2010-01-22 10:54:38 -08:00
visionmedia 2a5ccd8826 Updated libxmljs to v0.2.0 2010-01-22 10:52:57 -08:00
visionmedia 497402311f Merge branch 'master' of git://github.com/gjritter/express 2010-01-22 06:45:27 -08:00
Gregory Ritter d52090619b Added some additional file extension to mime type mappings. 2010-01-21 22:38:00 -07:00
visionmedia 9d0d6e412b Removed unnecessary require in chat app 2010-01-19 16:18:40 -08:00
visionmedia 9b5605a944 Updated haml submodule 2010-01-19 15:19:30 -08:00
ciaranj 51f4c965b5 Added 'binary' encoding for cached static file responses. 2010-01-19 21:33:40 +00:00
ciaranj 046bee8844 Merge branch 'master' of git://github.com/visionmedia/express 2010-01-18 20:48:56 +00:00
visionmedia 971739089a Using sass property expansion in example app 2010-01-18 11:26:30 -08:00
visionmedia f962f34e53 Removed sass file we dont need 2010-01-18 11:24:01 -08:00
visionmedia 34a88d029d Fixed query string spec
Failing due to number not being a string
2010-01-18 11:12:51 -08:00
visionmedia d3b2e6057d make all -> make test 2010-01-18 11:10:08 -08:00
visionmedia 12a58361b4 Misc 2010-01-18 11:06:25 -08:00
visionmedia 0085b0a33b Uploads dont become part of params unless they have length 2010-01-18 11:03:04 -08:00
visionmedia bf9505a4ce Fixed file upload 2010-01-18 11:01:44 -08:00
visionmedia 4c94457380 Fixed mergeParam() 2010-01-18 10:57:56 -08:00
visionmedia af313e5e8c Misc work on upload sample app 2010-01-18 10:52:47 -08:00
visionmedia eb7b96efe1 Added support for multi-part vals that are not files 2010-01-18 10:42:12 -08:00
visionmedia 5fe35bf97e Added multi-part upload support. Closes #155 2010-01-18 10:36:02 -08:00
visionmedia 12bc374ea9 Typo 2010-01-18 09:58:45 -08:00
visionmedia 68c57e08ae Started multi-part upload support 2010-01-18 09:54:50 -08:00
visionmedia f73c3fc404 Misc styling for sample app 2010-01-18 08:57:14 -08:00
visionmedia 4d926d3840 POST /upload 2010-01-18 08:20:44 -08:00
visionmedia 817ff41b0d Started upload example app 2010-01-18 08:14:17 -08:00
visionmedia f1614a5946 Moved chat app to examples/chat 2010-01-18 08:05:14 -08:00
visionmedia 17c1207d76 Added style.sass 2010-01-14 19:33:08 -08:00
visionmedia a92667fdad Bigger docs for benchmarks 2010-01-12 14:23:47 -08:00
visionmedia f03694e21e Added sass to the benchmarks 2010-01-12 14:20:36 -08:00
visionmedia 83fcf8b594 Better require paths
to pave the way for having a node package manager resolve dependencies
2010-01-12 14:15:18 -08:00
visionmedia 7afb895f1b Merge branch 'sass' 2010-01-12 14:07:02 -08:00
visionmedia 98e566ad69 Finished sass support. Closes #144
Check out my sass.js repo for more information
2010-01-12 14:06:58 -08:00
visionmedia 7d0470d285 Converted sample chat app css to sass 2010-01-12 14:06:04 -08:00
visionmedia f8879ac5d1 Started converting to sass 2010-01-12 13:58:13 -08:00
visionmedia 9f263e357b Added Sass.js submodule 2010-01-12 13:36:24 -08:00
ciaranj ae33e7b673 Merge branch 'master' of git://github.com/visionmedia/express
Conflicts:
	lib/express/static.js
2010-01-12 20:56:13 +00:00
visionmedia b766234b93 Fixed readme example 2010-01-11 18:16:32 -08:00
visionmedia a2899a0acd Misc styling 2010-01-11 17:06:57 -08:00
visionmedia 43da20a6b3 Misc refactoring 2010-01-11 16:59:06 -08:00
visionmedia 25cf42250a Refactored view contents caching 2010-01-11 16:55:53 -08:00
visionmedia 6b6ec3f19f Added link support to sample chat app 2010-01-11 16:35:22 -08:00
visionmedia 1a2e4342e2 production env caching view contents and static files 2010-01-11 16:25:23 -08:00
visionmedia 1ce1abf216 Static file caching. Closes #136
Boosts performance of static file serving roughly %45
2010-01-11 16:21:55 -08:00
ciaranj 704c933927 Merge branch 'master' of git://github.com/visionmedia/express 2010-01-11 22:11:35 +00:00
visionmedia 8207e7088f Memory cache now allows objects as values 2010-01-11 09:39:16 -08:00
visionmedia 46fdf10c97 "cache views" -> "cache view contents"
This will make way for "cache views" to be the canonical way
to cache view I/O contents AND their rendering
2010-01-11 09:23:29 -08:00
visionmedia 61b82480b0 Added cache plugin to plugins.js 2010-01-11 09:16:44 -08:00
visionmedia 4ce5c7650e Finished memory data-store for cache api. Closes #130
expiration etc coming soon.
2010-01-11 09:09:16 -08:00
visionmedia 965cbf63be Added normalize() 2010-01-11 08:55:56 -08:00
visionmedia d4d76cda58 Started implemented cache methods 2010-01-11 08:49:12 -08:00
visionmedia f2d08aa2e3 Added memory store specs 2010-01-11 08:29:56 -08:00
visionmedia a82b1b9270 Started Store.Memory specs 2010-01-11 08:17:32 -08:00
visionmedia c1617508c6 Started caching api 2010-01-11 07:59:34 -08:00
visionmedia cbc3163496 Release 0.0.2 2010-01-10 19:54:44 -08:00
visionmedia 8edc213517 Added view benchmarks; currently haml vs ejs 2010-01-10 09:42:48 -08:00
visionmedia b9f1589079 Merge branch 'master' of git://github.com/adamsanderson/express 2010-01-10 09:34:07 -08:00
visionmedia d7fa7864ce Updated Haml 2010-01-10 09:21:31 -08:00
visionmedia 725e2ec99a Added Request#attachment() specs. Closes #116 2010-01-08 09:59:27 -08:00
visionmedia 8953450e9a Utilizing nodes parseQuery() util. Closes #123
Introduces a failing Request#param() spec, yet to be fixed.
2010-01-08 09:53:17 -08:00
Adam Sanderson dda12561d3 Minor spell checking 2010-01-06 17:32:49 -08:00
visionmedia 9a2178d34d Display sample chat app messages on load 2010-01-06 15:17:34 -08:00
visionmedia b9278d4698 Updated haml 2010-01-06 15:13:08 -08:00
visionmedia 23d35c5828 Fix make init to work with older versions of git 2010-01-06 14:19:40 -08:00
visionmedia 389594dab7 Fixing makefile 2010-01-06 14:18:04 -08:00
ciaranj e125ae2c48 Merge branch 'master' of git://github.com/visionmedia/express 2010-01-06 18:34:42 +00:00
visionmedia 4f4b4f0de5 Misc 2010-01-06 08:59:05 -08:00
visionmedia 0469a41d0b Added link to scons 2010-01-06 08:58:55 -08:00
visionmedia a09c773f1b Specs can now run independant specs for those who cant build deps. Closes #127
Use `make test-independant` to ignore element-collection if you are not working on that
2010-01-06 08:57:29 -08:00
visionmedia 765c8ae0b3 Added super simple example route with string response 2010-01-06 08:45:43 -08:00
visionmedia a1eba23ee8 Fixed issues introduced by the node url module changes. Closes 126.
Everything is good to go now! should run fine. all specs pass.
2010-01-06 08:42:01 -08:00
visionmedia 25b34263e2 Parsing params correctly again due to node changes 2010-01-06 08:34:33 -08:00
visionmedia 30d23a8699 Fixing uri -> url 2010-01-06 08:22:41 -08:00
visionmedia 9d096378f3 Tests pass under node --version v0.1.24-13-ge2abc5f 2010-01-06 08:08:46 -08:00
ciaranj 3f9cbbbffb Merge branch 'master' of git://github.com/visionmedia/express 2010-01-06 09:47:40 +00:00
visionmedia 080e4290ea Fixed several specs due to throw_error being a bitch 2010-01-05 15:32:36 -08:00
visionmedia 39aea80fbb JSpec 3.1.0 2010-01-05 15:28:56 -08:00
visionmedia 8ac1eaa233 Fixed two assertions failing due to Collection#keys() returning strings 2010-01-05 15:17:22 -08:00
visionmedia 7df254be64 Updated readme test info 2010-01-05 15:10:39 -08:00
visionmedia 596a39ba19 Added make init for submodules 2010-01-05 15:09:34 -08:00
visionmedia a36b13006b Fixed libxmljs submodule 2010-01-05 15:06:51 -08:00
visionmedia e8771336ba Typo 2010-01-05 15:06:04 -08:00
ciaranj 6f7141a266 Merge branch 'master' of git://github.com/visionmedia/express 2010-01-05 23:04:50 +00:00
visionmedia 6f995d0d49 Refactored node spec runner 2010-01-05 15:00:42 -08:00
visionmedia 694572bd8a Updated to JSpec 3.0.0 2010-01-05 15:00:12 -08:00
visionmedia ddd6f27080 Fixed faulty Collection#toArray() spec due to keys() returning strings 2010-01-05 14:56:41 -08:00
visionmedia 60ed9bd49f libxmljs parseString -> parseHtmlString 2010-01-05 14:55:19 -08:00
visionmedia 54a8a7472a make test now builds libxmljs.node before testing 2010-01-05 14:47:51 -08:00
visionmedia 19672e7c40 Arch warning for testing binaries 2010-01-05 13:57:16 -08:00
ciaranj 99e3130f3c Merge branch 'master' of git://github.com/visionmedia/express 2010-01-04 12:47:40 +00:00
visionmedia 17c41689e3 Added Gareth to contrib list 2010-01-03 20:16:15 -08:00
visionmedia 22d4b18c0e Merge branch 'master' of github.com:visionmedia/express 2010-01-03 20:13:54 -08:00
visionmedia 4e85cace68 Release 0.0.1 2010-01-03 06:47:49 -08:00
James Herdman da390ff43d Introduce .gitignore. 2009-12-31 09:31:44 +08:00
ciaranj 774f96fcb9 Merge branch 'master' of github.com:ciaranj/express 2009-12-30 11:04:33 +00:00
ciaranj 17ab8e81aa Merge branch 'master' of git://github.com/visionmedia/express
Conflicts:
	lib/express/static.js
2009-12-30 11:03:27 +00:00
ciaranj b02b384f14 Add support for binding to *all* hosts by passing 'null' as the hostname
when 'run-ing' the app
2009-12-22 23:09:16 +00:00
ciaranj 3ec970bb67 Added in a *Very* basic cache (this will ultimately leak memory)
Can be enabled on or off by using set('cache statics')
2009-12-22 22:25:30 +00:00
ciaranj 22e338fbd4 Add support to StaticFile so that it works with non-textual files.
Previously binary files were being interpreted as UTF8 encoded bytestreams
this isn't an ideal for pushing out static files such as images.

I've modified the StaticFile type so that it explicitly declares the encoding
as 'binary' and updated the mechanism used to push back to the client to
also be 'binary aware'.  I'm not happy with my approach as I've violated the
encapsulation of the logic that was previously in the request.respond method, but
without further direction on how that object is moving this was the easiest thing to do
in order to keep moving with my plan to have a full website ;)
2009-12-22 15:15:37 +00:00
84 arquivos alterados com 2686 adições e 2545 exclusões
+3
Ver Arquivo
@@ -0,0 +1,3 @@
.DS_Store
*.seed
*.log
+9 -6
Ver Arquivo
@@ -1,9 +1,12 @@
[submodule "support/haml"]
path = support/haml
url = git://github.com/creationix/haml-js.git
[submodule "lib/support/oo"]
path = lib/support/oo
url = git://github.com/visionmedia/js-oo.git
[submodule "lib/support/ext"]
path = lib/support/ext
url = git://github.com/visionmedia/ext.js.git
[submodule "lib/support/sass"]
path = lib/support/sass
url = git://github.com/visionmedia/sass.js.git
[submodule "lib/support/haml"]
path = lib/support/haml
url = git://github.com/creationix/haml-js.git
[submodule "lib/support/js-oo"]
path = lib/support/js-oo
url = git://github.com/visionmedia/js-oo.git
+127 -1
Ver Arquivo
@@ -1,5 +1,131 @@
0.0.1 / YYYY-MM-DD
0.7.2 / 2010-03-16
==================
* Fixed GIT submodules (HAH!)
0.7.1 / 2010-03-16
==================
* Changed; Express now using submodules again until a PM is adopted
* Changed; chat example using millisecond conversions from ext
0.7.0 / 2010-03-15
==================
* Added Request#pass() support (finds the next matching route, or the given path)
* Added Logger plugin (default "common" format replaces CommonLogger)
* Removed Profiler plugin
* Removed CommonLogger plugin
0.6.0 / 2010-03-11
==================
* Added seed.yml for kiwi package management support
* Added HTTP client query string support when method is GET. Closes #205
* Added support for arbitrary view engines.
For example "foo.engine.html" will now require('engine'),
the exports from this module are cached after the first require().
* Added async plugin support
* Removed usage of RESTful route funcs as http client
get() etc, use http.get() and friends
* Removed custom exceptions
0.5.0 / 2010-03-10
==================
* Added ext dependency (library of js extensions)
* Removed extname() / basename() utils. Use path module
* Removed toArray() util. Use arguments.values
* Removed escapeRegexp() util. Use RegExp.escape()
* Removed process.mixin() dependency. Use utils.mixin()
* Removed Collection
* Removed ElementCollection
* Shameless self promotion of ebook "Advanced JavaScript" (http://dev-mag.com) ;)
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
==================
* Added parseParam() support for name[] etc. (allows for file inputs with "multiple" attr) Closes #180
* Added Both Cache and Session option "reapInterval" may be "reapEvery". Closes #174
* Added expiration support to cache api with reaper. Closes #133
* Added cache Store.Memory#reap()
* Added Cache; cache api now uses first class Cache instances
* Added abstract session Store. Closes #172
* Changed; cache Memory.Store#get() utilizing Collection
* Renamed MemoryStore -> Store.Memory
* Fixed use() of the same plugin several time will always use latest options. Closes #176
0.1.0 / 2010-02-03
==================
* Changed; Hooks (before / after) pass request as arg as well as evaluated in their context
* Updated node support to 0.1.27 Closes #169
* Updated dirname(__filename) -> __dirname
* Updated libxmljs support to v0.2.0
* Added session support with memory store / reaping
* Added quick uid() helper
* Added multi-part upload support
* Added Sass.js support / submodule
* Added production env caching view contents and static files
* Added static file caching. Closes #136
* Added cache plugin with memory stores
* Added support to StaticFile so that it works with non-textual files.
* Removed dirname() helper
* Removed several globals (now their modules must be required)
0.0.2 / 2010-01-10
==================
* Added view benchmarks; currently haml vs ejs
* Added Request#attachment() specs. Closes #116
* Added use of node's parseQuery() util. Closes #123
* Added `make init` for submodules
* Updated Haml
* Updated sample chat app to show messages on load
* Updated libxmljs parseString -> parseHtmlString
* Fixed `make init` to work with older versions of git
* Fixed specs can now run independant specs for those who cant build deps. Closes #127
* Fixed issues introduced by the node url module changes. Closes 126.
* Fixed two assertions failing due to Collection#keys() returning strings
* Fixed faulty Collection#toArray() spec due to keys() returning strings
* Fixed `make test` now builds libxmljs.node before testing
0.0.1 / 2010-01-03
==================
* Initial release
+10 -6
Ver Arquivo
@@ -1,13 +1,17 @@
NODE = node
all: test
test:
@$(NODE) spec/spec.node.js
@$(NODE) spec/node.js all
app:
@$(NODE) examples/app.js
app: app-chat
benchmark:
@$(NODE) benchmarks/collection.js
app-chat:
@$(NODE) examples/chat/app.js
.PHONY: test benchmark app
app-upload:
@$(NODE) examples/upload/app.js
.PHONY: test app
+36 -47
Ver Arquivo
@@ -11,100 +11,89 @@
* Sexy DSL with robust sinatra-like routing
* High performance
* Session support
* Mime helpers
* Redirection helpers
* Nested parameter parsing
* Full test coverage
* Extremely readable specs
* Multipart file upload support
* Test helpers (mock requests etc)
* Environment based configuration
* Light-weight JavaScript class implementation via js-oo
* Collections and chainable iterators
* ElementCollections / markup parsing via libxmljs and css selector traversal support via css2xpath
* View support (ejs, haml, mustache)
* View support (ejs, haml, sass, etc)
* Full test coverage
* Extremely readable specs
## Installation
Currently Express must be cloned (or downloaded), you can use the following command to
get rolling and initialize the submodule dependencies:
Install the [Kiwi package manager for nodejs](http://github.com/visionmedia/kiwi)
and run:
$ kiwi -v install express
or
$ git clone git://github.com/visionmedia/express.git && cd express && git submodule update --init && make app
Or with the [gh](http://github.com/visionmedia/gh) utility:
$ gh clone visionmedia express && cd express && git submodule update --init && make app
Install via git clone:
$ git://github.com/visionmedia/express.git && cd express && git submodule update --init
## Performance
Extensive benchmarking will wait until a development version
has been released.
Extensive performance enhancements have not yet been made,
since we are focusing on the framework it-self at the moment.
Currently Express can chew through a request with a two Haml views (*page and layout*)
requested **2000** times with concurrency of **80** in **2.4** seconds and **814**
requests per second. With no caching involved.
An identical Sinatra application was served with the **Thin** HTTP server
and scored **8.3** seconds and **238** requests per second. In this situation
Express is currently **3.5** times faster than Sinatra.
However if you are interested view the premature [benchmarks for Express framework](http://vision-media.ca/resources/nodejs/express-nodejs-web-development-framework-performance).
## Examples
require.paths.unshift('lib')
Below is a tiny Express application. View the [Wiki](http://wiki.github.com/visionmedia/express/) for detailed information.
require.paths.unshift('express/lib')
require('express')
require('express/plugins')
configure(function(){
use(MethodOverride)
use(ContentLength)
use(Redirect)
set('root', dirname(__filename))
enable('cache views')
get('/user', function(){
this.redirect('/user/' + this.currentUser.id)
})
get('/hello', function(){
this.contentType('html')
return '<h1>World<h1>'
})
get('/user/:id?', function(id) {
get('/user/:id', function(id){
this.render('user.haml.html', {
locals: {
name: id ? 'User ' + id : 'You'
user: this.currentUser,
usersOnline: Session.store.length()
}
})
})
run()
## Running Tests
Express uses the [JSpec](http://jspec.info) BDD JavaScript testing
framework to write and run elegant spec suites. JSpec is froozen
to spec/lib and does not require seperate installation.
To run all specifications:
framework to write and run elegant spec suites. JSpec is frozen
to spec/lib and **does not** require separate installation.
$ make test
Run individual suites:
$ node spec/spec.node.js core
$ node spec/spec.node.js mime
$ node spec/spec.node.js routing
$ node spec/node.js core
$ node spec/node.js mime
$ node spec/node.js routing
...
Express is currently being developed with node --version:
v0.1.21-66-g59a78d6
v0.1.32
## More Information
* [JavaScript Extensions &amp; Utilities](http://github.com/visionmedia/ext.js)
* [JavaScript Sass](http://github.com/visionmedia/sass.js)
* Featured in [Advanced JavaScript e-book](http://www.dev-mag.com/2010/02/18/advanced-javascript/) for only $4
## Contributors
* TJ Holowaychuk (visionmedia) &lt;tj@vision-media.ca&gt;
* Aaron Heckmann (aheckmann) &lt;aaron.heckmann+github@gmail.com&gt;
* Ciaran Jessup (ciaranj) &lt;ciaranj@gmail.com&gt;
* Gareth Jones (csausdev) &lt;gareth.jones@sensis.com.au&gt;
## License
-83
Ver Arquivo
@@ -1,83 +0,0 @@
;(function(){
var currentSuite
/**
* Contents of _fn_. Strips function literal and signature.
*
* @param {function} fn
* @return {string}
* @api private
*/
function contentsOf(fn) {
return fn.toString().match(/^[^\{]*{((.*\n*)*)}/m)[1]
}
/**
* Pad _str_ to _len_.
*
* @param {string} str
* @param {integer} len
* @return {string}
* @api private
*/
function pad(str, len) {
return str + (new Array(len - str.length)).join(' ')
}
/**
* Time the execution of _fn_
*
* @param {function} fn
* @return {float}
* @api private
*/
function time(fn) {
var start = Number(new Date)
fn()
return (Number(new Date) - start) / 1000
}
/**
* Benchmark _fn_ with the given _label_.
*
* @param {string} label
* @param {function} fn
* @api public
*/
function benchmark(label, fn) {
var duration = time(function(){
for (var i = 0; i < currentSuite.times; ++i)
fn()
}).toFixed(3)
print(pad(' ' + label, 50 - duration.toString().length) + duration + ' |')
}
/**
* Create a benchmark suite with the given _label_, which
* will run each benchmark n _times_. If _times_ is omitted
* then it defaults to 1.
*
* @param {string} label
* @param {integer, function} times
* @param {function} fn
* @api public
*/
suite = function(label, times, fn) {
currentSuite = this
if (typeof times == 'function')
this.times = 1, fn = times
else
this.times = times
print('\n ' + pad(label, 42 - this.times.toString().length) + this.times + ' time(s)')
print(' -------------------------------------------------')
eval(contentsOf(fn))
print('')
}
})()
-111
Ver Arquivo
@@ -1,111 +0,0 @@
require.paths.unshift('lib')
require.paths.unshift('benchmarks')
process.mixin(GLOBAL, require('sys'))
process.mixin(GLOBAL, require('benchmark'))
require('express')
print = puts
range = function(a, b) {
var array = []
while (a++ < b)
array.push(a-1)
return array
}
suite('Collection with [0..10,000]', 1000, function(){
array = range(0, 10000)
benchmark('for', function(){
for (var i = 0, len = array.length; i < len; ++i) ;
})
benchmark('for uncached', function(){
for (var i = 0; i < array.length; ++i) ;
})
benchmark('forEach()', function(){
array.forEach(function(){})
})
benchmark('#each()', function(){
$(array).each(function(){})
})
benchmark('#map()', function(){
$(array).map(function(n){ return n += 1 })
})
benchmark('#map() with shorthand', function(){
$(array).map('a += 1')
})
benchmark('#find()', function(){
$(array).find(function(n){ return n > 5000 })
})
benchmark('#select()', function(){
$(array).select(function(n){ return n % 2 })
})
benchmark('#first()', function(){
$(array).first(5000)
})
benchmark('#slice()', function(){
$(array).slice(100, 5000)
})
benchmark('#drop()', function(){
$(array).drop(5000)
})
benchmark('#length()', function(){
$(array).length()
})
benchmark('#keys()', function(){
$(array).keys()
})
benchmark('#toArray()', function(){
$(array).toArray()
})
benchmark('#min()', function(){
$(array).min()
})
benchmark('#max()', function(){
$(array).max()
})
benchmark('#sum()', function(){
$(array).sum()
})
benchmark('#avg()', function(){
$(array).avg()
})
benchmark('#clone()', function(){
$(array).clone()
})
benchmark('#merge()', function(){
$(array).merge({ foo: 'bar' })
})
benchmark('#sample()', function(){
$(array).sample()
})
benchmark('#chunk()', function(){
$(array).chunk(5)
})
benchmark('#at()', function(){
$(array).at(5000)
})
})
-58
Ver Arquivo
@@ -1,58 +0,0 @@
require.paths.unshift('lib')
require('express')
require('express/plugins')
configure(function(){
use(MethodOverride)
use(ContentLength)
use(CommonLogger)
set('root', dirname(__filename))
enable('cache views')
})
var messages = [],
StaticFile = require('express/static').File
get('/', function(){
this.redirect('/chat')
})
get('/chat', function(){
this.render('chat.haml.html', {
locals: {
messages: messages
}
})
})
post('/chat', function(){
messages.push(escape(this.param('message')).replace(/:\)/g, '<img src="http://icons3.iconfinder.netdna-cdn.com/data/icons/ledicons/emoticon_smile.png">'))
this.halt(200)
})
get('/chat/messages', function(){
var self = this,
previousLength = messages.length,
timer = setInterval(function(){
if (messages.length > previousLength)
self.contentType('json'),
previousLength = messages.length,
self.halt(200, JSON.encode(messages)),
clearInterval(timer)
}, 100)
})
get('/public/*', function(file){
this.sendfile(dirname(__filename) + '/public/' + file)
})
get('/error/view', function(){
this.render('does.not.exist')
})
get('/error', function(){
throw new Error('oh noes!')
})
run()
+82
Ver Arquivo
@@ -0,0 +1,82 @@
require.paths.unshift('lib')
require('express')
require('express/plugins')
var messages = [],
utils = require('express/utils'),
kiwi = require('kiwi')
configure(function(){
kiwi.seed('haml')
kiwi.seed('sass')
use(MethodOverride)
use(ContentLength)
use(Cookie)
use(Cache, { lifetime: (5).minutes, reapInterval: (1).minute })
use(Session, { lifetime: (15).minutes, reapInterval: (1).minute })
use(Logger)
set('root', __dirname)
})
get('/', function(){
this.pass('/chat')
})
get('/chat', function(){
this.render('chat.haml.html', {
locals: {
title: 'Chat',
messages: messages,
name: this.session.name,
usersOnline: Session.store.length()
}
})
})
post('/chat', function(){
this.session.name = this.param('name')
messages
.push(utils.escape(this.param('name')) + ': ' + utils.escape(this.param('message'))
.replace(/(http:\/\/[^\s]+)/g, '<a href="$1" target="express-chat">$1</a>')
.replace(/:\)/g, '<img src="http://icons3.iconfinder.netdna-cdn.com/data/icons/ledicons/emoticon_smile.png">'))
this.halt(200)
})
get('/chat/messages', function(){
var self = this,
previousLength = messages.length,
timer = setInterval(function(){
if (messages.length > previousLength)
self.contentType('json'),
previousLength = messages.length,
self.halt(200, JSON.encode(messages)),
clearInterval(timer)
}, 100)
})
get('/public/*', function(file){
this.sendfile(__dirname + '/public/' + file)
})
get('/*.css', function(file){
this.render(file + '.sass.css', { layout: false })
})
get('/error/view', function(){
this.render('does.not.exist')
})
get('/error', function(){
throw new Error('oh noes!')
})
get('/simple', function(){
return 'Hello :)'
})
get('/favicon.ico', function(){
this.halt()
})
run()

Antes

Largura:  |  Altura:  |  Tamanho: 5.2 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 5.2 KiB

@@ -5,7 +5,7 @@ $(function(){
var message = $('input[name=message]'),
name = $('input[name=name]')
if (message.val())
$.post('/chat', { message: $.trim(name.val()) + ': ' + message.val() }, function(){
$.post('/chat', { name: name.val(), message: message.val() }, function(){
message.val('')
})
else
@@ -1,8 +1,10 @@
%h1 Chat
%img.bubble{ src: '/public/images/bubble.png' }
%ul#messages
:each msg in messages
%li= msg
%form{ method: 'post' }
%input{ type: 'hidden', name: '_method', value: 'put' }
%input{ type: 'text', name: 'name', value: 'guest' }
%input{ type: 'text', name: 'name', value: name || 'guest' }
%input{ type: 'text', name: 'message' }
%input{ type: 'submit', value: 'Send' }
@@ -1,8 +1,11 @@
%html
%head
%title Chat
%title= title
%script{ src: '/public/javascripts/jquery.js' }
%script{ src: '/public/javascripts/app.js' }
%link{ rel: 'stylesheet', href: '/public/stylesheets/style.css' }
%link{ rel: 'stylesheet', href: '/style.css' }
%body
#wrapper= body
#online
Online:
%strong= usersOnline
+81
Ver Arquivo
@@ -0,0 +1,81 @@
body
:font-family "Helvetica Neue", "Lucida Grande", "Arial"
:font-size 13px
:text-align center
=text-stroke 1px rgba(255, 255, 255, 0.1)
:color #555
h1, h2
:margin 0
:font-size 22px
:color #343434
h1
:text-shadow 1px 2px 2px #ddd
:font-size 60px
img.bubble
:position absolute
:top -25px
:left 120px
#wrapper
:position relative
:margin 100px auto
:width 500px
:text-align left
ul
:margin 0
:padding 0
:max-height 300px
:overflow-x hidden
li
:margin 5px 0
:padding 3px 8px
:list-style none
:border 1px solid #eee
=border-radius 3px
=border-radius 3px
li:hover
:cursor pointer
:color #2E2E2E
input[type=text]
:padding 5px
:border 1px solid #ddd
:outline none
=border-radius 2px
input[type=text]:focus
:border-color #00C3FF
input[type=submit]
=border-radius 2px
=box-shadow 0 1px 2px #ddd
:padding 6px 10px
:border solid 1px #999
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd))
:color #333
:text-decoration none
:cursor pointer
:display inline-block
:text-align center
:text-shadow 0px 1px 1px #fff
:line-height 1
input[type=submit]:hover
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4))
input[type=submit]:active
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7))
input[name=name]
:width 80px
a
:color #1ABFF1
=transition-property padding
=transition-duration 0.15s
a:hover
:padding 0 5px
a:hover:before
:content 'visit: '
#online
:font-size 12px
-84
Ver Arquivo
@@ -1,84 +0,0 @@
body {
font-family: "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 13px;
text-align: center;
-webkit-text-stroke: 1px rgba(255, 255, 255, 0.1);
color: #555;
}
h1, h2 {
margin: 0;
font-size: 22px;
color: #343434;
}
h1 {
text-shadow: 1px 2px 2px #ddd;
font-size: 60px;
}
img.bubble {
position: absolute;
top: -25px;
left: 120px;
}
#wrapper {
position: relative;
margin: 100px auto;
width: 500px;
text-align: left;
}
ul {
margin: 0;
padding: 0;
}
ul li {
margin: 5px 0;
padding: 3px 8px;
list-style: none;
border: 1px solid #eee;
-webkit-border-radius: 3px;
-mox-border-radius: 3px;
-webkit-transition-property: color;
-webkit-transition-duration: 0.1s;
}
ul li:hover {
cursor: pointer;
color: #2E2E2E;
}
ul {
max-height: 300px;
overflow-x: hidden;
}
input[type=text] {
padding: 5px;
border: 1px solid #ddd;
outline: none;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
}
input[type=text]:focus {
border-color: #00C3FF;
}
input[type=submit] {
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
-webkit-box-shadow: 0 1px 2px #ddd;
-moz-box-shadow: 0 1px 2px #ddd;
padding: 6px 10px;
border: solid 1px #999;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd));
color: #333;
text-decoration: none;
cursor: pointer;
display: inline-block;
text-align: center;
text-shadow: 0px 1px 1px #fff;
line-height: 1;
}
input[type=submit]:hover {
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4));
}
input[type=submit]:active {
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7));
}
input[name=name] {
width: 80px;
}
+48
Ver Arquivo
@@ -0,0 +1,48 @@
require.paths.unshift('lib')
require('express')
require('express/plugins')
var kiwi = require('kiwi')
configure(function(){
kiwi.seed('haml')
kiwi.seed('sass')
use(MethodOverride)
use(ContentLength)
use(Cookie)
use(Session)
use(Flash)
use(Logger)
set('root', __dirname)
})
get('/', function(){
this.redirect('/upload')
})
get('/upload', function(){
this.render('upload.haml.html', {
locals: {
flashes: this.flash('info')
}
})
})
post('/upload', function(){
this.param('images').each(function(image){
puts(image.filename + ' -> ' + image.tempfile)
this.flash('info', 'Uploaded ' + image.filename)
}, this)
this.redirect('/upload')
})
get('/public/*', function(file){
this.sendfile(__dirname + '/public/' + file)
})
get('/*.css', function(file){
this.render(file + '.sass.css', { layout: false })
})
run()
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+14
Ver Arquivo
@@ -0,0 +1,14 @@
%html
%head
%title Upload
%script{ src: '/public/javascripts/jquery.js' }
%script{ src: '/public/javascripts/app.js' }
%link{ rel: 'stylesheet', href: '/style.css' }
%body
#wrapper
%h1 Upload
:if flashes
%ul.messages.info
:each msg in flashes
%li= msg
.body= body
+68
Ver Arquivo
@@ -0,0 +1,68 @@
body
:font-family "Helvetica Neue", "Lucida Grande", "Arial"
:font-size 13px
:text-align center
:-webkit-text-stroke 1px rgba(255, 255, 255, 0.1)
:color #555
h1, h2
:margin 0 0 15px 0
:font-size 22px
:color #343434
h1
:text-shadow 1px 2px 2px #ddd
:font-size 60px
h2
:margin-top 15px
#wrapper
:position relative
:margin 100px auto
:width 500px
:text-align left
input[type=file]
:padding 5px
:border 1px solid #ddd
:outline none
:-webkit-border-radius 2px
:-moz-border-radius 2px
input[type=file]:focus
:border-color #00C3FF
input[type=submit]
:-webkit-border-radius 2px
:-moz-border-radius 2px
:-webkit-box-shadow 0 1px 2px #ddd
:-moz-box-shadow 0 1px 2px #ddd
:padding 6px 10px
:border solid 1px #999
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd))
:color #333
:text-decoration none
:cursor pointer
:display inline-block
:text-align center
:text-shadow 0px 1px 1px #fff
:line-height 1
input[type=submit]:hover
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4))
input[type=submit]:active
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7))
input[name=name]
:width 80px
form
.panel
:float left
:width 100%
: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
+18
Ver Arquivo
@@ -0,0 +1,18 @@
:if typeof images !== 'undefined'
.images
:each img in images
%img{ src: img }
%h2 Singles
%form{ method: 'post', enctype: 'multipart/form-data' }
%input{ type: 'file', name: 'images[0]' }
%input{ type: 'file', name: 'images[1]' }
%input{ type: 'file', name: 'images[2]' }
.panel
%input{ type: 'submit', value: 'Upload' }
%h2 Multiple
%form{ method: 'post', enctype: 'multipart/form-data' }
%input{ type: 'file', name: 'images[]', multiple: 'multiple' }
.panel
%input{ type: 'submit', value: 'Upload' }
+6 -2
Ver Arquivo
@@ -1,3 +1,7 @@
require('support/js-oo/lib/oo')
require('express/core')
require.paths.unshift(__dirname + '/support/ext/lib')
require('ext')
require('support/oo/lib/oo')
require('support/haml/lib/haml')
require('support/sass/lib/sass')
require('express/core')
-544
Ver Arquivo
@@ -1,544 +0,0 @@
// Express - Collection - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Throw $break in order to stop iteration.
*/
$break = '__break__'
var property = /^\w+$/,
method = /^\w+\(/
/**
* Normalize callback _fn_. When a string is
* passed convert the shorthand expr to a function.
*
* - Functions are passed through un-touched
* - Strings with length of < 4 are considered operators between a and b
* - Single words are considered properties on a
* - Single functions are considered method calls on a
* - Larger strings are considered return expressions
*
* @param {string, function} fn
* @return {function}
* @api private
*/
function callback(fn) {
if (fn === undefined) return
if (fn instanceof Function) return fn
if (fn.length < 4) return Function('a, b, c', 'return a ' + fn + ' b')
if (property.test(fn) ||
method.test(fn)) fn = 'a.' + fn
return Function('a, b, c', 'return ' + fn)
}
// --- Collection
Collection = Class({
/**
* Initialize collection with an array-like object.
*
* @param {object} arr
* @api private
*/
init: function(arr) {
this.arr = arr || []
},
/**
* Return the value of _index_ or null.
*
* @param {int} index
* @return {mixed}
* @api public
*/
at: function(index) {
if ('length' in this.arr)
return this.arr[index]
var result, i = 0
this.each(function(val){
if (i++ == index) {
result = val
throw $break
}
})
return result
},
/**
* Iterate collection using callback _fn_,
* passing both the value and index.
*
* @param {function} fn
* @return {Collection}
* @api public
*/
each: function(fn) {
try {
if (this.arr.forEach)
this.arr.forEach(fn)
else
for (var key in this.arr)
if (this.arr.hasOwnProperty(key))
fn(this.arr[key], key)
}
catch (e) {
if (e != $break) throw e
}
return this
},
/**
* Reverse a collection.
*
* @return {Collection}
* @api public
*/
reverse: function() {
if (this.arr.reverse)
return $(this.arr.reverse())
return this
},
/**
* Join a collection with the given _str_ or ''.
*
* @param {string} str
* @return {string}
* @api public
*/
join: function(str) {
return this.toArray().join(str || '')
},
/**
* Sort collection with optional _fn_.
*
* @param {function} fn
* @return {Collection}
* @warn converts to array
* @api public
*/
sort: function(fn) {
fn = callback(fn)
return $(this.toArray().sort(fn))
},
/**
* Iterate with _memo_ using callback _fn_.
* The _memo_ object is passed as the first
* argument, and the return value of _fn_ becomes
* the value of _memo_ providing a functional
* approach to reducing a collection.
*
* @param {mixed} memo
* @param {function} fn
* @return {mixed}
* @api public
*/
reduce: function(memo, fn) {
fn = callback(fn)
if (this.arr.reduce)
return this.arr.reduce(fn, memo)
this.each(function(val, key){
memo = fn(memo, val, key)
})
return memo
},
/**
* Map using callback _fn_, returning a
* new collection of return values.
*
* @param {function} fn
* @return {Collection}
* @api public
*/
map: function(fn) {
fn = callback(fn)
if (this.arr.map)
return $(this.arr.map(fn))
return $(this.reduce([], function(array, val, key){
array.push(fn(val, key))
return array
}))
},
/**
* Return collection of values when _fn_ evaluates
* to true.
*
* @param {function} fn
* @return {Collection}
* @api public
*/
select: function(fn) {
fn = callback(fn)
return $(this.reduce([], function(array, val, key){
if (fn(val, key))
array.push(val)
return array
}))
},
/**
* Return collection of values when _fn_ evaluates
* to false.
*
* @param {function} fn
* @return {Collection}
* @api public
*/
reject: function(fn) {
fn = callback(fn)
return $(this.reduce([], function(array, val, key){
if (!fn(val, key))
array.push(val)
return array
}))
},
/**
* Check if all arguments passed are found within
* the collection using the === operator.
*
* @return {bool}
* @api public
*/
includes: function() {
var self = this
return $(arguments).all(function(arg){
return self.any(function(val){
return val === arg
})
})
},
/**
* Return the first _n_ value(s), defaults to 1.
*
* @param {int} n
* @return {mixed}
* @api public
*/
first: function(n) {
return n ?
this.slice(0, n) :
this.at(0)
},
/**
* Return the last _n_ value(s), defaults to -1.
*
* @param {int} n
* @return {mixed}
* @api public
*/
last: function(n) {
var len = this.length()
return n ?
this.slice(len - n, len) :
this.at(--len)
},
/**
* Drop the first _n_ values, returning the others.
*
* @param {int} n
* @return {Collection}
* @api public
*/
drop: function(n) {
return this.slice(n, this.length())
},
/**
* Return a slice of values from _start_ to _end_.
*
* @param {int} start
* @param {int} end
* @return {Collection}
* @api public
*/
slice: function(start, end) {
if (this.arr.slice)
return $(this.arr.slice(start, end))
var i = 0
return $(this.reduce([], function(array, val){
if (i++ >= start)
if (i <= end)
array.push(val)
else
throw $break
return array
}))
},
/**
* Iterate until _fn_ evaluates to true, then
* return the matching value.
*
* @param {function} fn
* @return {mixed}
* @api public
*/
find: function(fn) {
var result, fn = callback(fn)
this.each(function(val, key){
if (fn(val, key)) {
result = val
throw $break
}
})
return result
},
/**
* Return true if _fn_ ever evaluates to true, otherwise
* returns false.
*
* @param {function} fn
* @return {bool}
* @api public
*/
any: function(fn) {
fn = callback(fn)
if (this.arr.some)
return this.arr.some(fn)
return !! this.find(fn)
},
/**
* Returns true if _fn_ always evaluates to true, otherwise
* returns false.
*
* @param {function} fn
* @return {bool}
* @api public
*/
all: function(fn) {
fn = callback(fn)
if (this.arr.every)
return this.arr.every(fn)
return this.reduce(true, function(state, val, key){
if (!state) throw $break
return !! fn(val, key)
})
},
/**
* Select values matching _pattern_
*
* @param {regexp} pattern
* @return {Collection}
* @api public
*/
grep: function(pattern) {
return this.select(function(val){
return pattern.exec(val)
})
},
/**
* Return keys as a collection.
*
* @return {Collection}
* @api public
*/
keys: function() {
return $(Object.keys(this.arr))
},
/**
* Convert a collection to a true array.
* Works recursively
*
* @return {array}
* @api public
*/
toArray: function() {
return this.reduce([], function(array, val){
val instanceof Collection ?
array.push(val.toArray()) :
array.push(val)
return array
})
},
/**
* Return the lowest number in the collection.
*
* @return {number}
* @api public
*/
min: function() {
return this.reduce(null, function(min, val){
return min === null ? val :
val < min ? val :
min
})
},
/**
* Return the largest number in the collection.
*
* @return {number}
* @api public
*/
max: function() {
return this.reduce(null, function(max, val){
return max === null ? val :
val > max ? val :
max
})
},
/**
* Group collection into several collections grouped by _size_.
*
* @param {int} size
* @return {Collection}
* @api public
*/
chunk: function(size) {
var len = this.arr.length,
chunks = [], chunk = [], i = 0
this.each(function(val, key){
chunk.push(val)
if (i++ > 0 && (i % size == 0 || i == len))
chunks.push($(chunk)),
chunk = []
})
return $(chunks)
},
/**
* Return the length of this collection.
*
* @return {int}
* @api public
*/
length: function() {
if ('length' in this.arr)
return this.arr.length
return this.reduce(0, function(len){
return ++len
})
},
/**
* Return the sum of numeric values in this collection.
*
* @return {number}
* @api public
*/
sum: function() {
return this.reduce(0, function(sum, n){
return sum + n
})
},
/**
* Return the average of numeric values in this collection.
*
* @return {number}
* @api public
*/
avg: function() {
return this.sum() / this.length()
},
/**
* Merge _other_ with this collection.
*
* @param {mixed} other
* @return {Collection}
* @api public
*/
merge: function(other) {
if (this.arr instanceof Array)
return $($(other).reduce(this.arr, function(array, val){
array.push(val)
return array
}))
else
return $(process.mixin(this.arr, $(other).arr))
},
/**
* Clone the collection.
*
* @return {Collection}
* @api public
*/
clone: function() {
return this.merge({})
},
/**
* Return the value of a random index.
*
* @return {mixed}
* @api public
*/
sample: function() {
return this.at(Math.floor(Math.random() * this.length()))
},
/**
* Convert collection to a string.
*
* @return {string}
* @api public
*/
toString: function() {
return '[Collection ' + this.arr + ']'
}
})
/**
* Create a new collection from an array-like object.
*
* @param {object} arr
* @return {Collection}
* @api public
*/
var $ = exports.$ = function(arr) {
if (arr instanceof Collection) return arr
return new Collection(arr)
}
+127 -59
Ver Arquivo
@@ -1,15 +1,20 @@
// Express - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
process.mixin(require('sys'))
process.mixin(require('express/exceptions'))
process.mixin(require('express/collection'))
process.mixin(require('express/event'))
process.mixin(require('express/request'))
process.mixin(require('express/helpers'))
process.mixin(require('express/plugin'))
process.mixin(require('express/mime'))
process.mixin(require('express/dsl'))
/**
* Module dependencies.
*/
var multipart = require('multipart'),
utils = require('express/utils'),
events = require('events'),
fs = require('fs')
global.merge(require('sys'))
global.merge(require('express/event'))
global.merge(require('express/request'))
global.merge(require('express/plugin'))
global.merge(require('express/dsl'))
// --- Route
@@ -37,19 +42,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
@@ -77,7 +69,7 @@ Route = Class({
var self = this
this.keys = []
if (path instanceof RegExp) return path
return new RegExp('^' + escapeRegexp(normalizePath(path), '.')
return new RegExp('^' + RegExp.escape(normalizePath(path), '.')
.replace(/\*/g, '(.+)')
.replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
self.keys.push(key)
@@ -95,7 +87,7 @@ Route = Class({
Router = Class({
/**
* Initialize with _request_.
* Initialize with _request_ and parse url.
*
* @param {Request} request
* @api private
@@ -112,10 +104,19 @@ Router = Class({
* @api private
*/
route: function(){
var route = this.matchingRoute()
if (route)
return route.run(this.request.captures.slice(1), this.request)
route: function() {
var body,
route = this.matchingRoute()
if (route) {
body = route.fn.apply(this.request, this.request.captures.slice(1));
if (this.request.passed) {
if (typeof this.request.passed === 'string')
this.request.url.pathname = this.request.passed
this.request.passed = false
return this.route()
}
return body
}
else if (this.request.accepts('html') && set('helpful 404'))
this.request.halt(404, require('express/pages/not-found').render(this.request))
else
@@ -129,11 +130,13 @@ Router = Class({
* @api private
*/
matchingRoute: function(){
var self = this
return $(Express.routes).find(function(route){
return self.match(route)
})
matchingRoute: function() {
this.lastMatchingRoute = this.lastMatchingRoute || 0
var routes = Express.routes, route
while (route = routes[this.lastMatchingRoute++])
if (this.match(route))
break
return route
},
/**
@@ -147,7 +150,7 @@ Router = Class({
match: function(route) {
if (this.request.method.toLowerCase() == route.method)
if (this.request.captures = this.request.uri.path.match(route.path)) {
if (this.request.captures = this.request.url.pathname.match(route.path)) {
this.mapParams(route)
return true
}
@@ -155,17 +158,16 @@ Router = Class({
/**
* Map #request.captures to #request.params.path based on the
* given _route_ keys.
* given _route_ params.
*
* @param {Route} route
* @api private
*/
mapParams: function(route) {
var self = this
$(route.keys).each(function(key, i){
self.request.params.path[key] = self.request.captures[++i]
})
route.keys.each(function(key, i){
this.request.params.path[key] = this.request.captures[++i]
}, this)
}
})
@@ -207,15 +209,58 @@ 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
require('http')
.createServer(function(request, response){
request.body = ''
request.setBodyEncoding('utf8')
request.addListener('body', function(chunk){ request.body += chunk })
request.addListener('complete', function(){ self.route(request, response) })
request.setBodyEncoding('binary')
function callback(e, result) {
if (e)
self.error(e, request, response)
else if (!pendingFiles)
self.route(request, response)
}
if (request.headers['content-type'] &&
request.headers['content-type'].includes('multipart/form-data')) {
var stream = multipart.parse(request),
pendingFiles = 0
request.params = { post: {}}
stream
.addListener('partBegin', function(part) {
if (part.filename)
++pendingFiles,
part.tempfile = '/tmp/express-' + Number(new Date) + utils.uid(),
part.fileStream = fs.createWriteStream(part.tempfile)
else
part.buf = ''
})
.addListener('body', function(chunk) {
if (stream.part.fileStream)
stream.part.fileStream.write(chunk)
else
stream.part.buf += chunk
})
.addListener('partEnd', function(part) {
if (!part.name) return
if (part.fileStream)
part.fileStream.close(function(){
--pendingFiles
callback()
}),
utils.mergeParam(part.name, { filename: part.filename, tempfile: part.tempfile }, request.params.post)
else
utils.mergeParam(part.name, part.buf, request.params.post)
})
.addListener('error', callback)
.addListener('complete', callback)
}
else
request
.addListener('data', function(chunk){ request.body += chunk })
.addListener('end', callback)
})
.listen(this.port, this.host, this.backlog)
puts('Express started at http://' + this.host + ':' + this.port + '/ in ' + Express.environment + ' mode')
@@ -230,29 +275,46 @@ Server = Class({
*/
route: function(request, response){
request = new Request(request, response)
request.trigger('request')
try {
if (typeof (body = (new Router(request)).route()) == 'string')
request.halt(200, body)
}
catch (e) {
if (e instanceof ExpressError)
throw e
if (request.accepts('html') && set('show exceptions'))
request.halt(500, require('express/pages/show-exceptions').render(request, e))
else
request.halt(500)
if (set('throw exceptions'))
throw e
}
var self = this,
request = new Request(request, response)
request.trigger('request', function(e) {
try {
if (e) throw e
if (request.response.finished) return
if (typeof (body = (new Router(request)).route()) === 'string')
request.halt(200, body)
} catch (e) {
self.error(e, request)
}
})
},
/**
* Handle errors.
*
* @param {Error} e
* @param {object} request
* @param {object} response
* @api private
*/
error: function (e, request, response) {
if (!(request instanceof Request))
request = new Request(request, response),
request.trigger('request')
if (request.accepts('html') && set('show exceptions'))
request.halt(500, require('express/pages/show-exceptions').render(request, e))
else
request.halt(500)
if (set('throw exceptions'))
throw e
}
})
// --- Express
Express = {
version: '0.0.1',
version: '0.7.2',
config: [],
routes: [],
plugins: [],
@@ -264,6 +326,7 @@ Express = {
configure(function(){
use(require('express/plugins/view').View)
use(require('express/plugins/cache').Cache)
use(require('express/plugins/redirect').Redirect)
use(require('express/plugins/body-decoder').BodyDecoder)
})
@@ -276,3 +339,8 @@ configure('development', function(){
configure('test', function(){
enable('throw exceptions')
})
configure('production', function(){
enable('cache view contents')
enable('cache static files')
})
+28 -20
Ver Arquivo
@@ -13,7 +13,10 @@ 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 (!Express.server.running)
Express.routes.push(new Route(method, path, fn, options))
else
throw new Error('cannot create route ' + method.toUpperCase() + " `" + path + "' at runtime")
}
}
@@ -28,11 +31,11 @@ function route(method) {
*/
exports.set = function(option, val) {
return val === undefined ?
Express.settings[option] instanceof Function ?
Express.settings[option]() :
Express.settings[option] :
Express.settings[option] = val
return val === undefined
? Express.settings[option] instanceof Function
? Express.settings[option]()
: Express.settings[option]
: Express.settings[option] = val
}
/**
@@ -68,12 +71,16 @@ exports.disable = function(option) {
*/
exports.run = function() {
configure(Express.environment = process.ENV.EXPRESS_ENV || 'development')
configure(Express.environment = process.env.EXPRESS_ENV || 'development')
Express.plugins.each(function(plugin){
if ('init' in plugin.klass)
plugin.klass.init(plugin.options)
})
Express.server.run.apply(Express.server, arguments)
}
/**
* Configure _environment_ with _fn_.
* Configure _env_ with _fn_.
*
* Global configuration, disregards which
* environment is active:
@@ -92,22 +99,23 @@ exports.run = function() {
*
* configure('development')
*
* @param {string, function} environment
* @param {string, function} env
* @param {function} fn
* @api public
*/
exports.configure = function(environment, fn) {
if (environment instanceof Function)
fn = environment, environment = 'all'
exports.configure = function(env, fn) {
if (env instanceof Function)
fn = env, env = 'all'
if (fn instanceof Function)
return Express.config.push([environment, fn])
if (typeof environment != 'string')
throw 'environment require'
for (var i = 0, len = Express.config.length; i < len; ++i)
if (Express.config[i][0] == environment ||
Express.config[i][0] == 'all')
Express.config[i][1].call(Express)
return Express.config.push([env, fn])
if (typeof env !== 'string')
throw new Error('environment required')
Express.config.each(function(conf){
if (conf[0] === env ||
conf[0] === 'all')
conf[1].call(Express)
})
}
// --- Routing API
@@ -115,4 +123,4 @@ exports.configure = function(environment, fn) {
exports.get = exports.view = route('get')
exports.post = exports.create = route('post')
exports.del = exports.destroy = route('delete')
exports.put = exports.update = route('put')
exports.put = exports.update = route('put')
-179
Ver Arquivo
@@ -1,179 +0,0 @@
// Express - ElementCollection - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Load libxml support.
*/
var libxml = require('support/libxmljs')
// --- css2xpath
/** version 0.1 2009-04-30
* @author Andrea Giammarchi
* @license Mit Style License
* @project http://code.google.com/p/css2xpath/
*/
var css2xpath=(function(){var b=[/\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]/g,"[@$1$2]",/\s*,\s*/g,"|",/\s*(\+|~|>)\s*/g,"$1",/([a-zA-Z0-9\_\-\*])~([a-zA-Z0-9\_\-\*])/g,"$1/following-sibling::$2",/([a-zA-Z0-9\_\-\*])\+([a-zA-Z0-9\_\-\*])/g,"$1/following-sibling::*[1]/self::$2",/([a-zA-Z0-9\_\-\*])>([a-zA-Z0-9\_\-\*])/g,"$1/$2",/\[([^=]+)=([^'|"][^\]]*)\]/g,"[$1='$2']",/(^|[^a-zA-Z0-9\_\-\*])(#|\.)([a-zA-Z0-9\_\-]+)/g,"$1*$2$3",/([\>\+\|\~\,\s])([a-zA-Z\*]+)/g,"$1//$2",/\s+\/\//g,"//",/([a-zA-Z0-9\_\-\*]+):first-child/g,"*[1]/self::$1",/([a-zA-Z0-9\_\-\*]+):last-child/g,"$1[not(following-sibling::*)]",/([a-zA-Z0-9\_\-\*]+):only-child/g,"*[last()=1]/self::$1",/([a-zA-Z0-9\_\-\*]+):empty/g,"$1[not(*) and not(normalize-space())]",/([a-zA-Z0-9\_\-\*]+):not\(([^\)]*)\)/g,function(f,e,d){return e.concat("[not(",a(d).replace(/^[^\[]+\[([^\]]*)\].*$/g,"$1"),")]")},/([a-zA-Z0-9\_\-\*]+):nth-child\(([^\)]*)\)/g,function(f,e,d){switch(d){case"n":return e;case"even":return"*[position() mod 2=0 and position()>=0]/self::"+e;case"odd":return e+"[(count(preceding-sibling::*) + 1) mod 2=1]";default:d=(d||"0").replace(/^([0-9]*)n.*?([0-9]*)$/,"$1+$2").split("+");d[1]=d[1]||"0";return"*[(position()-".concat(d[1],") mod ",d[0],"=0 and position()>=",d[1],"]/self::",e)}},/:contains\(([^\)]*)\)/g,function(e,d){return"[contains(string(.),'"+d+"')]"},/\[([a-zA-Z0-9\_\-]+)\|=([^\]]+)\]/g,"[@$1=$2 or starts-with(@$1,concat($2,'-'))]",/\[([a-zA-Z0-9\_\-]+)\*=([^\]]+)\]/g,"[contains(@$1,$2)]",/\[([a-zA-Z0-9\_\-]+)~=([^\]]+)\]/g,"[contains(concat(' ',normalize-space(@$1),' '),concat(' ',$2,' '))]",/\[([a-zA-Z0-9\_\-]+)\^=([^\]]+)\]/g,"[starts-with(@$1,$2)]",/\[([a-zA-Z0-9\_\-]+)\$=([^\]]+)\]/g,function(f,e,d){return"[substring(@".concat(e,",string-length(@",e,")-",d.length-3,")=",d,"]")},/\[([a-zA-Z0-9\_\-]+)\!=([^\]]+)\]/g,"[not(@$1) or @$1!=$2]",/#([a-zA-Z0-9\_\-]+)/g,"[@id='$1']",/\.([a-zA-Z0-9\_\-]+)/g,"[contains(concat(' ',normalize-space(@class),' '),' $1 ')]",/\]\[([^\]]+)/g," and ($1)"],c=b.length;return function a(e){var d=0;while(d<c){e=e.replace(b[d++],b[d++])}return"//"+e}})();
// --- ElementCollection
ElementCollection = Collection.extend({
/**
* Initialize with string of _markup_.
*
* @param {string} markup
* @api private
*/
init: function(markup) {
if (typeof markup != 'string')
return this.__super__(markup)
if (!(/<html>/.test(markup)))
markup = '<html><body>' + markup + '</body></html>'
this.document = libxml.parseString(markup)
this.arr = [this.document.root()]
},
/**
* Return the first element's name.
*
* @return {string}
* @api public
*/
name: function() {
return this.at(0).name()
},
/**
* Search child elements with the given _xpath_.
*
* @param {string} xpath
* @return {ElementCollection}
* @api public
*/
xpath: function(xpath) {
// TODO: refactor with flatten()
return $(this.reduce([], function(array, e){
$(e.find(xpath)).each(function(child){
array.push(child)
})
return array
}))
},
/**
* Search child elements with the given css _selector_
*
* @param {string} selector
* @return {ElementCollection}
* @api public
*/
search: function(selector) {
return this.xpath(css2xpath(selector))
},
/**
* Return collection of children.
*
* @return {ElementCollection}
* @api public
*/
children: function() {
// TODO: refactor with flatten()
return $(this.reduce([], function(array, e){
$(e.children()).each(function(child){
array.push(child)
})
return array
}))
},
/**
* Return collection of parents.
*
* @return {ElementCollection}
* @api public
*/
parents: function() {
return this.map(function(e){
return e.parent()
})
},
/**
* Return the first element's parent.
*
* @return {ElementCollection}
* @api public
*/
parent: function() {
return $([this.at(0).parent()])
},
/**
* Return the prev element.
*
* @return {ElementCollection}
* @api public
*/
prev: function() {
return $([this.at(0).prev_sibling()])
},
/**
* Return the next element.
*
* @return {ElementCollection}
* @api public
*/
next: function() {
return $([this.at(0).next_sibling()])
},
/**
* Return the first element's text content.
*
* @return {string}
* @api public
*/
text: function() {
return this.at(0).text()
},
/**
* Convert collection to a string.
*
* @return {string}
* @api public
*/
toString: function() {
if (this.at(0) && this.at(0).doc)
return '[Collection <elements>]'
return this.__super__()
}
})
/**
* Add markup support to $().
*
* @param {object} arr
* @return {Collection, ElementCollection}
* @api public
*/
var $ = exports.$ = function(arr) {
if (arr instanceof Collection) return arr
return new ElementCollection(arr)
}
+1 -1
Ver Arquivo
@@ -13,7 +13,7 @@ exports.Event = Class({
init: function(name, data) {
this.name = name
process.mixin(this, data)
this.merge(data || {})
},
/**
-14
Ver Arquivo
@@ -1,14 +0,0 @@
// Express - Exceptions - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
// --- ExpressError
ExpressError = Class({
name: 'ExpressError',
init: function(message) {
this.message = message
},
toString: function() {
return this.name + ': ' + this.message
}
})
-151
Ver Arquivo
@@ -1,151 +0,0 @@
// Express - Helpers - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* JSON aliases.
*/
JSON.encode = JSON.stringify
JSON.decode = JSON.parse
/**
* Return the directory name of the given _path_.
*
* @param {string} path
* @return {string}
* @api public
*/
exports.dirname = function(path) {
return path.split('/').slice(0, -1).join('/')
}
/**
* Return the extension name of the given _path_,
* or null when not present.
*
* @param {string} path
* @return {string}
* @api public
*/
exports.extname = function(path) {
if (path.lastIndexOf('.') < 0) return
return path.slice(path.lastIndexOf('.') + 1)
}
/**
* Return the basename of the given _path_.
*
* @param {string} path
* @return {string}
* @api public
*/
exports.basename = function(path) {
return path.split('/').slice(-1)[0]
}
/**
* Escape special characters in _html_.
*
* @param {string} html
* @return {string}
* @api public
*/
exports.escape = function(html) {
return html.toString()
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
}
/**
* Convert native array-like objects into an
* array with optional _offset_.
*
* @param {object} arr
* @param {int} offset
* @return {array}
* @api public
*/
exports.toArray = function(arr, offset) {
return Array.prototype.slice.call(arr, offset)
}
/**
* Escape RegExp _chars_ in _string_. Where _chars_
* defaults to regular expression special characters.
*
* _chars_ should be a space delimited list of characters,
* for example '[ ] ( )'.
*
* @param {string} string
* @param {string} chars
* @return {Type}
* @api public
*/
exports.escapeRegexp = function(string, chars) {
var specials = (chars || '/ . * + ? | ( ) [ ] { } \\').split(' ').join('|\\')
return string.replace(new RegExp('(\\' + specials + ')', 'g'), '\\$1')
}
/**
* Decode values in _params_.
*
* @param {hash} params
* @see parseNestedParams()
* @api private
*/
function decode(params) {
for (var key in params)
params[key] = decodeURIComponent(params[key]).replace(/\+/g, ' ')
}
/**
* Parse nested _params_.
*
* @param {hash} params
* @return {hash}
* @see parseParams()
* @api public
*/
exports.parseNestedParams = function(params) {
var parts, key
decode(params)
for (key in params)
if (parts = key.split('['))
if (parts.length > 1)
for (var i = 0, prop = params, len = parts.length; i < len; ++i) {
var name = parts[i].replace(']', '')
if (i == len - 1)
prop[name] = params[key],
prop = params,
delete params[key]
else
prop = prop[name] = prop[name] || {}
}
return params
}
/**
* Parse params _string_ into a nested hash.
*
* @param {string} string
* @return {hash}
* @api public
*/
exports.parseParams = function(string) {
return exports.parseNestedParams($(string.split('&')).reduce({}, function(params, pair){
pair = pair.split('=')
params[pair[0]] = pair[1]
return params
}))
}
+94
Ver Arquivo
@@ -0,0 +1,94 @@
// Express - HTTP - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var http = require('http'),
utils = require('express/utils'),
parse = require('url').parse,
queryString = require('querystring')
/**
* Mega super awesome private request utility.
*
* @param {string} method
* @param {string} url
* @param {hash} data
* @param {hash} headers
* @param {function} fn
* @param {number} redirects
* @api private
*/
function request(method, url, data, headers, fn, redirects) {
var buf = '',
redirects = redirects || 3,
url = parse(url),
path = url.pathname || '/',
search = url.search || '',
hash = url.hash || '',
port = url.port || 80,
headers = { host: url.hostname }.merge(headers || {}),
client = http.createClient(port, url.hostname)
if (headers.redirect)
redirects = headers.redirect,
delete headers.redirect
if (data) {
data = queryString.stringify(data)
if (method === 'GET')
search += (search ? '&' : '?') + data
else
headers['content-length'] = data.length,
headers['content-type'] = 'application/x-www-form-urlencoded'
}
var req = client.request(method, path + search + hash, headers)
if (data && method !== 'GET') req.write(data)
req.addListener('response', function(res){
if (req.statusCode < 200 || req.statusCode >= 400)
fn(new Error('request failed with status ' + res.statusCode + ' "' + http.STATUS_CODES[res.statusCode] + '"'))
else if (res.statusCode >= 300 && res.statusCode < 400)
if (--redirects)
request(method, res.headers.location, headers, data, fn, redirects)
else
fn(new Error('maximum number of redirects reached'))
else {
res.setBodyEncoding('utf8')
res
.addListener('data', function(chunk){ buf += chunk })
.addListener('end', function(){ fn(null, buf, res) })
}
})
req.close()
}
/**
* Return HTTP Client function for the given _method_.
*
* @param {string} method
* @return {function}
* @api private
*/
function client(method) {
return function() {
var headers, data,
args = Array.prototype.slice.call(arguments),
url = args.shift(),
fn = args.pop(),
data = args.shift(),
headers = args.shift()
if (typeof fn !== 'function')
throw new TypeError('http client requires a callback function')
return request(method.toUpperCase(), url, data, headers, fn)
}
}
// --- Public API
exports.get = exports.view = client('get')
exports.post = exports.create = client('post')
exports.put = exports.update = client('put')
exports.del = exports.destroy = client('delete')
+182 -9
Ver Arquivo
@@ -1,136 +1,278 @@
// Express - Mime - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var types = {
/**
* Module dependencies.
*/
var extname = require('path').extname
/**
* Mime type lookup table.
*/
exports.types = {
'323' : 'text/h323',
'3gp' : 'video/3gpp',
'a' : 'application/octet-stream',
'acx' : 'application/internet-property-stream',
'ai' : 'application/postscript',
'aif' : 'audio/x-aiff',
'aifc' : 'audio/x-aiff',
'aiff' : 'audio/x-aiff',
'asc' : 'application/pgp-signature',
'asf' : 'video/x-ms-asf',
'asr' : 'video/x-ms-asf',
'asm' : 'text/x-asm',
'asx' : 'video/x-ms-asf',
'atom' : 'application/atom+xml',
'au' : 'audio/basic',
'avi' : 'video/x-msvideo',
'axs' : 'application/olescript',
'bas' : 'text/plain',
'bat' : 'application/x-msdownload',
'bcpio' : 'application/x-bcpio',
'bin' : 'application/octet-stream',
'bmp' : 'image/bmp',
'bz2' : 'application/x-bzip2',
'c' : 'text/x-c',
'cab' : 'application/vnd.ms-cab-compressed',
'cat' : 'application/vnd.ms-pkiseccat',
'cc' : 'text/x-c',
'cdf' : 'application/x-netcdf',
'cer' : 'application/x-x509-ca-cert',
'cgm' : 'image/cgm',
'chm' : 'application/vnd.ms-htmlhelp',
'class' : 'application/octet-stream',
'clp' : 'application/x-msclip',
'cmx' : 'image/x-cmx',
'cod' : 'image/cis-cod',
'com' : 'application/x-msdownload',
'conf' : 'text/plain',
'cpio' : 'application/x-cpio',
'cpp' : 'text/x-c',
'cpt' : 'application/mac-compactpro',
'crd' : 'application/x-mscardfile',
'crl' : 'application/pkix-crl',
'crt' : 'application/x-x509-ca-cert',
'csh' : 'application/x-csh',
'css' : 'text/css',
'csv' : 'text/csv',
'cxx' : 'text/x-c',
'dcr' : 'application/x-director',
'deb' : 'application/x-debian-package',
'der' : 'application/x-x509-ca-cert',
'diff' : 'text/x-diff',
'dir' : 'application/x-director',
'djv' : 'image/vnd.djvu',
'djvu' : 'image/vnd.djvu',
'dll' : 'application/x-msdownload',
'dmg' : 'application/octet-stream',
'dms' : 'application/octet-stream',
'doc' : 'application/msword',
'dot' : 'application/msword',
'dtd' : 'application/xml-dtd',
'dv' : 'video/x-dv',
'dvi' : 'application/x-dvi',
'dxr' : 'application/x-director',
'ear' : 'application/java-archive',
'eml' : 'message/rfc822',
'eps' : 'application/postscript',
'etx' : 'text/x-setext',
'evy' : 'application/envoy',
'exe' : 'application/x-msdownload',
'ez' : 'application/andrew-inset',
'f' : 'text/x-fortran',
'f77' : 'text/x-fortran',
'f90' : 'text/x-fortran',
'fif' : 'application/fractals',
'flr' : 'x-world/x-vrml',
'flv' : 'video/x-flv',
'for' : 'text/x-fortran',
'gem' : 'application/octet-stream',
'gemspec' : 'text/x-script.ruby',
'gif' : 'image/gif',
'gram' : 'application/srgs',
'grxml' : 'application/srgs+xml',
'gtar' : 'application/x-gtar',
'gz' : 'application/x-gzip',
'h' : 'text/x-c',
'hdf' : 'application/x-hdf',
'hh' : 'text/x-c',
'hlp' : 'application/winhlp',
'hqx' : 'application/mac-binhex40',
'hta' : 'application/hta',
'htc' : 'text/x-component',
'htm' : 'text/html',
'html' : 'text/html',
'htt' : 'text/webviewhtml',
'ice' : 'x-conference/x-cooltalk',
'ico' : 'image/vnd.microsoft.icon',
'ics' : 'text/calendar',
'ief' : 'image/ief',
'ifb' : 'text/calendar',
'iges' : 'model/iges',
'igs' : 'model/iges',
'iii' : 'application/x-iphone',
'ins' : 'application/x-internet-signup',
'isp' : 'application/x-internet-signup',
'iso' : 'application/octet-stream',
'jar' : 'application/java-archive',
'java' : 'text/x-java-source',
'jfif' : 'image/pipeg',
'jnlp' : 'application/x-java-jnlp-file',
'jp2' : 'image/jp2',
'jpe' : 'image/jpeg',
'jpeg' : 'image/jpeg',
'jpg' : 'image/jpeg',
'js' : 'application/javascript',
'json' : 'application/json',
'kar' : 'audio/midi',
'latex' : 'application/x-latex',
'lha' : 'application/octet-stream',
'lsf' : 'video/x-la-asf',
'lsx' : 'video/x-la-asf',
'lzh' : 'application/octet-stream',
'log' : 'text/plain',
'm13' : 'application/x-msmediaview',
'm14' : 'application/x-msmediaview',
'm3u' : 'audio/x-mpegurl',
'm4a' : 'audio/mp4a-latm',
'm4b' : 'audio/mp4a-latm',
'm4p' : 'audio/mp4a-latm',
'm4u' : 'video/vnd.mpegurl',
'm4v' : 'video/mp4',
'mac' : 'image/x-macpaint',
'man' : 'text/troff',
'mathml' : 'application/mathml+xml',
'mbox' : 'application/mbox',
'mdb' : 'application/x-msaccess',
'mdoc' : 'text/troff',
'me' : 'text/troff',
'mesh' : 'model/mesh',
'mht' : 'message/rfc822',
'mhtml' : 'message/rfc822',
'mid' : 'audio/midi',
'midi' : 'audio/midi',
'mif' : 'application/vnd.mif',
'mime' : 'message/rfc822',
'mml' : 'application/mathml+xml',
'mng' : 'video/x-mng',
'mny' : 'application/x-msmoney',
'mov' : 'video/quicktime',
'movie' : 'video/x-sgi-movie',
'mp2' : 'video/mpeg',
'mp3' : 'audio/mpeg',
'mp4' : 'video/mp4',
'mp4v' : 'video/mp4',
'mpa' : 'video/mpeg',
'mpe' : 'video/mpeg',
'mpeg' : 'video/mpeg',
'mpg' : 'video/mpeg',
'mpga' : 'audio/mpeg',
'mpp' : 'application/vnd.ms-project',
'mpv2' : 'video/mpeg',
'ms' : 'text/troff',
'msh' : 'model/mesh',
'msi' : 'application/x-msdownload',
'mvb' : 'application/x-msmediaview',
'mxu' : 'video/vnd.mpegurl',
'nc' : 'application/x-netcdf',
'nws' : 'message/rfc822',
'oda' : 'application/oda',
'odp' : 'application/vnd.oasis.opendocument.presentation',
'ods' : 'application/vnd.oasis.opendocument.spreadsheet',
'odt' : 'application/vnd.oasis.opendocument.text',
'ogg' : 'application/ogg',
'p' : 'text/x-pascal',
'p10' : 'application/pkcs10',
'p12' : 'application/x-pkcs12',
'p7b' : 'application/x-pkcs7-certificates',
'p7c' : 'application/x-pkcs7-mime',
'p7m' : 'application/x-pkcs7-mime',
'p7r' : 'application/x-pkcs7-certreqresp',
'p7s' : 'application/x-pkcs7-signature',
'pas' : 'text/x-pascal',
'pbm' : 'image/x-portable-bitmap',
'pct' : 'image/pict',
'pdb' : 'chemical/x-pdb',
'pdf' : 'application/pdf',
'pem' : 'application/x-x509-ca-cert',
'pfx' : 'application/x-pkcs12',
'pgm' : 'image/x-portable-graymap',
'pgn' : 'application/x-chess-pgn',
'pgp' : 'application/pgp-encrypted',
'pic' : 'image/pict',
'pict' : 'image/pict',
'pkg' : 'application/octet-stream',
'pko' : 'application/ynd.ms-pkipko',
'pl' : 'text/x-script.perl',
'pm' : 'text/x-script.perl-module',
'pma' : 'application/x-perfmon',
'pmc' : 'application/x-perfmon',
'pml' : 'application/x-perfmon',
'pmr' : 'application/x-perfmon',
'pmw' : 'application/x-perfmon',
'png' : 'image/png',
'pnm' : 'image/x-portable-anymap',
'pnt' : 'image/x-macpaint',
'pntg' : 'image/x-macpaint',
'pot' : 'application/vnd.ms-powerpoint',
'ppm' : 'image/x-portable-pixmap',
'pps' : 'application/vnd.ms-powerpoint',
'ppt' : 'application/vnd.ms-powerpoint',
'prf' : 'application/pics-rules',
'ps' : 'application/postscript',
'psd' : 'image/vnd.adobe.photoshop',
'pub' : 'application/x-mspublisher',
'py' : 'text/x-script.python',
'qt' : 'video/quicktime',
'qti' : 'image/x-quicktime',
'qtif' : 'image/x-quicktime',
'ra' : 'audio/x-pn-realaudio',
'rake' : 'text/x-script.ruby',
'ram' : 'audio/x-pn-realaudio',
'rar' : 'application/x-rar-compressed',
'ras' : 'image/x-cmu-raster',
'rb' : 'text/x-script.ruby',
'rdf' : 'application/rdf+xml',
'rgb' : 'image/x-rgb',
'rm' : 'application/vnd.rn-realmedia',
'rmi' : 'audio/mid',
'roff' : 'text/troff',
'rpm' : 'application/x-redhat-package-manager',
'rss' : 'application/rss+xml',
'rtf' : 'application/rtf',
'rtx' : 'text/richtext',
'ru' : 'text/x-script.ruby',
's' : 'text/x-asm',
'scd' : 'application/x-msschedule',
'sct' : 'text/scriptlet',
'setpay' : 'application/set-payment-initiation',
'setreg' : 'application/set-registration-initiation',
'sgm' : 'text/sgml',
'sgml' : 'text/sgml',
'sh' : 'application/x-sh',
'shar' : 'application/x-shar',
'sig' : 'application/pgp-signature',
'silo' : 'model/mesh',
'sit' : 'application/x-stuffit',
'skd' : 'application/x-koan',
'skm' : 'application/x-koan',
'skp' : 'application/x-koan',
'skt' : 'application/x-koan',
'smi' : 'application/smil',
'smil' : 'application/smil',
'snd' : 'audio/basic',
'so' : 'application/octet-stream',
'spc' : 'application/x-pkcs7-certificates',
'spl' : 'application/x-futuresplash',
'src' : 'application/x-wais-source',
'sst' : 'application/vnd.ms-pkicertstore',
'stl' : 'application/vnd.ms-pkistl',
'stm' : 'text/html',
'sv4cpio' : 'application/x-sv4cpio',
'sv4crc' : 'application/x-sv4crc',
'svg' : 'image/svg+xml',
'svgz' : 'image/svg+xml',
'swf' : 'application/x-shockwave-flash',
@@ -142,30 +284,60 @@ var types = {
'texi' : 'application/x-texinfo',
'texinfo' : 'application/x-texinfo',
'text' : 'text/plain',
'tgz' : 'application/x-compressed',
'tif' : 'image/tiff',
'tiff' : 'image/tiff',
'torrent' : 'application/x-bittorrent',
'tr' : 'text/troff',
'trm' : 'application/x-msterminal',
'tsv' : 'text/tab-seperated-values',
'txt' : 'text/plain',
'uls' : 'text/iuls',
'ustar' : 'application/x-ustar',
'vcd' : 'application/x-cdlink',
'vcf' : 'text/x-vcard',
'vcs' : 'text/x-vcalendar',
'vrml' : 'model/vrml',
'vxml' : 'application/voicexml+xml',
'war' : 'application/java-archive',
'wav' : 'audio/x-wav',
'wbmp' : 'image/vnd.wap.wbmp',
'wbxml' : 'application/vnd.wap.wbxml',
'wcm' : 'application/vnd.ms-works',
'wdb' : 'application/vnd.ms-works',
'wks' : 'application/vnd.ms-works',
'wma' : 'audio/x-ms-wma',
'wmf' : 'application/x-msmetafile',
'wml' : 'text/vnd.wap.wml',
'wmls' : 'text/vnd.wap.wmlscript',
'wmlsc' : 'application/vnd.wap.wmlscriptc',
'wmv' : 'video/x-ms-wmv',
'wmx' : 'video/x-ms-wmx',
'wps' : 'application/vnd.ms-works',
'wri' : 'application/x-mswrite',
'wrl' : 'model/vrml',
'wrz' : 'x-world/x-vrml',
'wsdl' : 'application/wsdl+xml',
'xaf' : 'x-world/x-vrml',
'xbm' : 'image/x-xbitmap',
'xht' : 'application/xhtml+xml',
'xhtml' : 'application/xhtml+xml',
'xla' : 'application/vnd.ms-excel',
'xlc' : 'application/vnd.ms-excel',
'xlm' : 'application/vnd.ms-excel',
'xls' : 'application/vnd.ms-excel',
'xlt' : 'application/vnd.ms-excel',
'xml' : 'application/xml',
'xof' : 'x-world/x-vrml',
'xpm' : 'image/x-xpixmap',
'xsl' : 'application/xml',
'xslt' : 'application/xslt+xml',
'xul' : 'application/vnd.mozilla.xul+xml',
'xwd' : 'image/x-xwindowdump',
'xyz' : 'chemical/x-xyz',
'yaml' : 'text/yaml',
'yml' : 'text/yaml',
'z' : 'application/x-compress',
'zip' : 'application/zip'
}
@@ -178,10 +350,11 @@ var types = {
* however this can be altered using the 'default mime type'
* setting.
*
* mime('png') // => 'image/png'
* mime('.png') // => 'image/png'
* mime('image.png') // => 'image/png'
* mime('path/to/image.png') // => 'image/png'
* var mime = require('express/mime')
* mime.type('png') // => 'image/png'
* mime.type('.png') // => 'image/png'
* mime.type('image.png') // => 'image/png'
* mime.type('path/to/image.png') // => 'image/png'
*
* @param {string} path
* @return {string}
@@ -189,9 +362,9 @@ var types = {
* @api public
*/
exports.mime = function(path) {
return types[path] ||
types[extname(path)] ||
exports.type = function(path) {
return exports.types[path] ||
exports.types[extname(path).substr(1)] ||
set('default mime type') ||
'application/octet-stream'
}
}
+1 -1
Ver Arquivo
@@ -6,7 +6,7 @@ var style = require('express/pages/style').style
exports.render = function(request) {
request.contentType('html')
var method = request.method.toLowerCase(),
path = request.uri.path || '/'
path = request.url.pathname || '/'
return '<html> \n\
<head> \n\
<title>Express -- Not Found</title> \n\
+6 -6
Ver Arquivo
@@ -13,7 +13,7 @@ var style = require('express/pages/style').style
function stack(e) {
if (e.stack)
return $(e.stack.split('\n').slice(1)).map(function(val, i){
return e.stack.split('\n').slice(1).map(function(val, i){
if (!i)
return '<li>' + val.replace(/^(.*?):/, '<span class="path">$1</span>:') + '</li>'
return '<li>' + val
@@ -32,8 +32,8 @@ function stack(e) {
*/
function hash(hash) {
if (!$(hash).length()) return '<tr><td class="empty" colspan="2">Empty</td></tr>'
return $(hash).map(function(val, key){
if (!hash || !hash.values.length) return '<tr><td class="empty" colspan="2">Empty</td></tr>'
return hash.map(function(val, key){
return '<tr><td>' + key + ':</td><td>' + JSON.encode(val) + '</td></tr>'
}).join('\n')
}
@@ -62,15 +62,15 @@ exports.render = function(request, e) {
</table> \n\
<h3>Params</h3> \n\
<table id="route-params"> \n\
' + hash(request.params) + ' \n\
' + hash(request.params.path) + ' \n\
</table> \n\
<h3>GET</h3> \n\
<table id="get-params"> \n\
' + hash(request.uri.params) + ' \n\
' + hash(request.params.get) + ' \n\
</table> \n\
<h3>POST</h3> \n\
<table id="post-params"> \n\
' + hash(request.uri.post) + ' \n\
' + hash(request.params.post) + ' \n\
</table> \n\
</div> \n\
</body> \n\
+17 -4
Ver Arquivo
@@ -1,8 +1,16 @@
// Express - Plugin - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var utils = require('express/utils');
/**
* Push _plugin_ with _options_ to the plugin stack.
* If _plugin_ has already been pushed, then it's options
* will override any previously set.
*
* @param {Plugin} plugin
* @param {hash} options
@@ -10,7 +18,12 @@
*/
exports.use = function(plugin, options) {
if ('init' in plugin) plugin.init()
if (Express.environment === 'test' && 'init' in plugin)
plugin.init(options)
Express.plugins.each(function(other, i){
if (other.klass === plugin)
delete Express.plugins[i]
})
Express.plugins.push({
klass: plugin,
options: options
@@ -30,7 +43,7 @@ exports.Plugin = Class({
init: function(options) {
if (options)
process.mixin(this, options)
utils.mixin(this, options)
},
/**
@@ -40,9 +53,9 @@ exports.Plugin = Class({
* @api private
*/
trigger: function(event) {
trigger: function(event, callback) {
if ('on' in this)
if (event.name in this.on)
this.on[event.name].call(this, event)
return this.on[event.name].call(this, event, callback)
}
})
+8 -6
Ver Arquivo
@@ -1,9 +1,11 @@
// Express - Plugins - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
process.mixin(require('express/plugins/hooks'))
process.mixin(require('express/plugins/cookie'))
process.mixin(require('express/plugins/profiler'))
process.mixin(require('express/plugins/common-logger'))
process.mixin(require('express/plugins/content-length'))
process.mixin(require('express/plugins/method-override'))
global.merge(require('express/plugins/hooks'))
global.merge(require('express/plugins/flash'))
global.merge(require('express/plugins/cache'))
global.merge(require('express/plugins/cookie'))
global.merge(require('express/plugins/session'))
global.merge(require('express/plugins/logger'))
global.merge(require('express/plugins/content-length'))
global.merge(require('express/plugins/method-override'))
+10 -2
Ver Arquivo
@@ -1,6 +1,14 @@
// Express - BodyDecoder - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var queryString = require('querystring')
// --- BodyDecoder
exports.BodyDecoder = Plugin.extend({
on: {
@@ -13,8 +21,8 @@ exports.BodyDecoder = Plugin.extend({
request: function(event) {
var request = event.request
if (request.header('content-type') &&
request.header('content-type').indexOf('application/x-www-form-urlencoded') > -1)
request.params.post = parseParams(request.body)
request.header('content-type').includes('application/x-www-form-urlencoded'))
request.params.post = queryString.parseQuery(request.body)
}
}
})
+220
Ver Arquivo
@@ -0,0 +1,220 @@
// Express - Cache - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
// --- Cache
var Cache = Class({
/**
* Initialize cache with _key_ and _val_.
*/
init: function(key, val) {
this.key = key
this.val = val
this.created = Number(new Date)
}
})
// --- Store
exports.Store = Class({
/**
* Ensure that the given _key_ is a string.
* Override in subclass to provide data-store specific functionality.
*
* @param {string} key
* @param {string} val
* @api public
*/
set: function(key, val) {
if (typeof key !== 'string')
throw new Error(this.name + ' store #set() key must be a string')
},
/**
* Ensure that the given _key_ is a string.
* Override in subclass to provide data-store specific functionality.
*
* @param {string} key
* @api public
*/
get: function(key) {
if (typeof key !== 'string')
throw new Error(this.name + 'store #get() key must be a string')
},
/**
* Convert to '[NAME Store]'.
*
* @return {string}
* @api public
*/
toString: function() {
return '[' + this.name + ' Store]'
}
})
// --- Store.Memory
exports.Store.Memory = exports.Store.extend({
/**
* Datastore name.
*/
name: 'Memory',
/**
* Initialize data.
*/
init: function() {
this.data = {}
},
/**
* Set the given _key_ to _val_, returning _val_.
*
* @param {string} key
* @param {string} val
* @return {string}
* @api public
*/
set: function(key, val) {
this.__super__(key, val)
return this.data[key] = new Cache(key, val), val
},
/**
* Get data found matching the given _key_.
*
* Examples:
*
* cache.get('page:front')
* // => '<html>...</html>'
*
* cache.get('page:*')
* // => { 'page:front': '<html>...</html>',
* 'page:users': '<html>...</html>',
* ... }
*
* @param {string} key
* @return {mixed}
* @api public
*/
get: function(key) {
this.__super__(key)
if (key.indexOf('*') === -1)
return this.data[key] instanceof Cache ?
this.data[key].val :
null
var regexp = this.normalize(key)
return this.data.reduce(function(vals, cache){
if (regexp.test(cache.key))
vals[cache.key] = cache.val
return vals
}, {})
},
/**
* Clear data matching the given _key_.
*
* Examples:
*
* cache.clear('page:front')
* cache.clear('page:*')
*
* @param {string} key
* @api public
*/
clear: function(key) {
if (key.indexOf('*') === -1)
return delete this.data[key]
var regexp = this.normalize(key)
for (var key in this.data)
if (this.data.hasOwnProperty(key))
if (regexp.test(key))
delete this.data[key]
},
/**
* Reap caches older than _ms_.
*
* @param {int} ms
* @api private
*/
reap: function(ms) {
var self = this,
threshold = Number(new Date(Number(new Date) - ms))
this.data.each(function(cache){
if (cache.created < threshold)
self.clear(cache.key)
})
},
/**
* Convert the given key matching _pattern_
* into a RegExp.
*
* - * is converted to (.*?)
*
* @param {string} pattern
* @return {regexp}
* @api private
*/
normalize: function(pattern) {
return new RegExp('^' + pattern.replace(/[*]/g, '(.*?)') + '$')
}
})
// --- Cache
exports.Cache = Plugin.extend({
extend: {
/**
* Initialize memory store and start reaper.
*
* Options:
*
* - dataStore constructor name of cache data store, defaults to Store.Memory
* - lifetime lifetime of cache in milliseconds, defaults to one day
* - reapInterval, reapEvery interval in milliseconds in which to reap old caches, defaults to one hour
*
* @param {hash} options
* @api private
*/
init: function(options) {
this.merge(options || {})
this.store = new (this.dataStore || exports.Store.Memory)(options)
Request.include({ cache: this.store })
this.startReaper()
},
/**
* Start reaper.
*
* @api private
*/
startReaper: function() {
var self = this,
oneDay = 86400000,
oneHour = 3600000
setInterval(function(){
self.store.reap(self.lifetime || oneDay)
}, self.reapInterval || self.reapEvery || oneHour)
}
}
})
-50
Ver Arquivo
@@ -1,50 +0,0 @@
// Express - CommonLogger - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Months.
*/
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
/**
* Format _date_.
*
* @param {Date} date
* @return {string}
* @api private
*/
function format(date) {
var d = date.getDate(),
m = months[date.getMonth()],
y = date.getFullYear(),
h = date.getHours(),
mi = date.getMinutes(),
s = date.getSeconds()
return (d < 10 ? '0' : '') + d + '/' + m + '/' + y + ' ' +
(h < 10 ? '0' : '') + h + ':' + (mi < 10 ? '0' : '') +
mi + ':' + (s < 10 ? '0' : '') + s
}
// --- CommonLogger
exports.CommonLogger = Plugin.extend({
on: {
/**
* Output log data.
*/
response: function(event) {
puts([event.request.headers.host,
'-',
'-',
'[' + format(new Date) + ']',
'"' + event.request.method.toUpperCase() + ' ' + (event.request.uri.path || '/') +
' HTTP/' + event.request.httpVersion + '"',
event.request.response.status,
event.request.response.headers['content-length'] || 0].join(' '))
}
}
})
+14 -11
Ver Arquivo
@@ -10,11 +10,11 @@
*/
exports.parseCookie = function(cookie) {
return $(cookie.replace(/^ *| *$/g, '').split(/ *; */)).reduce({}, function(hash, pair){
return cookie.replace(/^ *| *$/g, '').split(/ *; */).reduce(function(hash, pair){
var parts = pair.split(/ *= */)
hash[parts[0]] = parts[1]
return hash
})
}, {})
}
/**
@@ -29,14 +29,14 @@ exports.parseCookie = function(cookie) {
exports.compileCookie = function(name, val, options) {
if (!options) return name + '=' + val
return name + '=' + val + '; ' + $(options).map(function(val, key){
return name + '=' + val + '; ' + options.map(function(val, key){
if (val instanceof Date)
val = val.toString()
.replace(/^(\w+)/, '$1,')
.replace(/(\w+) (\d+) (\d+)/, '$2-$1-$3')
.replace(/GMT.*$/, 'GMT')
return val === true ? key : key + '=' + val
}).toArray().join('; ')
}).join('; ')
}
// --- Cookie
@@ -56,12 +56,12 @@ exports.Cookie = Plugin.extend({
*
* Options:
*
* - path: Cookie path, defaults to '/'
* - domain: Tail matched domain name such as 'vision-media.ca' or 'blog.vision-media.ca' etc
* - expires: Date object converted to 'Wdy, DD-Mon-YYYY HH:MM:SS GMT'
* - path Cookie path, defaults to '/'
* - domain Tail matched domain name such as 'vision-media.ca' or 'blog.vision-media.ca' etc
* - expires Date object converted to 'Wdy, DD-Mon-YYYY HH:MM:SS GMT'
* when undefined the cookie will last the duration of a the
* client's session.
* - secure: When true the cookie will be sent by the client only when transfering data via HTTPS
* - secure When true the cookie will be sent by the client only when transfering data via HTTPS
* - httpOnly When true the cookie will be sent to the server only and will not be accessable via
* client-side scripting.
*
@@ -73,6 +73,8 @@ exports.Cookie = Plugin.extend({
*/
cookie: function(name, val, options) {
options = options || {}
options.path = options.path || '/'
return val ?
this.response.cookies.push(exports.compileCookie(name, val, options)) :
this.cookies[name]
@@ -86,13 +88,14 @@ exports.Cookie = Plugin.extend({
on: {
/**
* Parser request cookie data.
* Parse request cookie data.
*/
request: function(event) {
event.request.response.cookies = []
if (event.request.headers.cookie)
event.request.cookies = exports.parseCookie(event.request.headers.cookie)
event.request.cookies = event.request.headers.cookie
? exports.parseCookie(event.request.headers.cookie)
: {}
},
/**
+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
}
}
})
}
}
})
+15 -9
Ver Arquivo
@@ -1,8 +1,11 @@
// Express - Hooks - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var before = [],
after = []
/**
* Callbacks.
*/
exports.callbacks = { before: [], after: [] }
/**
* Add a _fn_ to be excuted before a request.
@@ -12,7 +15,7 @@ var before = [],
*/
exports.before = function(fn) {
before.push(fn)
exports.callbacks.before.push(fn)
}
/**
@@ -23,7 +26,7 @@ exports.before = function(fn) {
*/
exports.after = function(fn) {
after.push(fn)
exports.callbacks.after.push(fn)
}
// --- Hooks
@@ -36,7 +39,10 @@ exports.Hooks = Plugin.extend({
*/
init: function() {
process.mixin(GLOBAL, exports)
global.merge({
before: exports.before,
after: exports.after
})
}
},
@@ -49,8 +55,8 @@ exports.Hooks = Plugin.extend({
*/
request: function(event) {
$(before).each(function(fn){
fn.call(event.request)
exports.callbacks.before.each(function(fn){
fn.call(event.request, event.request)
})
},
@@ -59,8 +65,8 @@ exports.Hooks = Plugin.extend({
*/
response: function(event) {
$(after).each(function(fn){
fn.call(event.request)
exports.callbacks.after.each(function(fn){
fn.call(event.request, event.request)
})
}
}
+80
Ver Arquivo
@@ -0,0 +1,80 @@
// Express - Logger - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var sys = require('sys')
/**
* Log formats
*/
var formats = {
common: function(event, start) {
printf('%s - - [%s] "%s %s HTTP/%d" %s %d %0.3f',
event.request.connection.remoteAddress,
(new Date).format('%d/%b/%Y %H:%M:%S'),
event.request.method.uppercase,
event.request.url.pathname || '/',
event.request.httpVersion,
event.request.response.status,
event.request.response.headers['content-length'] || 0,
(Number(new Date) - start) / 1000)
},
combined: function(event, start) {
formats.common(event, start)
printf('"%s" "%s"',
event.request.headers['referrer'] || event.request.headers['referer'] || '-',
event.request.headers['user-agent'])
},
plot: function(event, start) {
sys.print(Number(new Date) - start)
}
}
// --- Logger
exports.Logger = Plugin.extend({
extend: {
/**
* Initialize logger options.
*
* Options:
*
* - format
* 'common' outputs log in CommonLog format (DEFAULT)
* 'combined' outputs log in Apache Combined format
* 'plot' outputs request duration in milliseconds only
*
* @param {hash} options
* @api private
*/
init: function(options) {
this.merge(options || {})
}
},
on: {
/**
* Start timer.
*/
request: function(event) {
this.start = Number(new Date)
},
/**
* Output log data.
*/
response: function(event) {
formats[exports.Logger.format || 'common'](event, this.start)
sys.print('\n')
}
}
})
-25
Ver Arquivo
@@ -1,25 +0,0 @@
// Express - Profiler - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
exports.Profiler = Plugin.extend({
on: {
/**
* Start timer.
*/
request: function(event) {
this.start = Number(new Date)
},
/**
* Output duration.
*/
response: function(event) {
puts(event.request.method + ' ' +
event.request.uri.path + ': ' +
(Number(new Date) - this.start) + ' ms')
}
}
})
+5 -5
Ver Arquivo
@@ -12,7 +12,7 @@ exports.Redirect = Plugin.extend({
Request.include({
/**
* Redirect to _uri_ with optional status _code_,
* Redirect to _url_ with optional status _code_,
* defaulting to 303 "See Other".
*
* When using redirect('home') the resolution
@@ -26,14 +26,14 @@ exports.Redirect = Plugin.extend({
* header is used. Commonly misspelled as 'referer'
* which is supported as well.
*
* @param {string} uri
* @param {string} url
* @settings 'home', 'basepath'
* @api public
*/
redirect: function(uri, code) {
if (uri == 'back' || uri == 'home') uri = this[uri]
this.header('location', uri)
redirect: function(url, code) {
if (url === 'back' || url === 'home') url = this[url]
this.header('location', url)
this.halt(code || 303)
}
})
+201
Ver Arquivo
@@ -0,0 +1,201 @@
// Express - Session - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var utils = require('express/utils')
// --- Session
var Session = Class({
/**
* Initialize session _sid_.
*/
init: function(sid) {
this.id = sid
this.touch()
},
/**
* Update last access time.
*
* @api private
*/
touch: function() {
this.lastAccess = Number(new Date)
}
})
// --- Store
exports.Store = Class({
/**
* Convert to '[NAME Store]'.
*
* @return {string}
* @api public
*/
toString: function() {
return '[' + this.name + ' Store]'
}
})
// --- Store.Memory
exports.Store.Memory = exports.Store.extend({
/**
* Datastore name.
*/
name: 'Memory',
/**
* Initialize in-memory session store.
*/
init: function() {
this.store = {}
},
/**
* Fetch session with the given _sid_ or
* a new Session is returned.
*
* @param {int} sid
* @return {Session}
* @api private
*/
fetch: function(sid) {
return this.store[sid] || new Session(sid)
},
/**
* Commit _session_ data.
*
* @param {Session} session
* @api private
*/
commit: function(session) {
return this.store[session.id] = session
},
/**
* Clear all sessions.
*
* @api public
*/
clear: function() {
this.store = {}
},
/**
* Destroy session using the given _sid_.
*
* @param {int} sid
* @api public
*/
destroy: function(sid) {
delete this.store[sid]
},
/**
* Return the number of sessions currently stored.
*
* @return {int}
* @api public
*/
length: function() {
return this.store.values.length
},
/**
* Reap sessions older than _ms_.
*
* @param {int} ms
* @api private
*/
reap: function(ms) {
var threshold = Number(new Date(Number(new Date) - ms))
this.store.each(function(session, sid){
if (session.lastAccess < threshold)
this.destroy(sid)
}, this)
}
})
// --- Session
exports.Session = Plugin.extend({
extend: {
/**
* Initialize memory store and start reaper.
*
* Options:
*
* - dataStore   constructor name of session data store, defaults to Store.Memory
* - lifetime lifetime of session in milliseconds, defaults to one day
* - reapInterval, reapEvery interval in milliseconds in which to reap old sessions, defaults to one hour
*
* @param {hash} options
* @api private
*/
init: function(options) {
this.merge(options || {})
this.store = new (this.dataStore || exports.Store.Memory)(options)
this.startReaper()
},
/**
* Start reaper.
*
* @api private
*/
startReaper: function() {
setInterval(function(){
this.store.reap(this.lifetime || (1).day)
}, this.reapInterval || this.reapEvery || (1).hour, this)
}
},
// --- Events
on: {
/**
* Create session id when not found; delegate to store.
*/
request: function(event) {
var sid
if (!(sid = event.request.cookie('sid')))
event.request.cookie('sid', sid = utils.uid(), set('session cookie'))
event.request.session = exports.Session.store.fetch(sid)
event.request.session.touch()
},
/**
* Delegate to store, allowing it to save sessions changes.
*/
response: function(event) {
exports.Session.store.commit(event.request.session)
}
}
})
+21 -21
Ver Arquivo
@@ -1,22 +1,19 @@
// Express - View - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var posix = require('posix')
/**
* Template content cache.
* Module dependencies.
*/
var cache = {}
var utils = require('express/utils'),
extname = require('path').extname,
fs = require('fs')
/**
* Supported template engines.
* Cache supported template engine exports.
*/
var engine = {
ejs: require('support/ejs/ejs'),
haml: require('support/haml/haml')
}
var engines = {}
// --- View
@@ -51,13 +48,14 @@ 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
* @settings 'views', 'cache views'
* @settings 'views', 'cache view contents'
* @api public
*/
@@ -67,12 +65,13 @@ exports.View = Plugin.extend({
path = set('views') + '/' + view,
type = path.split('.').slice(-2)[0],
ext = 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)
content = (engines[type] = engines[type] || require(type)).render(content, options)
if (layout)
self.render('layout.' + type + '.' + ext, process.mixin(options, {
self.render(layout + '.' + type + ext, utils.mixin(true, options, {
layout: false,
locals: {
body: content
@@ -81,13 +80,14 @@ exports.View = Plugin.extend({
else
self.halt(200, content)
}
if (set('cache views') && cache[view])
render(cache[view])
if (set('cache view contents') && self.cache.get(path))
render(self.cache.get(path))
else
posix.cat(path).addCallback(function(content){
render(cache[view] = content)
}).addErrback(function(e){
throw e
fs.readFile(path, function(e, content){
if (e) throw e
set('cache view contents')
? render(self.cache.set(path, content))
: render(content)
})
}
})
+106 -79
Ver Arquivo
@@ -2,34 +2,15 @@
// Express - Request - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* StaticFile for Request#sendfile()
* Module dependencies.
*/
var StaticFile = require('express/static').File
/**
* Default response code bodies.
*/
var statusBodies = require('http').STATUS_CODES
// --- InvalidStatusCode
InvalidStatusCode = ExpressError.extend({
name: 'InvalidStatusCode',
init: function(status) {
this.message = status + ' is an invalid HTTP response code'
}
})
// --- InvalidResponseBody
InvalidResponseBody = ExpressError.extend({
name: 'InvalidResponseBody',
init: function(request) {
this.message = request.method + ' ' + JSON.encode(request.uri.path) + ' did not respond with a body string'
}
})
var StaticFile = require('express/static').File,
statusBodies = require('http').STATUS_CODES,
queryString = require('querystring'),
mime = require('express/mime'),
utils = require('express/utils'),
url = require('url')
// --- Helpers
@@ -53,22 +34,28 @@ exports.Request = Class({
/**
* Initialize with node's _request_ and _response_ objects.
*
* - Defaults headers to {}
* - Parses request url
* - Normalizes url pathname
* - Parses GET params when available
* - Initializes plugins
*
* @param {object} request
* @param {object} response
* @api private
*/
init: function(request, response) {
process.mixin(true, this, request)
utils.mixin(true, this, request)
response.headers = {}
this.response = response
this.uri.path = exports.normalizePath(this.uri.path)
this.params = {
get: parseNestedParams(this.uri.params),
post: {},
path: {}
}
this.plugins = $(Express.plugins).map(function(plugin){
this.url = url.parse(this.url)
this.url.pathname = exports.normalizePath(this.url.pathname)
this.params = this.params || {}
this.params.path = {}
this.params.get = this.url.query ? queryString.parseQuery(this.url.query) : {}
this.params.post = this.params.post || {}
this.plugins = Express.plugins.map(function(plugin){
return new plugin.klass(plugin.options)
})
},
@@ -84,9 +71,9 @@ exports.Request = Class({
*/
header: function(key, val) {
return val === undefined ?
this.headers[key.toLowerCase()] :
this.response.headers[key.toLowerCase()] = val
return val === undefined
? this.headers[key.toLowerCase()]
: this.response.headers[key.toLowerCase()] = val
},
/**
@@ -109,21 +96,32 @@ exports.Request = Class({
/**
* Check if Accept header includes the mime type
* for the given _path_, which calls mime().
* 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}
* @see mime()
* @api public
*/
accepts: function(path) {
return this.header('accept') ?
this.header('accept').indexOf(mime(path)) !== -1 :
true
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
},
/**
@@ -145,9 +143,6 @@ exports.Request = Class({
* default to the default body associated with the response
* _code_.
*
* When an invalid status _code_ is passed, InvalidStatusCode
* will be thrown.
*
* @param {int} code
* @param {string} body
* @param {string} encoding
@@ -155,11 +150,10 @@ exports.Request = Class({
* @api public
*/
halt: function(code, body, encoding) {
halt: function(code, body, encoding, callback) {
this.status(code = code || 404)
if (body = body || statusBodies[code])
return this.respond(body, encoding)
throw new InvalidStatusCode(code)
return this.respond(body, encoding, callback)
},
/**
@@ -171,48 +165,81 @@ exports.Request = Class({
* @api private
*/
respond: function(body, encoding) {
respond: function(body, encoding, callback) {
var self = this
this.response.body = body
this.trigger('response')
if (typeof this.response.body != 'string') throw new InvalidResponseBody(this)
if (typeof this.response.status != 'number') throw new InvalidStatusCode(this.response.status)
this.response.sendHeader(this.response.status, this.response.headers)
this.response.sendBody(this.response.body, encoding)
this.response.finish()
this.trigger('response', function(e) {
if (e)
if (callback !== undefined) callback(e)
else throw e
self.response.writeHeader(self.response.status, self.response.headers)
self.response.write(self.response.body, encoding)
self.response.close()
});
},
/**
* Set Content-Type header to the mime type
* for the given _path_, which calls mime().
* Pass control to the next matching route, or
* the given _path_.
*
* @param {string} path
* @return {Request}
* @see mime()
* NOTE: _path_ may be the request pathname only,
* and may not contain a query string etc.
*
* @param {string} path
* @api public
*/
contentType: function(path) {
this.header('content-type', mime(path))
pass: function(path) {
this.passed = path || true
return this
},
/**
* Trigger even _name_ with optional _data_.
* Set Content-Type header to the mime type
* for the given _path_, which calls mime.type().
*
* @param {string} name
* @param {hash} data
* @param {string} path
* @return {Request}
* @api public
*/
trigger: function(name, data) {
data = process.mixin(data || {}, {
request: this,
response: this.response
})
this.plugins.each(function(plugin){
plugin.trigger(new Event(name, data))
})
contentType: function(path) {
this.header('content-type', mime.type(path))
return this
},
/**
* Trigger event _name_ with optional _data_ and _callback_ function.
* The _callback_ function may be the second or third argument.
*
* @param {string} name
* @param {object} data
* @param {function} callback
* @return {Request}
* @api public
*/
trigger: function(name, data, callback) {
if (data instanceof Function)
callback = data,
data = null
data = data || {}
data.merge({ request: this, response: this.response })
var self = this,
complete = 0,
total = this.plugins.length
;(function next(e) {
if (e || complete === total)
callback(e)
else {
try {
if (self.plugins.at(complete++).trigger(new Event(name, data), next) !== true)
next()
} catch(e) {
next(e)
}
}
})()
return this
},
@@ -239,9 +266,9 @@ exports.Request = Class({
*/
attachment: function(path) {
this.header('content-disposition', path ?
'attachment; filename="' + path + '"' :
'attachment')
this.header('content-disposition', path
? 'attachment; filename="' + path + '"'
: 'attachment')
return this
},
+11 -11
Ver Arquivo
@@ -1,6 +1,12 @@
// Express - MockRequest - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var utils = require('express/utils')
// --- MockRequest
var MockRequest = Class({
@@ -22,16 +28,10 @@ var MockRequest = Class({
init: function(method, path, options) {
this.method = method
this.url = path
this.connection = {
remoteAddress: '127.0.0.1'
}
this.uri = {
queryString: '',
fragment: '',
params: {},
path: path,
full: path
}
this.headers = {
'host': 'localhost',
'user-agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19',
@@ -39,7 +39,7 @@ var MockRequest = Class({
'accept-language': 'en-us',
'connection': 'keep-alive'
}
process.mixin(true, this, options)
utils.mixin(true, this, options)
}
})
@@ -51,7 +51,7 @@ var MockResponse = Class({
* Store _code_ and _headers_.
*/
sendHeader: function(code, headers) {
writeHeader: function(code, headers) {
this.status = code
this.headers = headers
},
@@ -60,7 +60,7 @@ var MockResponse = Class({
* Store _body_.
*/
sendBody: function(body) {
write: function(body) {
this.body = body
},
@@ -68,7 +68,7 @@ var MockResponse = Class({
* Flag response as finished.
*/
finish: function() {
close: function() {
this.finished = true
}
})
+22 -17
Ver Arquivo
@@ -1,18 +1,13 @@
// Express - Static - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var path = require('path'),
posix = require('posix')
// --- InvalidPathError
/**
* Module dependencies.
*/
InvalidPathError = ExpressError.extend({
name: 'InvalidPathError',
init: function(path) {
this.message = "`" + path + "' is not a valid path"
}
})
var path = require('path'),
fs = require('fs')
// --- File
exports.File = Class({
@@ -26,7 +21,8 @@ exports.File = Class({
init: function(path) {
this.path = path
if (path.indexOf('..') != -1) throw new InvalidPathError(path)
if (path.indexOf('..') != -1)
Error.raise('InvalidPathError', "`" + path + "' is not a valid path")
},
/**
@@ -35,19 +31,28 @@ exports.File = Class({
* - Ensures the file exists
* - Ensures the file is a regular file (not FIFO, Socket, etc)
* - Automatically assigns content type
* - Halts with 404 when failing
*
* @param {Request} request
* @settings 'cache static files'
* @api public
*/
send: function(request) {
var file = this.path
var cache, file = this.path
if (set('cache static files') && (cache = request.cache.get(file)))
return request.contentType(cache.type),
request.halt(200, cache.content, 'binary')
path.exists(file, function(exists){
if (!exists) request.halt()
posix.stat(file).addCallback(function(stats){
if (!stats.isFile()) request.halt()
posix.cat(file, 'binary').addCallback(function(content){
if (!exists) return request.halt()
fs.stat(file, function(e, stats){
if (e) throw e
if (!stats.isFile()) return request.halt()
fs.readFile(file, 'binary', function(e, content){
if (e) throw e
request.contentType(file)
if (set('cache static files'))
request.cache.set(file, { type: file, content: content })
request.halt(200, content, 'binary')
})
})
+151
Ver Arquivo
@@ -0,0 +1,151 @@
// Express - Helpers - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var queryString = require('querystring')
/**
* JSON aliases.
*/
JSON.encode = JSON.stringify
JSON.decode = JSON.parse
/**
* Return a unique identifier.
*
* @return {string}
* @api public
*/
exports.uid = function() {
var uid = ''
for (var n = 4; n; --n)
uid += (Math.abs((Math.random() * 0xFFFFFFF) | 0)).toString(16)
return uid
}
/**
* Escape special characters in _html_.
*
* @param {string} html
* @return {string}
* @api public
*/
exports.escape = function(html) {
return String(html)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
}
/**
* Merge param _key_ and _val_ into _params_. Key
* should be a query string key such as 'user[name]',
* and _val_ is it's associated object. The root _params_
* object is returned.
*
* @param {string} key
* @param {mixed} val
* @return {hash}
* @api public
*/
exports.mergeParam = function(key, val, params) {
var orig = params,
keys = key.trim().match(/\w+/g),
array = /\[\]$/.test(key)
keys.reduce(function(parts, key, i){
if (i === keys.length - 1)
if (key in params)
params[key] instanceof Array
? params[key].push(val)
: params[key] = [params[key], val]
else
params[key] = array ? [val] : val
if (!(key in params)) params[key] = {}
params = params[key]
return parts[key]
}, queryString.parseQuery(key))
return orig
}
/**
* Return a clone of _obj_.
*
* @param {mixed} obj
* @return {mixed}
* @api public
*/
function Clone() {}
exports.clone = function(obj) {
Clone.prototype = obj
return new Clone
}
// From jQuery.extend in the jQuery JavaScript Library v1.3.2
// Copyright (c) 2009 John Resig
// Dual licensed under the MIT and GPL licenses.
// http://docs.jquery.com/License
// Modified for node.js (formely for copying properties correctly)
exports.mixin = function() {
// copy reference to target object
var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, source;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !(typeof target === 'function') )
target = {};
// mixin process itself if only one argument is passed
if ( length == i ) {
target = GLOBAL;
--i;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (source = arguments[i]) != null ) {
// Extend the base object
Object.getOwnPropertyNames(source).forEach(function(k){
var d = Object.getOwnPropertyDescriptor(source, k) || {value: source[k]};
if (d.get) {
target.__defineGetter__(k, d.get);
if (d.set) {
target.__defineSetter__(k, d.set);
}
}
else {
// Prevent never-ending loop
if (target !== d.value) {
if (deep && d.value && typeof d.value === "object") {
target[k] = exports.mixin(deep,
// Never move original objects, clone them
target[k] || (d.value.length != null ? [] : {})
, d.value);
}
else {
target[k] = d.value;
}
}
}
});
}
}
// Return the modified object
return target;
};
-53
Ver Arquivo
@@ -1,53 +0,0 @@
/* Ejs template parser for CommonJS
*
* Copyright (c) 2009, Howard Rauscher
* Licensed under the MIT License
*
* base on:
* Simple JavaScript Templating (http://ejohn.org/blog/javascript-micro-templating/)
* John Resig - http://ejohn.org/ - MIT Licensed
*/
(function(){
var cache = {};
var ejs = this.ejs = {};
ejs.parse = function tmpl(str, options) {
options = options || {};
options.context = options.context || {};
options.locals = options.locals || {};
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = cache[str] ||
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[];" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str
.replace(/\-%>(\n|\r)/g, "%>")
.replace(/[\t\b\f]/g, " ")
.replace(/[\n\r]/g, "\f")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'").replace(/\f+/g, '\\n') +
"');}return p.join('');");
cache[str] = fn;
// Provide some basic currying to the user
return fn.call(options.context, options.locals);
};
})();
exports.render = ejs.parse;
+1
Submodule lib/support/ext added at 967039b7d6
Ver Arquivo
+1
Submodule lib/support/sass added at 2a648b3766
+4
Ver Arquivo
@@ -0,0 +1,4 @@
---
name: Express
description: Sinatra inspired web development framework
version: 0.7.2
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 8.4 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 3.5 KiB

+2 -2
Ver Arquivo
@@ -114,12 +114,12 @@ body.jspec {
background: url(images/sprites.png) 3px -7px no-repeat;
}
#jspec-report td.fail {
background: url(images/sprites.png) 3px -47px no-repeat;
background: url(images/sprites.png) 3px -158px no-repeat;
font-weight: bold;
color: #FC0D0D;
}
#jspec-report td.requires-implementation {
background: url(images/sprites.png) 3px -87px no-repeat;
background: url(images/sprites.png) 3px -333px no-repeat;
}
#jspec-report tr.description td {
margin-top: 25px;
+115
Ver Arquivo
@@ -0,0 +1,115 @@
// JSpec - Growl - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
;(function(){
Growl = {
// --- Version
version: '1.0.0',
/**
* Execute the given _cmd_, returning an array of lines from stdout.
*
* Examples:
*
* Growl.exec('growlnotify', '-m', msg)
*
* @param {string ...} cmd
* @return {array}
* @api public
*/
exec: function(cmd) {
var lines = [], line
with (JavaImporter(java.lang, java.io)) {
var proccess = Runtime.getRuntime().exec(Array.prototype.slice.call(arguments))
var stream = new DataInputStream(proccess.getInputStream())
while (line = stream.readLine())
lines.push(line + '')
stream.close()
}
return lines
},
/**
* Return the extension of the given _path_ or null.
*
* @param {string} path
* @return {string}
* @api private
*/
extname: function(path) {
return path.lastIndexOf('.') != -1 ?
path.slice(path.lastIndexOf('.') + 1, path.length) :
null
},
/**
* Version of the 'growlnotify' binary.
*
* @return {string}
* @api private
*/
binVersion: function() {
try { return this.exec('growlnotify', '-v')[0].split(' ')[1] } catch (e) {}
},
/**
* Send growl notification _msg_ with _options_.
*
* Options:
*
* - title Notification title
* - sticky Make the notification stick (defaults to false)
* - name Application name (defaults to growlnotify)
* - image
* - path to an icon sets --iconpath
* - path to an image sets --image
* - capitalized word sets --appIcon
* - filename uses extname as --icon
* - otherwise treated as --icon
*
* Examples:
*
* Growl.notify('New email')
* Growl.notify('5 new emails', { title: 'Thunderbird' })
*
* @param {string} msg
* @param {options} hash
* @api public
*/
notify: function(msg, options) {
options = options || {}
var args = ['growlnotify', '-m', msg]
if (!this.binVersion()) throw new Error('growlnotify executable is required')
if (image = options.image) {
var flag, ext = this.extname(image)
flag = flag || ext == 'icns' && 'iconpath'
flag = flag || /^[A-Z]/.test(image) && 'appIcon'
flag = flag || /^png|gif|jpe?g$/.test(ext) && 'image'
flag = flag || ext && (image = ext) && 'icon'
flag = flag || 'icon'
args.push('--' + flag, image)
}
if (options.sticky) args.push('--sticky')
if (options.name) args.push('--name', options.name)
if (options.title) args.push(options.title)
this.exec.apply(this, args)
}
}
JSpec.include({
name: 'Growl',
reporting: function(options){
var stats = JSpec.stats
if (stats.failures) Growl.notify('failed ' + stats.failures + ' assertions', { title: 'JSpec'})
else Growl.notify('passed ' + stats.passes + ' assertions', { title: 'JSpec' })
}
})
})()
+1 -1
Ver Arquivo
@@ -47,7 +47,7 @@ JSpec
},
have_classes : function(actual) {
return !JSpec.any(JSpec.argumentsToArray(arguments, 1), function(arg){
return !JSpec.any(JSpec.toArray(arguments, 1), function(arg){
return !JSpec.does(actual, 'have_class', arg)
})
},
+102 -105
Ver Arquivo
@@ -1,20 +1,20 @@
// JSpec - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
(function(){
;(function(){
JSpec = {
version : '2.11.13',
version : '3.2.1',
assert : true,
cache : {},
suites : [],
modules : [],
allSuites : [],
matchers : {},
stubbed : [],
options : {},
request : 'XMLHttpRequest' in this ? XMLHttpRequest : null,
stats : { specs: 0, assertions: 0, failures: 0, passes: 0, specsFinished: 0, suitesFinished: 0 },
options : { profile: false },
/**
* Default context in which bodies are evaluated.
@@ -51,15 +51,12 @@
},
/**
* Load fixture at _path_. This utility function
* supplies the means to resolve, and cache fixture contents
* via the DOM or Rhino.
* Load fixture at _path_.
*
* Fixtures are resolved as:
*
* - <path>
* - fixtures/<path>
* - fixtures/<path>.html
* - <path>.html
*
* @param {string} path
* @return {string}
@@ -69,18 +66,14 @@
fixture : function(path) {
if (JSpec.cache[path]) return JSpec.cache[path]
return JSpec.cache[path] =
JSpec.tryLoading(path) ||
JSpec.tryLoading('fixtures/' + path) ||
JSpec.tryLoading('fixtures/' + path + '.html') ||
JSpec.tryLoading('spec/' + path) ||
JSpec.tryLoading('spec/fixtures/' + path) ||
JSpec.tryLoading('spec/fixtures/' + path + '.html')
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path) ||
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path + '.html')
}
},
// --- Objects
formatters : {
reporters : {
/**
* Report to server.
@@ -123,7 +116,7 @@
},
/**
* Default formatter, outputting to the DOM.
* Default reporter, outputting to the DOM.
*
* Options:
* - reportToId id of element to output reports to, defaults to 'jspec'
@@ -149,6 +142,7 @@
report.innerHTML = '<div id="jspec-report" class="' + classes + '"><div class="heading"> \
<span class="passes">Passes: <em>' + results.stats.passes + '</em></span> \
<span class="failures">Failures: <em>' + results.stats.failures + '</em></span> \
<span class="passes">Duration: <em>' + results.duration + '</em> ms</span> \
</div><table class="suites">' + map(results.allSuites, function(suite) {
var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran
if (displaySuite && suite.hasSpecs())
@@ -170,7 +164,7 @@
},
/**
* Terminal formatter.
* Terminal reporter.
*
* @api public
*/
@@ -178,7 +172,8 @@
Terminal : function(results, options) {
failuresOnly = option('failuresOnly')
print(color("\n Passes: ", 'bold') + color(results.stats.passes, 'green') +
color(" Failures: ", 'bold') + color(results.stats.failures, 'red') + "\n")
color(" Failures: ", 'bold') + color(results.stats.failures, 'red') +
color(" Duration: ", 'bold') + color(results.duration, 'green') + " ms \n")
function indent(string) {
return string.replace(/^(.)/gm, ' $1')
@@ -208,7 +203,7 @@
},
/**
* Console formatter.
* Console reporter.
*
* @api public
*/
@@ -246,7 +241,8 @@
// Report assertion results
report : function() {
this.passed ? JSpec.stats.passes++ : JSpec.stats.failures++
if (JSpec.assert)
this.passed ? JSpec.stats.passes++ : JSpec.stats.failures++
return this
},
@@ -270,7 +266,7 @@
// Proxy
object[method] = function(){
args = argumentsToArray(arguments)
args = toArray(arguments)
result = old.apply(object, args)
self.calls.push({ args : args, result : result })
return result
@@ -302,7 +298,7 @@
// Proxy arguments passed
with_args : function() {
this.expectedArgs = argumentsToArray(arguments)
this.expectedArgs = toArray(arguments)
return this
},
@@ -312,7 +308,7 @@
return any(this.calls, function(call){
return self.expectedResult.an_instance_of ?
call.result.constructor != self.expectedResult.an_instance_of:
hash(self.expectedResult) != hash(call.result)
!equal(self.expectedResult, call.result)
})
},
@@ -322,7 +318,7 @@
return any(this.calls, function(call){
return self.expectedResult.an_instance_of ?
call.result.constructor == self.expectedResult.an_instance_of:
hash(self.expectedResult) == hash(call.result)
equal(self.expectedResult, call.result)
})
},
@@ -346,7 +342,7 @@
if (arg == null) return call.args[i] == null
return arg.an_instance_of ?
call.args[i].constructor != arg.an_instance_of:
hash(arg) != hash(call.args[i])
!equal(arg, call.args[i])
})
})
@@ -359,7 +355,7 @@
return any(self.expectedArgs, function(i, arg){
return arg.an_instance_of ?
call.args[i].constructor == arg.an_instance_of:
hash(arg) == hash(call.args[i])
equal(arg, call.args[i])
})
})
@@ -380,7 +376,8 @@
// Report assertion results
report : function() {
this.passed ? ++JSpec.stats.passes : ++JSpec.stats.failures
if (JSpec.assert)
this.passed ? ++JSpec.stats.passes : ++JSpec.stats.failures
return this
},
@@ -506,14 +503,14 @@
pass : function(message) {
this.assertions.push({ passed: true, message: message })
++JSpec.stats.passes
if (JSpec.assert) ++JSpec.stats.passes
},
// Add failing assertion
fail : function(message) {
this.assertions.push({ passed: false, message: message })
++JSpec.stats.failures
if (JSpec.assert) ++JSpec.stats.failures
},
// Run deferred assertions
@@ -679,7 +676,7 @@
/**
* Include _object_ which may be a hash or Module instance.
*
* @param {has, Module} object
* @param {hash, Module} object
* @return {JSpec}
* @api public
*/
@@ -690,7 +687,7 @@
if ('init' in module) module.init()
if ('utilities' in module) extend(this.defaultContext, module.utilities)
if ('matchers' in module) this.addMatchers(module.matchers)
if ('formatters' in module) extend(this.formatters, module.formatters)
if ('reporters' in module) extend(this.reporters, module.reporters)
if ('DSLs' in module)
each(module.DSLs, function(name, methods){
JSpec.DSLs[name] = JSpec.DSLs[name] || {}
@@ -711,7 +708,7 @@
*/
hook : function(name, args) {
args = argumentsToArray(arguments, 1)
args = toArray(arguments, 1)
return inject(JSpec.modules, [], function(results, module){
if (typeof module[name] == 'function')
results.push(JSpec.evalHook(module, name, args))
@@ -805,7 +802,7 @@
* @api public
*/
argumentsToArray : function(arguments, offset) {
toArray : function(arguments, offset) {
return Array.prototype.slice.call(arguments, offset || 0)
},
@@ -842,7 +839,9 @@
return 'expected ' + puts(actual) + ' to ' +
(negate ? 'not ' : '') +
name.replace(/_/g, ' ') +
' ' + puts.apply(this, expected.slice(1))
' ' + (expected.length > 1 ?
puts.apply(this, expected.slice(1)) :
'')
},
/**
@@ -907,34 +906,35 @@
return (value = query(key)) !== null ? value :
JSpec.options[key] || null
},
/**
* Generates a hash of the object passed.
*
* @param {object} object
* @return {string}
* @api private
*/
hash : function(object) {
if (object == null) return 'null'
if (object == undefined) return 'undefined'
function serialize(prefix) {
return inject(object, prefix + ':', function(buffer, key, value){
return buffer += hash(value)
})
}
switch (object.constructor) {
case Array : return serialize('a')
case RegExp: return 'r:' + object.toString()
case Number: return 'n:' + object.toString()
case String: return 's:' + object.toString()
case Object: return 'o:' + inject(object, [], function(array, key, value){
array.push([key, hash(value)])
}).sort()
default: return object.toString()
}
},
/**
* Check if object _a_, is equal to object _b_.
*
* @param {object} a
* @param {object} b
* @return {bool}
* @api private
*/
equal: function(a, b) {
if (typeof a != typeof b) return
if (a === b) return true
if (a instanceof RegExp)
return a.toString() === b.toString()
if (a instanceof Date)
return Number(a) === Number(b)
if (typeof a != 'object') return
if (a.length !== undefined)
if (a.length !== b.length) return
else
for (var i = 0, len = a.length; i < len; ++i)
if (!equal(a[i], b[i]))
return
for (var key in a)
if (!equal(a[key], b[key]))
return
return true
},
/**
* Return last element of an array.
@@ -957,30 +957,35 @@
*/
puts : function(object) {
if (arguments.length > 1) {
return map(argumentsToArray(arguments), function(arg){
if (arguments.length > 1)
return map(toArray(arguments), function(arg){
return puts(arg)
}).join(', ')
}
if (object === undefined) return ''
if (object === undefined) return 'undefined'
if (object === null) return 'null'
if (object === true) return 'true'
if (object === false) return 'false'
if (object.an_instance_of) return 'an instance of ' + object.an_instance_of.name
if (object.jquery && object.selector.length > 0) return 'selector ' + puts(object.selector) + ''
if (object.jquery && object.selector.length > 0) return 'selector ' + puts(object.selector)
if (object.jquery) return object.get(0).outerHTML
if (object.nodeName) return object.outerHTML
switch (object.constructor) {
case String: return "'" + object + "'"
case Number: return object
case Function: return object.name || object
case String:
return '"' + object
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t')
+ '"'
case Array:
return inject(object, '[', function(b, v){
return b + ', ' + puts(v)
}).replace('[,', '[') + ' ]'
case Object:
object.__hit__ = true
return inject(object, '{', function(b, k, v) {
return b + ', ' + puts(k) + ' : ' + puts(v)
if (k == '__hit__') return b
return b + ', ' + k + ': ' + (v && v.__hit__ ? '<circular reference>' : puts(v))
}).replace('{,', '{') + ' }'
default:
return object.toString()
@@ -1024,7 +1029,7 @@
*/
does : function(actual, matcher, expected) {
var assertion = new JSpec.Assertion(JSpec.matchers[matcher], actual, argumentsToArray(arguments, 2))
var assertion = new JSpec.Assertion(JSpec.matchers[matcher], actual, toArray(arguments, 2))
return assertion.run().result
},
@@ -1041,8 +1046,8 @@
*/
expect : function(actual) {
assert = function(matcher, args, negate) {
var expected = argumentsToArray(args, 1)
function assert(matcher, args, negate) {
var expected = toArray(args, 1)
matcher.negate = negate
assertion = new JSpec.Assertion(matcher, actual, expected, negate)
hook('beforeAssertion', assertion)
@@ -1051,11 +1056,11 @@
return assertion.result
}
to = function(matcher) {
function to(matcher) {
return assert(matcher, arguments, false)
}
not_to = function(matcher) {
function not_to(matcher) {
return assert(matcher, arguments, true)
}
@@ -1114,17 +1119,20 @@
/**
* Iterate an object, invoking the given callback.
*
* @param {hash, array, string} object
* @param {hash, array} object
* @param {function} callback
* @return {JSpec}
* @api public
*/
each : function(object, callback) {
if (typeof object == 'string') object = object.split(' ')
for (key in object)
if (object.hasOwnProperty(key))
callIterator(callback, key, object[key])
if (object.constructor == Array)
for (var i = 0, len = object.length; i < len; ++i)
callIterator(callback, i, object[i])
else
for (var key in object)
if (object.hasOwnProperty(key))
callIterator(callback, key, object[key])
},
/**
@@ -1326,7 +1334,7 @@
var context = this.context || this.defaultContext
var contents = this.contentsOf(body)
hook('evaluatingBody', dsl, matchers, context, contents)
try { eval('with (dsl){ with (context) { with (matchers) { ' + contents + ' }}}') }
try { with (dsl){ with (context) { with (matchers) { eval(contents) }}} }
catch(e) { error(errorMessage, e) }
},
@@ -1344,7 +1352,7 @@
return input.
replace(/\t/g, ' ').
replace(/\r\n|\n|\r/g, '\n').
replace(/__END__[^]*/, '').
split('__END__')[0].
replace(/([\w\.]+)\.(stub|destub)\((.*?)\)$/gm, '$2($1, $3)').
replace(/describe\s+(.*?)$/gm, 'describe($1, function(){').
replace(/^\s+it\s+(.*?)$/gm, ' it($1, function(){').
@@ -1381,8 +1389,9 @@
*/
report : function() {
this.duration = Number(new Date) - this.start
hook('reporting', JSpec.options)
new (JSpec.options.formatter || JSpec.formatters.DOM)(JSpec, JSpec.options)
new (JSpec.options.reporter || JSpec.reporters.DOM)(JSpec, JSpec.options)
},
/**
@@ -1397,9 +1406,8 @@
run : function(options) {
if (any(hook('running'), haveStopped)) return this
if (options) extend(this.options, options)
if (option('profile')) console.group('Profile')
this.start = Number(new Date)
each(this.suites, function(suite) { JSpec.runSuite(suite) })
if (option('profile')) console.groupEnd()
return this
},
@@ -1462,10 +1470,8 @@
runSpec : function(spec) {
this.currentSpec = spec
if (option('profile')) console.time(spec.description)
try { this.evalBody(spec.body) }
catch (e) { fail(e) }
if (option('profile')) console.timeEnd(spec.description)
spec.runDeferredAssertions()
destub()
this.stats.specsFinished++
@@ -1588,8 +1594,7 @@
*/
tryLoading : function(file) {
try { return JSpec.load(file) }
catch (e) {}
try { return JSpec.load(file) } catch (e) {}
},
/**
@@ -1638,14 +1643,15 @@
var main = this
var find = JSpec.any
var utils = 'haveStopped stub hookImmutable hook destub map any last pass fail range each option inject select \
error escape extend puts hash query strip color does addMatchers callIterator argumentsToArray'.split(/\s+/)
while (utils.length) util = utils.shift(), eval('var ' + util + ' = JSpec.' + util)
error escape extend puts query strip color does addMatchers callIterator toArray equal'.split(/\s+/)
while (utils.length) eval('var ' + utils[0] + ' = JSpec.' + utils.shift())
if (!main.setTimeout) main.setTimeout = function(callback){ callback() }
// --- Matchers
addMatchers({
equal : "===",
eql : "equal(actual, expected)",
be : "alias equal",
be_greater_than : ">",
be_less_than : "<",
@@ -1664,13 +1670,6 @@
have_length : "actual.length == expected",
be_within : "actual >= expected[0] && actual <= last(expected)",
have_length_within : "actual.length >= expected[0] && actual.length <= last(expected)",
eql : function(actual, expected) {
return actual.constructor == Array ||
actual instanceof Object ?
hash(actual) == hash(expected):
actual == expected
},
receive : { defer : true, match : function(actual, method, times) {
proxy = new JSpec.ProxyAssertion(actual, method, times, this.negate)
@@ -1693,7 +1692,7 @@
case Number:
case RegExp:
case Function:
state = actual.toString().match(arg.toString())
state = actual.toString().indexOf(arg) !== -1
break
case Object:
@@ -1701,7 +1700,7 @@
break
case Array:
state = any(actual, function(value){ return hash(value) == hash(arg) })
state = any(actual, function(value){ return equal(value, arg) })
break
}
if (!state) return false
@@ -1715,9 +1714,9 @@
this.e = e
var assert = function(arg) {
switch (arg.constructor) {
case RegExp : return arg.test(e)
case RegExp : return arg.test(e.message || e.toString())
case String : return arg == (e.message || e.toString())
case Function : return (e.name || 'Error') == arg.name
case Function : return e instanceof arg || e.name == arg.name
}
}
return message ? assert(expected) && assert(message) :
@@ -1771,6 +1770,4 @@
}
})
if ('exports' in main) exports.JSpec = JSpec
})()
+5 -2
Ver Arquivo
@@ -15,7 +15,8 @@
commands: {
quit: ['Terminate the shell', function(){ _quit() }],
exit: ['Terminate the shell', function(){ _quit() }]
exit: ['Terminate the shell', function(){ _quit() }],
p: ['Inspect an object', function(o){ return o.toSource() }]
},
/**
@@ -27,7 +28,9 @@
start : function() {
for (var name in this.commands)
if (this.commands.hasOwnProperty(name))
this.main.__defineGetter__(name, this.commands[name][1])
this.commands[name][1].length ?
this.main[name] = this.commands[name][1] :
this.main.__defineGetter__(name, this.commands[name][1])
}
}
+1 -1
Ver Arquivo
@@ -1,5 +1,5 @@
// Mock Timers - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
// JSpec - Mock Timers - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
;(function(){
+12 -2
Ver Arquivo
@@ -8,7 +8,10 @@
var OriginalXMLHttpRequest = 'XMLHttpRequest' in this ?
XMLHttpRequest :
function(){}
var OriginalActiveXObject = 'ActiveXObject' in this ?
ActiveXObject :
undefined
// --- MockXMLHttpRequest
var MockXMLHttpRequest = function() {
@@ -66,11 +69,15 @@
*/
send : function(data) {
var self = this
this.data = data
this.readyState = 4
if (this.method == 'HEAD') this.responseText = null
this.responseHeaders['content-length'] = (this.responseText || '').length
if(this.async) this.onreadystatechange()
lastRequest = function(){
return self
}
}
}
@@ -132,6 +139,7 @@
function mockRequest() {
return { and_return : function(body, type, status, headers) {
XMLHttpRequest = MockXMLHttpRequest
ActiveXObject = false
status = status || 200
headers = headers || {}
headers['content-type'] = type
@@ -152,6 +160,7 @@
function unmockRequest() {
XMLHttpRequest = OriginalXMLHttpRequest
ActiveXObject = OriginalActiveXObject
}
JSpec.include({
@@ -175,7 +184,8 @@
DSLs : {
snake : {
mock_request: mockRequest,
unmock_request: unmockRequest
unmock_request: unmockRequest,
last_request: function(){ return lastRequest() }
}
}
+48
Ver Arquivo
@@ -0,0 +1,48 @@
require.paths.unshift('spec', 'lib', 'spec/lib')
require("jspec")
require("express")
require("express/spec")
print = puts
quit = process.exit
readFile = require('fs').readFileSync
function run(specs) {
specs.forEach(function(spec){
JSpec.exec('spec/spec.' + spec + '.js')
})
}
specs = {
independant: [
'core',
'routing',
'utils',
'request',
'mime',
'static',
'plugins',
'plugins.cache',
'plugins.view',
'plugins.content-length',
'plugins.method-override',
'plugins.body-decoder',
'plugins.redirect',
'plugins.hooks',
'plugins.cookie',
'plugins.session',
'plugins.flash',
]
}
switch (process.ARGV[2]) {
case 'all':
run(specs.independant)
break
default:
run([process.ARGV[2]])
}
Express.environment = 'test'
JSpec.run({ reporter: JSpec.reporters.Terminal, failuresOnly: true }).report()
-472
Ver Arquivo
@@ -1,472 +0,0 @@
process.mixin(require('express/collection'))
describe 'Express'
describe 'Collection'
describe '$(array)'
it 'should return a Collection'
$(['foo', 'bar']).should.be_an_instance_of Collection
end
end
describe '$(object)'
it 'should return a Collection'
$({ foo: 'bar' }).should.be_an_instance_of Collection
end
end
describe '$(Collection)'
it 'should return the collection passed'
var collection = $(['foo'])
$(collection).should.equal collection
end
end
describe 'shorthand expressions'
describe 'with 3 or less chars'
it 'should be considered binary operator between a / b'
$(5..1).sort('-').toArray().should.eql 1..5
$(5..1).reduce(0, '+').should.eql 15
end
end
describe 'with over 3 chars'
it 'should be considered a return expression'
$(5..1).sort('a - b').toArray().should.eql 1..5
$(5..1).reduce(0, 'a + b').should.eql 15
end
it 'should consider a single word a property on a'
$(['foo', 'foobar']).map('length').toArray().should.eql [3, 6]
end
it 'should consider a single function a method on a'
$(['foo', 'foobar']).map('charAt(0)').toArray().should.eql ['f', 'f']
end
end
end
describe '#at()'
it 'should return the value at the given index'
$(['foo', 'bar']).at(0).should.eql 'foo'
$(['foo', 'bar']).at(1).should.eql 'bar'
$(['foo', 'bar']).at(2).should.be_null
end
it 'should work with objects'
$({ foo: 'bar', baz: 'raz' }).at(0).should.eql 'bar'
$({ foo: 'bar', baz: 'raz' }).at(1).should.eql 'raz'
$({ foo: 'bar', baz: 'raz' }).at(2).should.be_null
end
end
describe '#each()'
it 'should iterate passing index and value'
var result = []
$(['foo', 'bar']).each(function(val, i){
result.push(i, val)
})
result.should.eql [0, 'foo', 1, 'bar']
end
it 'should work with objects'
var result = []
$({ foo: 'bar', baz: 'raz' }).each(function(val, key){
result.push(key, val)
})
result.should.eql ['foo', 'bar', 'baz', 'raz']
end
it 'should return the collection'
$([]).each(function(){}).should.be_an_instance_of Collection
end
end
describe '#reduce()'
it 'should iterate with memo object'
var sum = $([1,2,3]).reduce(0, function(sum, n){ return sum + n })
sum.should.eql 6
end
it 'should allow shorthand expressions'
$([1,2,3]).reduce(0, '+').should.eql 6
$([1,2,3]).reduce(0, 'a + b').should.eql 6
end
end
describe '#map()'
it 'should iterate collecting results into a new collection'
var collection = $(['foo', 'bar']).map(function(val){ return val.toUpperCase() })
collection.at(0).should.eql 'FOO'
collection.at(1).should.eql 'BAR'
end
it 'should work with objects'
var collection = $({ foo: 'bar', baz: 'raz' }).map(function(val){ return val.toUpperCase() })
collection.at(0).should.eql 'BAR'
collection.at(1).should.eql 'RAZ'
end
it 'should allow shorthand expressions'
$(['foo', 'bar']).map('a.toUpperCase()').toArray().should.eql ['FOO', 'BAR']
end
end
describe '#first()'
it 'should return the first value'
$(['foo']).first().should.eql 'foo'
end
it 'should return the first n values'
$([5,4,3,2,1]).first(2).at(0).should.eql 5
$([5,4,3,2,1]).first(2).at(1).should.eql 4
end
it 'should work with objects'
$({ foo: 'bar' }).first().should.eql 'bar'
end
end
describe '#last()'
it 'should return the last value'
$(['foo', 'bar']).last().should.eql 'bar'
end
it 'should return the last n values'
$([5,4,3,2,1]).last(2).at(0).should.eql 2
$([5,4,3,2,1]).last(2).at(1).should.eql 1
end
it 'should work with objects'
$({ a: 'foo', b: 'bar' }).last(2).at(0).should.eql 'foo'
$({ a: 'foo', b: 'bar' }).last(2).at(1).should.eql 'bar'
$({ a: 'foo', b: 'bar' }).last().should.eql 'bar'
end
end
describe '#drop()'
it 'should drop the first n values'
$(1..5).drop(2).arr.should.eql 3..5
end
end
describe '#find()'
it 'should return the value of the first match'
var result = $(['foo', 'bar']).find(function(val){ return val.charAt(0) == 'b' })
result.should.eql 'bar'
end
it 'should return null when nothing matches'
var result = $(['foo', 'bar']).find(function(val){ return val.charAt(0) == 'a' })
result.should.be_null
end
it 'should work with objects'
var result = $({ foo: 'bar', baz: 'raz' }).find(function(val, key){
return val.charAt(0) == 'r'
})
result.should.eql 'raz'
end
it 'should allow shorthand expressions'
$(['foo', 'bar']).find("a.charAt(0) == 'b'").should.eql 'bar'
end
end
describe '#all()'
it 'should return true when all evaluate to true'
$(['foo', 'foobar']).all(function(val){ return val.charAt(0) == 'f' }).should.be_true
end
it 'should return false when any evaluate to false'
$(['foo', 'bar', 'foobar']).all(function(val){ return val.charAt(0) == 'f' }).should.be_false
end
it 'should work with objects'
$({ a: 'foo', b: 'foobar' }).all(function(val){ return val.charAt(0) == 'f' }).should.be_true
end
it 'should allow shorthand expressions'
$(['foo', 'bar']).all('a.length > 2').should.be_true
end
end
describe '#any()'
it 'should return true when found'
$(['foo', 'bar']).any(function(val){ return val.charAt(0) == 'b' }).should.be_true
end
it 'should return false when not found'
$(['foo', 'bar']).any(function(val){ return val.charAt(0) == 'r' }).should.be_false
end
it 'should work with objects'
$({ foo: 'bar' }).any(function(val){ return val.charAt(0) == 'b' }).should.be_true
$({ foo: 'bar' }).any(function(val){ return val.charAt(0) == 'c' }).should.be_false
end
it 'should allow shorthand expressions'
$(['foo', 'bar']).any('a.length > 2').should.be_true
end
end
describe '#select()'
it 'should return values which evaluate to true'
var result = $([1,2,3,4,5]).select(function(n){ return n % 2 })
result.at(0).should.eql 1
result.at(1).should.eql 3
result.at(2).should.eql 5
end
it 'should return a Collection'
var result = $([1,2,3,4,5]).select(function(n){ return n % 2 })
result.should.be_an_instance_of Collection
end
it 'should allow shorthand expressions'
$([1,2,3,4,5]).select('a % 2').toArray().should.eql [1,3,5]
end
end
describe '#reject()'
it 'should return values which evaluate to false'
var result = $([1,2,3,4,5,6]).reject(function(n){ return n % 2 })
result.at(0).should.eql 2
result.at(1).should.eql 4
result.at(2).should.eql 6
end
it 'should return a Collection'
var result = $([1,2,3,4,5]).reject(function(n){ return n % 2 })
result.should.be_an_instance_of Collection
end
it 'should allow shorthand expressions'
$(['foo', 'bar']).reject('a.charAt(0) == "b"').toArray().should.eql ['foo']
end
end
describe '#slice()'
it 'should return a slice of values'
var collection = $(['foo', 'bar', 'baz']).slice(1, 3)
collection.at(0).should.eql 'bar'
collection.at(1).should.eql 'baz'
end
it 'should work with objects'
var collection = $({ foo: 1, bar: 2, baz: 3, raz: 4 }).slice(1, 3)
collection.at(0).should.eql 2
collection.at(1).should.eql 3
end
end
describe '#grep()'
it 'should select values matching the regular expression passed'
var result = $(['foo', 'bar', 'foobar', 'baz']).grep(/foo(bar)?/)
result.at(0).should.eql 'foo'
result.at(1).should.eql 'foobar'
result.at(2).should.be_null
end
end
describe '#keys()'
it 'should return indices when array-like'
$(['foo', 'bar']).keys().at(0).should.eql 0
$(['foo', 'bar']).keys().at(1).should.eql 1
end
it 'should return keys when an object'
$({ foo: 'bar', baz: 'raz' }).keys().at(0).should.eql 'foo'
$({ foo: 'bar', baz: 'raz' }).keys().at(1).should.eql 'baz'
end
end
describe '#toArray()'
it 'should return an array'
$(['foo', 'bar']).keys().toArray().should.eql [0, 1]
end
it 'should work on nested collections'
$([$(['foo']), $(['bar'])]).toArray().should.eql [['foo'], ['bar']]
end
it 'should work with objects'
$({ foo: 'bar', baz: { name: 'wahoo' }}).toArray().should.eql ['bar', { name: 'wahoo' }]
end
end
describe '#min()'
it 'should return the min value'
$([4,5,2,3,62]).min().should.eql 2
end
end
describe '#max()'
it 'should return the max value'
$([3,5,2,3,43,2]).max().should.eql 43
end
end
describe '#length()'
it 'should work with arrays'
$([1,2,3]).length().should.eql 3
end
it 'should work with objects'
$({ a: 'b', c: 'd', e: 'f' }).length().should.eql 3
end
end
describe '#chunk()'
it 'should group into chunks of the given size'
$([1,1,2,2,3,3]).chunk(2).toArray().should.eql [[1,1],[2,2],[3,3]]
$([1,1,2,2,3,3]).chunk(4).toArray().should.eql [[1,1,2,2],[3,3]]
end
end
describe '#sum()'
it 'should return the sum of the numeric values'
$([1,2,3]).sum().should.eql 6
end
end
describe '#avg()'
it 'should return the average of numeric values'
$([3,1]).avg().should.eql 2
end
end
describe '#merge()'
it 'should merge two array collections'
var a = $([1,2,3])
var b = $([4,5,6])
a.merge(b).toArray().should.eql [1,2,3,4,5,6]
var a = $([1,2,3])
var b = [4,5,6]
a.merge(b).toArray().should.eql [1,2,3,4,5,6]
end
it 'should merge two object collections'
var a = $({ a: 'b' })
var b = $({ c: 'd' })
a.merge(b).arr.should.eql { a: 'b', c: 'd' }
var a = $({ a: 'b' })
var b = { c: 'd' }
a.merge(b).arr.should.eql { a: 'b', c: 'd' }
end
it 'should merge an array and object collection'
var a = $([1,2])
var b = $({ a: 'b', c: 'd' })
a.merge(b).arr.should.eql [1, 2, 'b', 'd']
var a = $([1,2])
var b = { a: 'b', c: 'd' }
a.merge(b).arr.should.eql [1, 2, 'b', 'd']
end
it 'should merge an object and array collection'
var a = $({ a: 'b', c: 'd' })
var b = $([1,2])
a.merge(b).arr.should.eql { a: 'b', c: 'd', 0: 1, 1: 2 }
var a = $({ a: 'b', c: 'd' })
var b = [1,2]
a.merge(b).arr.should.eql { a: 'b', c: 'd', 0: 1, 1: 2 }
end
end
describe '#clone()'
it 'should clone an array collection'
var a = $([1,2,3])
var b = a.clone()
a.should.not.equal b
b.arr.should.eql [1,2,3]
end
it 'should clone an object collection'
var a = $({ foo: 'bar' })
var b = a.clone()
a.should.not.equal b
b.arr.should.eql { foo: 'bar' }
end
end
describe '#sample()'
it 'should return a random value'
Math.stub('random').and_return(0.1)
$([1,2,3,4]).sample().should.eql 1
end
end
describe '#reverse()'
it 'should reverse a collection'
$([1,2,3]).reverse().toArray().should.eql [3,2,1]
end
end
describe '#sort()'
it 'should sort a collection'
$([3,1,2]).sort().toArray().should.eql [1,2,3]
end
it 'should sort with a function'
$([3,1,2]).sort(function(a, b){ return b - a }).toArray().should.eql [3,2,1]
end
it 'should allow shorthand expressions'
$([3,1,2]).sort('-').toArray().should.eql [1,2,3]
end
end
describe '#join()'
it 'should join a collection with "" by default'
$([1,2,3]).join().should.eql '123'
end
it 'should join with an arbitrary string'
$([1,2,3]).join(' ').should.eql '1 2 3'
end
it 'should work with objects'
$({ foo: 'bar', baz: 'raz' }).join(' ').should.eql 'bar raz'
end
end
describe '#includes()'
it 'should return true when the value is present'
$([1,2,3]).includes(2).should.be_true
end
it 'should return true when all values are present'
$([1,2,3]).includes(2, 3).should.be_true
end
it 'should return false when the value is not present'
$([1,2,3]).includes(4).should.be_false
end
it 'should return false when the any value is not present'
$([1,2,3]).includes(1,2,4).should.be_false
$([1,2,3]).includes(1,4,2).should.be_false
end
end
describe '#toString()'
it 'should output [Collection ...] for array'
$([1,2,3]).toString().should.eql '[Collection 1,2,3]'
end
it 'should output [Collection [object Object]] for object'
$({ foo: "bar" }).toString().should.eql '[Collection [object Object]]'
end
it 'should output [Collection [[Collection ...]]] for nested collections'
$([$([1,2,3])]).toString().should.eql '[Collection [Collection 1,2,3]]'
end
end
end
end
-83
Ver Arquivo
@@ -1,83 +0,0 @@
process.mixin(require('express/collection'))
process.mixin(require('express/element-collection'))
describe 'Express'
describe 'ElementCollection'
describe '$("markup string")'
it 'should return a ElementCollection'
$('<p>foo</p>').should.be_an_instance_of Collection
$('<p>foo</p>').should.be_an_instance_of ElementCollection
end
it 'should wrap with <html><body>.. when not present'
$('<p>foo</p>').at(0).name().should.eql 'html'
end
it 'should not wrap with <html><body> when already present'
$('<html><body></body></html>').at(0).name().should.eql 'html'
end
end
describe '#name()'
it 'should return the first elements name'
$('<html><body></body></html>').name().should.eql 'html'
end
end
describe '#xpath()'
it 'should find children matching the given xpath'
$('<li>1</li><li>2</li>').xpath('descendant-or-self::li').length().should.eql 2
$('<li>1</li><li>2</li>').xpath('descendant-or-self::li').at(0).name().should.eql 'li'
var items = $('<li><p>Foo</p></li><li><p>Bar</p></li>').xpath('descendant-or-self::li')
items.xpath('descendant-or-self::p').length().should.eql 2
end
end
describe '#search()'
it 'should find children matching the given css selector'
$('<ul><li>foo</li><li>bar</li></ul>').search('ul > li:nth-child(2)').text().should.eql 'bar'
end
end
describe '#children()'
it 'should return children'
$('<p><em>foo</em><strong>bar</strong></p>').children().length().should.eql 1
$('<p><em>foo</em><strong>bar</strong></p>').xpath('descendant-or-self::p').children().length().should.eql 2
end
end
describe '#parents()'
it 'should return parents'
$('<ul><li></li></ul><ul><li></li></ul>').xpath('descendant-or-self::li').parents().length().should.eql 2
$('<ul><li></li></ul><ul><li></li></ul>').xpath("descendant-or-self::*/*[name() = 'ul' and (position() = 1)]/descendant::li").parents().length().should.eql 1
end
end
describe '#parent()'
it 'should return the first parent'
$('<ul><li></li></ul><ul><li></li></ul>').xpath('descendant-or-self::li').parent().length().should.eql 1
end
end
describe '#text()'
it 'should return an elements text'
$('<p>foo bar</p>').text().should.eql 'foo bar'
end
end
describe '#next()'
it 'should return the next element'
$('<em>foo</em><p>bar</p>').xpath('descendant-or-self::em').next().text().should.eql 'bar'
end
end
describe '#prev()'
it 'should return the previous element'
$('<em>foo</em><p>bar</p>').xpath('descendant-or-self::p').prev().text().should.eql 'foo'
end
end
end
end
-75
Ver Arquivo
@@ -1,75 +0,0 @@
describe 'Express'
describe 'parseParams()'
it 'should parse simple query string key / value pairs'
parseParams('foo=bar').should.eql { foo: 'bar' }
parseParams('foo=bar&baz=1').should.eql { foo: 'bar', baz: '1' }
end
it 'should parse named nested params'
var user = { user: { name: 'tj', email: 'tj@vision-media.ca' }}
parseParams('user[name]=tj&user[email]=tj@vision-media.ca').should.eql user
end
it 'should parse several levels of nesting'
var user = { user: { name: 'tj', email: { primary: 'tj@vision-media.ca' }}}
parseParams('user[name]=tj&user[email][primary]=tj@vision-media.ca').should.eql user
end
it 'should convert + to literal space'
parseParams('foo=bar+baz++that').should.eql { foo: 'bar baz that' }
parseParams('user[name]=tj+holowaychuk').should.eql { user: { name: 'tj holowaychuk' }}
end
it 'should decode hex literals'
parseParams('foo=bar%20baz%20%20that').should.eql { foo: 'bar baz that' }
parseParams('user[name]=tj%20holowaychuk').should.eql { user: { name: 'tj holowaychuk' }}
end
end
describe 'toArray()'
describe 'when given an array'
it 'should return the array'
toArray([1,2,3]).should.eql [1,2,3]
end
end
describe 'when given an object with indexed values and length'
it 'should return an array'
var args = -{ return arguments }('foo', 'bar')
toArray(args).should.eql ['foo', 'bar']
end
end
end
describe 'escape()'
it 'should escape html'
escape('<p>this & that').should.eql '&lt;p&gt;this &amp; that'
end
end
describe 'extname()'
it 'should return the a files extension'
extname('image.png').should.eql 'png'
extname('image.large.png').should.eql 'png'
extname('/path/to/image.large.png').should.eql 'png'
end
it 'should return null when not found'
extname('path').should.be_null
extname('/just/a/path').should.be_null
end
end
describe 'dirname()'
it 'should return the directory path'
dirname('/path/to/images/foo.bar.png').should.eql '/path/to/images'
end
end
describe 'basename()'
it 'should return a files basename'
basename('foo/bar/baz.image.png').should.eql 'baz.image.png'
end
end
end
+30 -24
Ver Arquivo
@@ -1,36 +1,42 @@
describe 'Express'
before
mime = require('express/mime')
end
before_each
reset()
end
describe 'mime()'
describe 'when given an extension with leading dot'
it 'should return the associated mime type'
mime('.png').should.eql 'image/png'
describe 'mime'
describe 'type()'
describe 'when given an extension with leading dot'
it 'should return the associated mime type'
mime.type('.png').should.eql 'image/png'
end
end
end
describe 'when given an extension without leading dot'
it 'should return the associated mime type'
mime('png').should.eql 'image/png'
describe 'when given an extension without leading dot'
it 'should return the associated mime type'
mime.type('png').should.eql 'image/png'
end
end
end
describe 'when given a file path'
it 'should return the associated mime type'
mime('/path/to/an/image.png').should.eql 'image/png'
describe 'when given a file path'
it 'should return the associated mime type'
mime.type('/path/to/an/image.png').should.eql 'image/png'
end
end
end
describe 'when given an unknown extension'
it 'should default to the "default mime type" setting'
set('default mime type', 'text/plain')
mime('meow').should.eql 'text/plain'
end
it 'should default to "application/octet-stream" otherwise'
mime('meow').should.eql 'application/octet-stream'
describe 'when given an unknown extension'
it 'should default to the "default mime type" setting'
set('default mime type', 'text/plain')
mime.type('meow').should.eql 'text/plain'
end
it 'should default to "application/octet-stream" otherwise'
mime.type('meow').should.eql 'application/octet-stream'
end
end
end
end
-43
Ver Arquivo
@@ -1,43 +0,0 @@
require.paths.unshift('spec', 'spec/lib', 'lib')
require("jspec")
require("express")
require("express/spec")
quit = process.exit
print = puts
readFile = function(path) {
var promise = require('posix').cat(path, "utf8")
var result = ''
promise.addErrback(function(){ throw "failed to read file `" + path + "'" })
promise.addCallback(function(contents){
result = contents
})
promise.wait()
return result
}
if (process.ARGV[2])
JSpec.exec('spec/spec.' + process.ARGV[2] + '.js')
else
JSpec
.exec('spec/spec.core.js')
.exec('spec/spec.routing.js')
.exec('spec/spec.helpers.js')
.exec('spec/spec.request.js')
.exec('spec/spec.mime.js')
.exec('spec/spec.static.js')
.exec('spec/spec.collection.js')
.exec('spec/spec.element-collection.js')
.exec('spec/spec.plugins.js')
.exec('spec/spec.plugins.view.js')
.exec('spec/spec.plugins.common-logger.js')
.exec('spec/spec.plugins.content-length.js')
.exec('spec/spec.plugins.method-override.js')
.exec('spec/spec.plugins.body-decoder.js')
.exec('spec/spec.plugins.redirect.js')
.exec('spec/spec.plugins.hooks.js')
.exec('spec/spec.plugins.cookie.js')
JSpec.run({ formatter: JSpec.formatters.Terminal, failuresOnly: true })
JSpec.report()
+119
Ver Arquivo
@@ -0,0 +1,119 @@
describe 'Express'
before_each
reset()
use(require('express/plugins/cache').Cache)
cache = require('express/plugins/cache')
end
describe 'Cache'
describe 'Request'
describe '#cache'
it 'should use memory store by default'
get('/item', function(){
return this.cache.toString()
})
get('/item').body.should.eql '[Memory Store]'
end
end
end
end
describe 'cache Store.Memory'
before_each
store = new cache.Store.Memory
end
describe '#toString()'
it 'should return [Memory Store]'
store.toString().should.eql '[Memory Store]'
end
end
describe '#set()'
describe 'given a key and value'
it 'should set the cache data'
store.set('foo', 'bar')
store.get('foo').should.eql 'bar'
end
it 'should override existing data'
store.set('foo', 'bar')
store.set('foo', 'baz')
store.get('foo').should.eql 'baz'
end
it 'should return data'
store.set('foo', 'bar').should.eql 'bar'
end
end
describe 'given an abitrary key'
it 'should throw an error'
-{ store.set({}, 'foo') }.should.throw_error
end
end
describe 'given an abitrary value'
it 'should serialize as JSON'
store.set('user', { name: 'tj' }).should.eql { name: 'tj' }
end
end
end
describe '#get()'
describe 'given a key'
it 'should return cached value'
store.set('foo', 'bar')
store.get('foo').should.eql 'bar'
end
it 'should unserialize JSON data'
store.set('user', { name: 'tj' })
store.get('user').should.eql { name: 'tj' }
end
end
describe 'given wildcards'
it 'should return a set of caches'
store.set('user:1', 'a')
store.set('user:2', 'b')
store.set('foo', 'bar')
store.get('user:*').should.eql { 'user:1': 'a', 'user:2': 'b' }
end
end
end
describe '#clear()'
describe 'given a key'
it 'should delete previous data'
store.set('foo', 'bar')
store.clear('foo')
store.get('foo').should.be_null
end
end
describe 'given wildcards'
it 'should clear a set of caches'
store.set('user:one', '1')
store.set('user:two', '2')
store.clear('user:*')
store.get('user:one').should.be_null
store.get('user:two').should.be_null
end
end
end
describe '#reap()'
it 'should destroy caches older than the given age in milliseconds'
store.set('user:one', '1')
store.data['user:one'].created = Number(new Date) - 300
store.set('user:two', '2')
store.data['user:two'].created = Number(new Date) - 100
store.reap(200)
store.get('user:one').should.be_null
store.get('user:two').should.not.be_null
end
end
end
end
-23
Ver Arquivo
@@ -1,23 +0,0 @@
describe 'Express'
before_each
reset()
use(require('express/plugins/common-logger').CommonLogger)
end
describe 'CommonLogger'
describe 'on'
describe 'response'
it 'should output in common log format'
GLOBAL.stub('puts')
GLOBAL.should.receive('puts')
get('/style.css', function(){
this.contentType('css')
return 'body { background: #000; }'
})
get('/style.css')
end
end
end
end
end
+1 -1
Ver Arquivo
@@ -82,7 +82,7 @@ describe 'Express'
this.cookie('foo', 'bar')
return ''
})
get('/user').headers['set-cookie'].should.eql 'SID=732423sdfs73243; path=/; secure, foo=bar'
get('/user').headers['set-cookie'].should.eql 'SID=732423sdfs73243; path=/; secure, foo=bar; path=/'
end
end
end
+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()'
+3 -1
Ver Arquivo
@@ -1,4 +1,6 @@
var mime = require('express/mime')
CSSColors = Plugin.extend({
extend: {
init: function() {
@@ -7,7 +9,7 @@ CSSColors = Plugin.extend({
},
on: {
response: function(event) {
if (event.response.headers['content-type'] == mime('css'))
if (event.response.headers['content-type'] == mime.type('css'))
event.response.body = event.response.body.replace('black', '#000')
}
}
+2 -2
Ver Arquivo
@@ -12,14 +12,14 @@ describe 'Express'
put('/user', function(){
return 'updated user'
})
post('/user', { uri: { params: { _method: 'put' }}}).body.should.eql 'updated user'
post('/user', { body: '_method=put', headers: { 'content-type': 'application/x-www-form-urlencoded' }}).body.should.eql 'updated user'
end
it 'should force _method to lowercase to conform to internal uses'
put('/user', function(){
return 'updated user'
})
post('/user', { uri: { params: { _method: 'PUT' }}}).body.should.eql 'updated user'
post('/user', { body: '_method=PUT', headers: { 'content-type': 'application/x-www-form-urlencoded' }}).body.should.eql 'updated user'
end
end
end
+108
Ver Arquivo
@@ -0,0 +1,108 @@
describe 'Express'
before_each
reset()
use(require('express/plugins/cookie').Cookie)
use(Session = require('express/plugins/session').Session)
Session.store.clear()
end
describe 'Session'
describe 'when sid cookie is not present'
it 'should set sid cookie'
get('/login', function(){ return '' })
get('/login').headers['set-cookie'].should.match(/^sid=(\w+);/)
end
end
describe 'when sid cookie is present'
it 'should not set sid'
get('/login', function(){ return '' })
get('/login', { headers: { cookie: 'sid=123' }}).headers.should.not.have_property 'set-cookie'
end
end
describe 'session Store.Memory'
before_each
memory = new (require('express/plugins/session').Store.Memory)
end
it 'should persist'
post('/login', function(){
return this.session.name = 'tj'
})
get('/login', function(){
return this.session.name
})
var headers = { headers: { cookie: 'sid=123' }}
post('/login', headers)
get('/login', headers).status.should.eql 200
get('/login', headers).body.should.eql 'tj'
end
describe '#toString()'
it 'should return [Memory Store]'
memory.toString().should.eql '[Memory Store]'
end
end
describe '#fetch()'
describe 'when the session does not exist'
it 'should return a new Session'
memory.fetch('1').should.have_property 'lastAccess'
end
end
describe 'when the session does exist'
it 'should return the previous session'
memory.commit({ id: '1', same: true })
memory.fetch('1').should.have_property 'same', true
end
end
end
describe '#clear()'
it 'should remove all sessions'
memory.commit({ id: '1' })
memory.commit({ id: '2' })
memory.clear()
memory.should.not.have_property '1'
memory.should.not.have_property '2'
end
end
describe '#length()'
it 'should return the number of session'
memory.commit({ id: '1' })
memory.commit({ id: '2' })
memory.length().should.eql 2
end
end
describe '#destroy()'
it 'should destroy a single session'
memory.commit({ id: '1' })
memory.commit({ id: '2' })
memory.destroy('1')
memory.store.should.not.have_property '1'
memory.store.should.have_property '2'
end
end
describe '#reap()'
it 'should destroy sessions older than the given age in milliseconds'
memory.commit({ id: '1', lastAccess: Number(new Date) - 300 })
memory.commit({ id: '2', lastAccess: Number(new Date) - 250 })
memory.commit({ id: '3', lastAccess: Number(new Date) - 100 })
memory.commit({ id: '4', lastAccess: Number(new Date) })
memory.reap(200)
memory.store.should.not.have_property '1'
memory.store.should.not.have_property '2'
memory.store.should.have_property '3'
memory.store.should.have_property '4'
end
end
end
end
end
+73 -17
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()'
@@ -84,19 +107,6 @@ describe 'Express'
get('/user').body.should.include('Oh noes!')
end
end
describe 'when given an invalid status code'
it 'should throw an InvalidStatusCode exception'
// TODO: throw_error(InvalidStatusCode, ...) when jspec is fixed
get('/user', function(){ this.halt(123123) })
-{ get('/user') }.should.throw_error(/InvalidStatusCode: 123123 is an invalid HTTP response code/)
try { get('/user') }
catch (e) {
e.should.be_an_instance_of ExpressError
e.should.be_an_instance_of InvalidStatusCode
}
end
end
end
describe '#contentType()'
@@ -108,6 +118,24 @@ describe 'Express'
get('/style.css').headers['content-type'].should.eql 'text/css'
end
end
describe '#attachment()'
it 'should set Content-Disposition to attachment'
get('/report', function(){
this.attachment()
return 'foo'
})
get('/report').headers['content-disposition'].should.eql 'attachment'
end
it 'should set attachment filename'
get('/report', function(){
this.attachment('report.pdf')
return 'foo'
})
get('/report').headers['content-disposition'].should.eql 'attachment; filename="report.pdf"'
end
end
describe '#param()'
it 'should return a route placeholder value'
@@ -155,12 +183,13 @@ describe 'Express'
get('/product/ipod').body.should.eql 'ipod'
end
it 'should access request.uri.params'
it 'should work with a query string'
get('/user', function(){
return this.param('page') || 'First page'
return String(this.param('page') || 'First page')
})
get('/user').body.should.eql 'First page'
get('/user', { uri: { params: { page: '2' }}}).body.should.eql '2'
get('/user?page=2').body.should.eql '2'
get('/user?foo[]=bar&page=5').body.should.eql '5'
end
it 'should work with splats'
@@ -169,6 +198,33 @@ describe 'Express'
})
get('/public/app.js').body.should.eql 'public, app, js'
end
end
end
describe '#pass()'
it 'should pass control to the next matching route'
get('/user', function(){
this.pass()
})
get('/user', function(){
this.pass()
return 'nodejs'
})
get('/user', function(){ return 'success'})
get('/user').body.should.eql 'success'
end
describe 'given a string'
it 'should pass to the given route'
get('/user', function(){
this.pass('/user/1')
})
get('/user/:id', function(){
return 'Supa doopa usa'
})
get('/user').body.should.eql 'Supa doopa usa'
end
end
end
end
end
-9
Ver Arquivo
@@ -84,15 +84,6 @@ describe 'Express'
end
end
describe 'with no response body'
it 'should throw a InvalidResponseBody'
get('/user', function(){
this.respond()
})
-{ get('/user') }.should.throw_error(/InvalidResponseBody: get "\/user" did not respond with a body string/)
end
end
describe 'with regular expression'
it 'should match'
get(/^\/user\/(\d+)\/(\w+)/, function(id, operation){
+6 -1
Ver Arquivo
@@ -11,7 +11,12 @@ describe 'Express'
end
it 'should throw an InvalidPathError when .. is found'
-{ new StaticFile('/../foobar') }.should.throw_error(/InvalidPathError/)
// TODO: use throw_error when fixed...
try { new StaticFile('/../foobar') }
catch (e) {
e.name.should.eql 'InvalidPathError'
e.message.should.eql "`/../foobar' is not a valid path"
}
end
end
end
+104
Ver Arquivo
@@ -0,0 +1,104 @@
describe 'Express'
before
utils = require('express/utils')
end
describe 'escape()'
it 'should escape html'
utils.escape('<p>this & that').should.eql '&lt;p&gt;this &amp; that'
end
end
describe 'uid()'
it 'should return a string of random characters'
utils.uid().should.not.eql utils.uid()
utils.uid().length.should.be_greater_than 20
end
end
describe 'mergeParam()'
describe 'with empty params'
it 'should merge the given key and value'
params = {}
utils.mergeParam('user[names][firstName]', 'tj', params)
params.user.names.firstName.should.eql 'tj'
end
end
describe 'with populated params'
it 'should merge not overwrite'
params = { user: { name: 'tj' }}
utils.mergeParam('user[email]', 'tj@vision-media.ca', params)
params.user.name.should.eql 'tj'
params.user.email.should.eql 'tj@vision-media.ca'
end
end
describe 'with an object as value'
it 'should preserve it'
params = {}
utils.mergeParam('images[]', { name: 1 }, params)
utils.mergeParam('images[]', { name: 2 }, params)
params.images.should.eql [{ name: 1}, { name: 2 }]
end
end
describe 'key[number]'
it 'should merge correctly'
params = { images: { one: 'foo.png' }}
utils.mergeParam('images[0]', 'bar.png', params)
params.images.one.should.eql 'foo.png'
params.images[0].should.eql 'bar.png'
end
end
describe 'key[]'
describe 'with a single value'
it 'should still be an array'
params = {}
utils.mergeParam('images[]', '1', params)
params.images.should.eql ['1']
end
end
describe 'with empty params'
it 'should merge correctly'
params = {}
utils.mergeParam('images[]', '1', params)
utils.mergeParam('images[]', '2', params)
params.images.should.eql ['1', '2']
end
end
describe 'with populated params'
it 'should convert to an array'
params = { images: 'foo.png'}
utils.mergeParam('images[]', '1', params)
params.images.should.eql ['foo.png', '1']
end
end
describe 'with several merges'
it 'should push values'
params = {}
utils.mergeParam('images[]', '1', params)
utils.mergeParam('images[]', '2', params)
utils.mergeParam('images[]', '3', params)
params.images.should.eql ['1', '2', '3']
end
end
describe 'when nested'
it 'should marge correctly'
params = {}
utils.mergeParam('user[tj][images][]', '1', params)
utils.mergeParam('user[tj][images][]', '2', params)
utils.mergeParam('user[tj][images][]', '3', params)
params.user.should.eql { tj: { images: ['1', '2', '3'] }}
end
end
end
end
end
Arquivo binário não exibido.