Comparar commits

..

187 Commits

Autor SHA1 Mensagem Data
visionmedia 2f6dfbc165 Release 0.4.0 2010-02-11 17:02:30 -08:00
visionmedia 49cb53d735 Merge branch 'route-http-client-security' 2010-02-11 16:36:47 -08:00
visionmedia 54f1a51a10 Throwing error when routes are added at runtime
Since it doubles as an http client, without this
someone could arbitrarily create routes.. haha not good!
2010-02-11 16:36:38 -08:00
visionmedia c6a2674c2b Merge branch 'http' 2010-02-11 16:15:42 -08:00
visionmedia 282a10ec83 RESTful route functions double as HTTP clients. Closes #69
For example:
  get("http://google.com").addCallback(function(content){
    puts(content)
  })
2010-02-11 16:15:37 -08:00
visionmedia 50276a06df Merge branch 'http-client' 2010-02-11 16:11:00 -08:00
visionmedia f452250f88 Added high level restful http client 2010-02-11 16:10:43 -08:00
visionmedia 9d44e237a5 Added status code string to error 2010-02-11 15:30:16 -08:00
visionmedia 9f0e5899c2 Fixed Host header issue 2010-02-11 15:11:06 -08:00
visionmedia e351a02a06 Merge branch 'flash' 2010-02-11 14:22:59 -08:00
visionmedia cd167ec777 Added flash() example to sample upload app 2010-02-11 14:22:55 -08:00
visionmedia 3290412477 Updated haml 2010-02-11 14:02:26 -08:00
visionmedia acf0128fb4 Merge branch 'view-context' 2010-02-11 13:57:33 -08:00
visionmedia e91ee22a89 Defaulting render() context to the current Request. Closes #197 2010-02-11 13:57:26 -08:00
visionmedia 45ef08cf99 Release 0.3.0 2010-02-11 07:12:08 -08:00
visionmedia baa7d12ed6 Updated haml / sass submodules. Closes #200 2010-02-11 07:09:15 -08:00
visionmedia 822de581b3 flash() returns null when no flashes are available. Closes #198 2010-02-09 16:34:19 -08:00
visionmedia 3863a76fc8 Merge branch 'flash' 2010-02-09 08:41:32 -08:00
visionmedia 9727fac291 Added flash support. Closes #64 2010-02-09 08:41:28 -08:00
visionmedia 5cadbcbbd7 Start flash specs 2010-02-07 21:30:28 -08:00
visionmedia 8ee0294672 Started Flash support 2010-02-07 21:29:15 -08:00
visionmedia 99789c3182 Bump Aaron up as a contributor 2010-02-07 21:11:29 -08:00
visionmedia 406a7f4fc7 Merge branch 'integration' 2010-02-07 21:02:23 -08:00
visionmedia 5a11f82e0e Docs 2010-02-07 21:02:21 -08:00
Aaron Heckmann 33eca37ec9 added accepts support for media groups
Example: this.accepts('html') will now return true when Accepts header contains 'text/*'.
Support for */* was not added since it seems a bit too blunt in the real world.
2010-02-06 23:20:42 -05:00
Aaron Heckmann fbd9cdd11e updated accepts comments 2010-02-06 22:36:05 -05:00
Aaron Heckmann 6ec6657512 accepts now allows multiple args. fixes #117 2010-02-06 21:10:43 -05:00
visionmedia 8e91d2039a Started high level HTTP api 2010-02-05 15:16:48 -08:00
visionmedia 1879648be7 Merge branch 'plugin-halt' 2010-02-05 13:56:11 -08:00
visionmedia 490770171d Hooks only exporting before()/after() 2010-02-05 13:56:07 -08:00
visionmedia 621063cc18 Added support for plugins to halt. Closes #189 2010-02-05 13:54:50 -08:00
visionmedia 821defc11b Hook callbacks exported 2010-02-05 13:52:54 -08:00
visionmedia dbc1709e0e Added failing before() hook halt spec 2010-02-05 13:43:20 -08:00
visionmedia 4d1bda0601 Removed Route#run(). Closes #188 2010-02-05 13:39:52 -08:00
visionmedia 1a9a3674c2 Fixed broken specs due to use(Cookie) missing 2010-02-05 13:08:58 -08:00
visionmedia 99b7e74422 Added alternate layout support. Closes #119
BAM~! lol would have been sooner i just have been focusing
on larger things like sessions
2010-02-05 13:08:28 -08:00
visionmedia add0a43c40 Merge branch 'integration' 2010-02-05 09:04:14 -08:00
visionmedia 3dc7c6a254 Merge branch 'dev' of git://github.com/aheckmann/express into integration 2010-02-05 09:04:08 -08:00
visionmedia e645123fbd Release 0.2.1 2010-02-05 09:03:55 -08:00
Aaron Heckmann 4b104db212 Merge commit 'express/master' into integration 2010-02-05 11:50:12 -05:00
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
ciaranj e125ae2c48 Merge branch 'master' of git://github.com/visionmedia/express 2010-01-06 18:34:42 +00:00
ciaranj 3f9cbbbffb Merge branch 'master' of git://github.com/visionmedia/express 2010-01-06 09:47:40 +00:00
ciaranj 6f7141a266 Merge branch 'master' of git://github.com/visionmedia/express 2010-01-05 23:04:50 +00:00
ciaranj 99e3130f3c Merge branch 'master' of git://github.com/visionmedia/express 2010-01-04 12:47:40 +00: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
54 arquivos alterados com 1853 adições e 289 exclusões
+3
Ver Arquivo
@@ -7,3 +7,6 @@
[submodule "lib/support/js-oo"]
path = lib/support/js-oo
url = git://github.com/visionmedia/js-oo.git
[submodule "lib/support/sass"]
path = lib/support/sass
url = git://github.com/visionmedia/sass.js.git
+61
Ver Arquivo
@@ -1,4 +1,65 @@
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
==================
+9 -2
Ver Arquivo
@@ -1,6 +1,8 @@
NODE = node
all: test
init:
@git submodule init && git submodule update
@@ -13,8 +15,13 @@ test-independant: init
test-dependant: init spec/support/libxmljs/libxmljs.node
@$(NODE) spec/node.js dependant
app:
@$(NODE) examples/app.js
app: app-chat
app-chat:
@$(NODE) examples/chat/app.js
app-upload:
@$(NODE) examples/upload/app.js
benchmark:
@$(NODE) benchmarks/collection.js
+7 -6
Ver Arquivo
@@ -11,6 +11,7 @@
* Sexy DSL with robust sinatra-like routing
* High performance
* Session support
* Mime helpers
* Redirection helpers
* Nested parameter parsing
@@ -21,7 +22,7 @@
* 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)
## Installation
@@ -49,16 +50,15 @@ Or with the [gh](http://github.com/visionmedia/gh) utility:
## Examples
require.paths.unshift('lib')
Below is a minimal app example when express is already within your load path.
require('express')
require('express/plugins')
configure(function(){
use(MethodOverride)
use(ContentLength)
use(Redirect)
set('root', dirname(__filename))
enable('cache views')
set('root', __dirname)
})
get('/hello', function(){
@@ -104,7 +104,7 @@ Run individual suites:
...
Express is currently being developed with node --version:
v0.1.24-13-ge2abc5f
v0.1.27
## More Information
@@ -115,6 +115,7 @@ Express is currently being developed with node --version:
## 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;
+29 -8
Ver Arquivo
@@ -8,20 +8,37 @@ require('express')
print = puts
engine = {
ejs: require('support/ejs/ejs'),
haml: require('support/haml/lib/haml')
ejs: require('ejs'),
haml: require('haml'),
sass: require('sass')
}
options = { locals: { article: { title: 'Foo', body: 'bar' }}}
ejs = ' \n\
<h1><%= article.title %></h1> \n\
<p><%= article.body %></p> \n\
ejs = ' \n\
<div id="primary"> \n\
<div class="block first"> \n\
<h1><%= article.title %></h1> \n\
<p><%= article.body %></p> \n\
</div> \n\
</div> \n\
'
haml = ' \n\
%h1= article.title\n\
%p= article.body \n\
haml = ' \n\
#primary \n\
.block.first \n\
%h1= article.title \n\
%p= article.body \n\
'
sass = ' \n\
red: #ff0000 \n\
body \n\
ul \n\
li \n\
a \n\
:color !red \n\
:list-style none \n\
'
suite('Template Engines', 1000, function(){
@@ -32,4 +49,8 @@ suite('Template Engines', 1000, function(){
benchmark('haml', function(){
engine.haml.render(haml, options)
})
benchmark('sass', function(){
engine.sass.render(sass)
})
})
+27 -6
Ver Arquivo
@@ -4,15 +4,21 @@ require('express')
require('express/plugins')
configure(function(){
var fiveMinutes = 300000,
oneMinute = 60000
use(MethodOverride)
use(ContentLength)
use(CommonLogger)
set('root', dirname(__filename))
enable('cache views')
use(Cookie)
use(Cache, { lifetime: fiveMinutes, reapInterval: oneMinute })
use(Session, { lifetime: fiveMinutes, reapInterval: oneMinute })
set('root', __dirname)
})
require('express/http')
var messages = [],
StaticFile = require('express/static').File
utils = require('express/utils')
get('/', function(){
this.redirect('/chat')
@@ -21,13 +27,20 @@ get('/', function(){
get('/chat', function(){
this.render('chat.haml.html', {
locals: {
messages: messages
title: 'Chat',
messages: messages,
name: this.session.name,
usersOnline: Session.store.length()
}
})
})
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.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)
})
@@ -44,7 +57,11 @@ get('/chat/messages', function(){
})
get('/public/*', function(file){
this.sendfile(dirname(__filename) + '/public/' + file)
this.sendfile(__dirname + '/public/' + file)
})
get('/*.css', function(file){
this.render(file + '.sass.css', { layout: false })
})
get('/error/view', function(){
@@ -59,4 +76,8 @@ 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
@@ -5,6 +5,6 @@
%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;
}
+45
Ver Arquivo
@@ -0,0 +1,45 @@
require.paths.unshift('lib')
require('express')
require('express/plugins')
configure(function(){
use(MethodOverride)
use(ContentLength)
use(CommonLogger)
use(Cookie)
use(Session)
use(Flash)
set('root', __dirname)
})
get('/', function(){
this.redirect('/upload')
})
get('/upload', function(){
this.render('upload.haml.html', {
locals: {
flashes: this.flash('info')
}
})
})
post('/upload', function(){
var self = this
$(this.param('images')).each(function(image){
puts(image.filename + ' -> ' + image.tempfile)
self.flash('info', 'Uploaded ' + image.filename)
})
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/js-oo/lib')
require.paths.unshift(__dirname + '/support/ejs/lib')
require.paths.unshift(__dirname + '/support/haml/lib')
require.paths.unshift(__dirname + '/support/sass/lib')
require('oo')
require('express/core')
+1 -2
Ver Arquivo
@@ -29,8 +29,7 @@ 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
if (property.test(fn) || method.test(fn)) fn = 'a.' + fn
return Function('a, b, c', 'return ' + fn)
}
+66 -21
Ver Arquivo
@@ -6,11 +6,18 @@ 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'),
events = require('events'),
File = require('file').File,
utils = require('express/utils')
// --- Route
Route = Class({
@@ -37,19 +44,6 @@ Route = Class({
this.fn = fn
},
/**
* Execute this route's #fn with _args_,
* against _context_ or GLOBAL.
*
* @param {array} args
* @return {mixed}
* @api private
*/
run: function(args, context) {
return this.fn.apply(context || GLOBAL, args)
},
/**
* Normalize _path_. When a RegExp it is simply returned,
* otherwise a string is converted to a regular expression
@@ -77,7 +71,7 @@ Route = Class({
var self = this
this.keys = []
if (path instanceof RegExp) return path
return new RegExp('^' + escapeRegexp(normalizePath(path), '.')
return new RegExp('^' + utils.escapeRegexp(normalizePath(path), '.')
.replace(/\*/g, '(.+)')
.replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
self.keys.push(key)
@@ -115,7 +109,7 @@ Router = Class({
route: function(){
var route = this.matchingRoute()
if (route)
return route.run(this.request.captures.slice(1), this.request)
return route.fn.apply(this.request, this.request.captures.slice(1))
else if (this.request.accepts('html') && set('helpful 404'))
this.request.halt(404, require('express/pages/not-found').render(this.request))
else
@@ -207,15 +201,59 @@ 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')
if (request.headers['content-type'] &&
request.headers['content-type'].indexOf('multipart/form-data') !== -1) {
var stream = new multipart.Stream(request),
promise = new events.Promise,
pendingFiles = 0
request.params = { post: {}}
promise.timeout(set('upload timeout') || 5000)
stream.addListener('part', function(part){
if (part.filename) {
var file = new File(part.tempfile = '/tmp/express-' + Number(new Date) + utils.uid(), 'w', { encoding: 'binary' })
++pendingFiles
part.pos = 0
part.addListener('body', function(chunk){
file.write(chunk, part.pos, 'binary').addErrback(function(){
promise.emitError.apply(promise, arguments)
})
part.pos += chunk.length
})
.addListener('complete', function(){
file.close().addCallback(function(){
if (!--pendingFiles)
promise.emitSuccess()
}).addErrback(function(){
promise.emitError.apply(promise, arguments)
})
utils.mergeParam(part.name, part, request.params.post)
})
}
else
part.buf = '',
part
.addListener('body', function(chunk){ part.buf += chunk })
.addListener('complete', function(){
if (part.buf.length)
utils.mergeParam(part.name, part.buf, request.params.post)
})
}).addListener('complete', function(){
if (!pendingFiles) promise.emitSuccess()
promise.addCallback(function(){ self.route(request, response) })
})
}
else
request
.addListener('body', function(chunk){ request.body += chunk })
.addListener('complete', function(){ self.route(request, response) })
})
.listen(this.port, this.host, this.backlog)
puts('Express started at http://' + this.host + ':' + this.port + '/ in ' + Express.environment + ' mode')
@@ -232,6 +270,7 @@ Server = Class({
route: function(request, response){
request = new Request(request, response)
request.trigger('request')
if (request.response.finished) return
try {
if (typeof (body = (new Router(request)).route()) == 'string')
request.halt(200, body)
@@ -252,7 +291,7 @@ Server = Class({
// --- Express
Express = {
version: '0.0.2',
version: '0.4.0',
config: [],
routes: [],
plugins: [],
@@ -264,6 +303,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 +316,8 @@ configure('development', function(){
configure('test', function(){
enable('throw exceptions')
})
configure('production', function(){
enable('cache view contents')
enable('cache static files')
})
+17 -2
Ver Arquivo
@@ -1,6 +1,12 @@
// Express - DSL - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var http = require('express/http')
/**
* Return a routing function for _method_.
*
@@ -13,7 +19,12 @@ function route(method) {
return function(path, options, fn){
if (options instanceof Function)
fn = options, options = {}
Express.routes.push(new Route(method, path, fn, options))
if (path.indexOf('http://') === 0)
return http[method].apply(this, arguments)
else if (!Express.server.running)
Express.routes.push(new Route(method, path, fn, options))
else
throw new Error('cannot create route ' + method.toUpperCase() + " `" + path + "' at runtime")
}
}
@@ -69,6 +80,10 @@ exports.disable = function(option) {
exports.run = function() {
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)
}
@@ -103,7 +118,7 @@ exports.configure = function(environment, fn) {
if (fn instanceof Function)
return Express.config.push([environment, fn])
if (typeof environment != 'string')
throw 'environment require'
throw new Error('environment required')
for (var i = 0, len = Express.config.length; i < len; ++i)
if (Express.config[i][0] == environment ||
Express.config[i][0] == 'all')
+3 -3
Ver Arquivo
@@ -2,7 +2,7 @@
// Express - ElementCollection - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Load libxml support.
* Module dependencies.
*/
var libxml = require('libxmljs')
@@ -126,7 +126,7 @@ ElementCollection = Collection.extend({
*/
prev: function() {
return $([this.at(0).prev_sibling()])
return $([this.at(0).prevSibling()])
},
/**
@@ -137,7 +137,7 @@ ElementCollection = Collection.extend({
*/
next: function() {
return $([this.at(0).next_sibling()])
return $([this.at(0).nextSibling()])
},
/**
+79
Ver Arquivo
@@ -0,0 +1,79 @@
// Express - HTTP - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var http = require('http'),
events = require('events'),
parse = require('url').parse,
queryString = require('querystring')
/**
* Request using the given _method_, _url_
* followed by optional _headers_, and _data_.
*
* @param {string} method
* @param {string} url
* @param {hash} headers
* @param {hash} data
* @param {Promise} promise
* @return {Promise}
* @api private
*/
function request(method, url, headers, data, promise) {
var buf = '',
promise = promise || new events.Promise,
url = parse(url),
path = url.pathname || '/',
search = url.search || '',
hash = url.hash || '',
port = url.port || 80,
headers = process.mixin(headers, { host: url.hostname }),
client = http.createClient(port, url.hostname)
client.addListener('error', function(e){
promise.emitError(new Error("client failed to " + method + " `" + url.href + "'"))
})
if (data) {
data = queryString.stringify(data)
headers['content-length'] = data.length
headers['content-type'] = 'application/x-www-form-urlencoded'
}
var request = client.request(method, path + search + hash, headers)
if (data) request.sendBody(data)
request.finish(function(response){
if (response.statusCode < 200 || response.statusCode >= 400)
promise.emitError(new Error('request failed with status ' + response.statusCode + ' "' + http.STATUS_CODES[response.statusCode] + '"'))
else if (response.statusCode >= 300 && response.statusCode < 400)
request(method, response.headers.location, headers, data, promise)
else {
response.setBodyEncoding('utf8')
response
.addListener('body', function(chunk){ buf += chunk })
.addListener('complete', function(){ promise.emitSuccess(buf, response) })
}
})
return promise
}
/**
* Return HTTP Client function for the given _method_,
* which optionally may _allowData_ to be passed.
*
* @param {string} method
* @param {bool} allowData
* @return {function}
* @api private
*/
function client(method, allowData) {
return function(url, headers, data) {
if (allowData) data = data || {}
return request(method.toUpperCase(), url, headers, data)
}
}
// --- Public API
exports.get = exports.view = client('get')
exports.post = exports.create = client('post', true)
exports.put = exports.update = client('put', true)
exports.del = exports.destroy = client('delete', true)
+182 -9
Ver Arquivo
@@ -1,136 +1,278 @@
// Express - Mime - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var types = {
/**
* Module dependencies.
*/
var utils = require('express/utils')
/**
* 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[utils.extname(path)] ||
set('default mime type') ||
'application/octet-stream'
}
}
+8 -1
Ver Arquivo
@@ -3,6 +3,8 @@
/**
* 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 +12,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
+3
Ver Arquivo
@@ -2,7 +2,10 @@
// Express - Plugins - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
process.mixin(require('express/plugins/hooks'))
process.mixin(require('express/plugins/flash'))
process.mixin(require('express/plugins/cache'))
process.mixin(require('express/plugins/cookie'))
process.mixin(require('express/plugins/session'))
process.mixin(require('express/plugins/profiler'))
process.mixin(require('express/plugins/common-logger'))
process.mixin(require('express/plugins/content-length'))
+218
Ver Arquivo
@@ -0,0 +1,218 @@
// 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) {
process.mixin(this, 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)
}
}
})
+10 -7
Ver Arquivo
@@ -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
}
}
})
}
}
})
+9 -9
Ver Arquivo
@@ -1,8 +1,7 @@
// Express - Hooks - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var before = [],
after = []
exports.callbacks = { before: [], after: [] }
/**
* Add a _fn_ to be excuted before a request.
@@ -12,7 +11,7 @@ var before = [],
*/
exports.before = function(fn) {
before.push(fn)
exports.callbacks.before.push(fn)
}
/**
@@ -23,7 +22,7 @@ exports.before = function(fn) {
*/
exports.after = function(fn) {
after.push(fn)
exports.callbacks.after.push(fn)
}
// --- Hooks
@@ -36,7 +35,8 @@ exports.Hooks = Plugin.extend({
*/
init: function() {
process.mixin(GLOBAL, exports)
process.mixin(GLOBAL, { before: exports.before,
after: exports.after })
}
},
@@ -49,8 +49,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 +59,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)
})
}
}
+29 -3
Ver Arquivo
@@ -1,7 +1,29 @@
// Express - Profiler - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var n = 0
exports.Profiler = Plugin.extend({
extend: {
/**
* Initialize profiler options.
*
* Options:
*
* - format 'plot' outputs request duration in milliseconds only
*
* @param {hash} options
* @api private
*/
init: function(options) {
process.mixin(this, options)
}
},
// --- Events
on: {
/**
@@ -17,9 +39,13 @@ exports.Profiler = Plugin.extend({
*/
response: function(event) {
puts(event.request.method + ' ' +
event.request.url.pathname + ': ' +
(Number(new Date) - this.start) + ' ms')
if (exports.Profiler.format === 'plot')
puts(Number(new Date) - this.start)
else
puts(event.request.method + ' ' +
event.request.url.pathname + ': ' +
(Number(new Date) - this.start) + ' ms' +
' #' + ++n)
}
}
})
+205
Ver Arquivo
@@ -0,0 +1,205 @@
// 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).length()
},
/**
* Reap sessions older than _ms_.
*
* @param {int} ms
* @api private
*/
reap: function(ms) {
var self = this,
threshold = Number(new Date(Number(new Date) - ms))
$(this.store).each(function(session, sid){
if (session.lastAccess < threshold)
self.destroy(sid)
})
}
})
// --- 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) {
process.mixin(this, options)
this.store = new (this.dataStore || exports.Store.Memory)(options)
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)
}
},
// --- 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)
}
}
})
+19 -15
Ver Arquivo
@@ -1,21 +1,21 @@
// Express - View - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var posix = require('posix')
/**
* Template content cache.
* Module dependencies.
*/
var cache = {}
var posix = require('posix'),
utils = require('express/utils')
/**
* Supported template engines.
*/
var engine = {
ejs: require('support/ejs/ejs'),
haml: require('support/haml/lib/haml')
ejs: require('ejs'),
haml: require('haml'),
sass: require('sass')
}
// --- View
@@ -51,13 +51,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
*/
@@ -66,13 +67,14 @@ exports.View = Plugin.extend({
options = options || {},
path = set('views') + '/' + view,
type = path.split('.').slice(-2)[0],
ext = extname(path),
layout = options.layout === undefined ? true : options.layout
ext = utils.extname(path),
layout = options.layout === undefined ? 'layout' : options.layout
options.context = options.context || this
self.contentType(ext)
function render(content) {
content = engine[type].render(content, options)
if (layout)
self.render('layout.' + type + '.' + ext, process.mixin(options, {
self.render(layout + '.' + type + '.' + ext, process.mixin(true, options, {
layout: false,
locals: {
body: content
@@ -81,11 +83,13 @@ 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)
set('cache view contents') ?
render(self.cache.set(path, content)) :
render(content)
}).addErrback(function(e){
throw e
})
+24 -14
Ver Arquivo
@@ -8,6 +8,7 @@
var StaticFile = require('express/static').File,
statusBodies = require('http').STATUS_CODES,
queryString = require('querystring'),
mime = require('express/mime'),
url = require('url')
// --- InvalidStatusCode
@@ -67,11 +68,10 @@ exports.Request = Class({
this.response = response
this.url = url.parse(this.url)
this.url.pathname = exports.normalizePath(this.url.pathname)
this.params = {
get: this.url.query ? queryString.parseQuery(this.url.query) : {},
post: {},
path: {}
}
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)
})
@@ -113,20 +113,31 @@ 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 :
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
},
@@ -187,16 +198,15 @@ exports.Request = Class({
/**
* Set Content-Type header to the mime type
* for the given _path_, which calls mime().
* for the given _path_, which calls mime.type().
*
* @param {string} path
* @return {Request}
* @see mime()
* @api public
*/
contentType: function(path) {
this.header('content-type', mime(path))
this.header('content-type', mime.type(path))
return this
},
+10 -3
Ver Arquivo
@@ -35,19 +35,26 @@ 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()
if (!exists) return request.halt()
posix.stat(file).addCallback(function(stats){
if (!stats.isFile()) request.halt()
if (!stats.isFile()) return request.halt()
posix.cat(file, 'binary').addCallback(function(content){
request.contentType(file)
if (set('cache static files'))
request.cache.set(file, { type: file, content: content })
request.halt(200, content, 'binary')
})
})
+43 -4
Ver Arquivo
@@ -1,6 +1,12 @@
// Express - Helpers - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var queryString = require('querystring')
/**
* JSON aliases.
*/
@@ -9,15 +15,17 @@ JSON.encode = JSON.stringify
JSON.decode = JSON.parse
/**
* Return the directory name of the given _path_.
* Return a unique identifier.
*
* @param {string} path
* @return {string}
* @api public
*/
exports.dirname = function(path) {
return path.split('/').slice(0, -1).join('/')
exports.uid = function() {
var uid = ''
for (var n = 4; n; --n)
uid += (Math.abs((Math.random() * 0xFFFFFFF) | 0)).toString(16)
return uid
}
/**
@@ -92,4 +100,35 @@ exports.toArray = function(arr, offset) {
exports.escapeRegexp = function(string, chars) {
var specials = (chars || '/ . * + ? | ( ) [ ] { } \\').split(' ').join('|\\')
return string.replace(new RegExp('(\\' + specials + ')', 'g'), '\\$1')
}
/**
* 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(queryString.parseQuery(key), 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]
})
return orig
}
+1
Submodule lib/support/sass added at 0dd500e7cd
+5 -5
Ver Arquivo
@@ -4,7 +4,7 @@
;(function(){
JSpec = {
version : '3.1.0',
version : '3.2.1',
assert : true,
cache : {},
suites : [],
@@ -1046,7 +1046,7 @@
*/
expect : function(actual) {
assert = function(matcher, args, negate) {
function assert(matcher, args, negate) {
var expected = toArray(args, 1)
matcher.negate = negate
assertion = new JSpec.Assertion(matcher, actual, expected, negate)
@@ -1056,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)
}
@@ -1692,7 +1692,7 @@
case Number:
case RegExp:
case Function:
state = actual.toString().match(arg.toString())
state = actual.toString().indexOf(arg) !== -1
break
case Object:
+5 -1
Ver Arquivo
@@ -27,12 +27,13 @@ specs = {
independant: [
'core',
'routing',
'helpers',
'utils',
'request',
'mime',
'static',
'collection',
'plugins',
'plugins.cache',
'plugins.view',
'plugins.common-logger',
'plugins.content-length',
@@ -41,6 +42,8 @@ specs = {
'plugins.redirect',
'plugins.hooks',
'plugins.cookie',
'plugins.session',
'plugins.flash',
],
dependant: [
'element-collection'
@@ -62,4 +65,5 @@ switch (process.ARGV[2]) {
run([process.ARGV[2]])
}
Express.environment = 'test'
JSpec.run({ reporter: JSpec.reporters.Terminal, failuresOnly: true }).report()
-48
Ver Arquivo
@@ -1,48 +0,0 @@
describe 'Express'
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
+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
+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')
}
}
+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
+24 -1
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()'
@@ -174,7 +197,7 @@ describe 'Express'
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?page=2').body.should.eql '2'
+138
Ver Arquivo
@@ -0,0 +1,138 @@
describe 'Express'
before
utils = require('express/utils')
end
describe 'toArray()'
describe 'when given an array'
it 'should return the array'
utils.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')
utils.toArray(args).should.eql ['foo', 'bar']
end
end
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 'extname()'
it 'should return the a files extension'
utils.extname('image.png').should.eql 'png'
utils.extname('image.large.png').should.eql 'png'
utils.extname('/path/to/image.large.png').should.eql 'png'
end
it 'should return null when not found'
utils.extname('path').should.be_null
utils.extname('/just/a/path').should.be_null
end
end
describe 'basename()'
it 'should return a files basename'
utils.basename('foo/bar/baz.image.png').should.eql 'baz.image.png'
end
end
describe 'mergeParam()'
describe 'with empty params'
it 'should merge the given key and value'
params = {}
utils.mergeParam('user[names][first]', 'tj', params)
params.user.names.first.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