Comparar commits

...

336 Commits

Autor SHA1 Mensagem Data
visionmedia 1255331160 Release 0.9.0 2010-04-14 16:56:13 -07:00
visionmedia 0fef51b5f6 Updated git submodules 2010-04-14 16:41:42 -07:00
visionmedia 7cf18c0468 Merge branch 'fix-querystring-0' 2010-04-13 13:25:55 -07:00
visionmedia b994509b9d Fixed bug preventing falsey params. Closes #286
Request#param() would prevously fail with ?page=0 for example
when param("page") is used since it would return undefined and not 0
2010-04-13 13:25:44 -07:00
visionmedia 6571e19e8f Added spec for #286 2010-04-13 13:17:27 -07:00
visionmedia 7441164db9 Merge branch 'integration' 2010-04-12 20:40:03 -07:00
visionmedia 09bb10cd3f Merge branch 'download' of git://github.com/aheckmann/express into integration 2010-04-12 20:39:20 -07:00
Aaron Heckmann bd6f24f4b1 add cookie support to readme 2010-04-12 22:56:59 -04:00
Aaron Heckmann 0a174ad219 move Request#download to Static plugin
#download() was a hangover from before the Static plugin existed and no longer worked. Also added missing specs so this won't happen again.
2010-04-12 22:49:49 -04:00
visionmedia 33d004fb9a Fixed MockResponse to match node API 2010-04-12 11:04:52 -07:00
visionmedia 652bf34c77 Merge branch 'integration' 2010-04-12 10:40:08 -07:00
Aaron Heckmann 3f11bad68e fix FileWriteStream.prototype.close renamed to end() 2010-04-11 23:10:41 -04:00
Aaron Heckmann 5245fbfc6d fix OutgoingMessage.prototype.close has been renamed to end() 2010-04-11 23:06:09 -04:00
visionmedia 68f8ee8a99 Fixed "express" executable due to node 0.1.90 changes 2010-04-11 18:39:14 -07:00
visionmedia 7b83a52dce http client fix from node renaming close to end 2010-04-11 18:10:58 -07:00
visionmedia 5f57c82478 Merge branch 'integration' 2010-04-11 18:08:05 -07:00
visionmedia 3128cbef73 Removed TODO comment
added an issue http://github.com/visionmedia/express/issues/issue/285
2010-04-11 18:08:00 -07:00
visionmedia 694b2bf055 Merge branch 'node_0.1.9_fixes' of git://github.com/ciaranj/express into integration 2010-04-11 18:04:31 -07:00
visionmedia e03a023c5f Merge branch 'integration' 2010-04-11 17:45:19 -07:00
ciaranj 3ff5d3bd09 Temporarily add back in the old multipart support
This will at least get us going again whilst decisions are made :)
2010-04-10 10:06:04 +01:00
ciaranj 04841196e3 Fixed the cookie test so it will work in different timezones.
Tj, I'm not sure what timezone you were in when you wrote the test, but
I've guessed that it was PDT ?   Hopefully this fix allows the test to
run in any timezone :) .. I was surprised that cookies don't use UTC which
could've mate things a lot cleaner, meh, old tech eh.
2010-04-10 09:59:41 +01:00
visionmedia 18dd495d99 Revert "Fixed relative require of Class"
This reverts commit f714945bbb.
2010-04-08 16:09:06 -07:00
visionmedia f714945bbb Fixed relative require of Class 2010-04-08 15:43:33 -07:00
visionmedia a318ef2232 Cookie expires now using Date#toGMTString()
Spec I found wanted hyphens, but this appears to be the norm
for JS cookies
2010-04-06 19:40:59 -07:00
visionmedia 8ed24de9d3 Updated haml submodule 2010-04-06 15:32:36 -07:00
visionmedia e06e50936d Updated sass submodule 2010-04-06 14:38:06 -07:00
visionmedia d710e9cb47 Merge branch 'integration' 2010-04-06 11:01:23 -07:00
Aaron Heckmann 9af3699850 utilize Date.now() 2010-04-04 12:50:11 -04:00
visionmedia 3cc7b4d8c2 Utilizing Date.now() 2010-04-02 14:12:36 -07:00
visionmedia 8b58732a1f Merge branch 'globals' 2010-04-02 13:35:04 -07:00
visionmedia a2a0935343 Request is no longer global 2010-04-02 13:13:26 -07:00
visionmedia 2668156e53 Event is no longer global 2010-04-02 13:07:54 -07:00
visionmedia d67615d239 puts() -> sys.puts() 2010-04-02 13:06:25 -07:00
visionmedia afd70b64ed Removed "sys" global merge 2010-04-02 13:05:18 -07:00
visionmedia 78a6e43667 Request#render() now only sets option.cache to true when set("cache view contents") is true
to allow development editing of the views
2010-03-31 10:36:52 -07:00
visionmedia a65af97e43 Docs 2010-03-31 10:28:40 -07:00
visionmedia 637dfabe69 render() always passing "cache" and "filename" options
if the engine does not use them, then they
will be ignored. However this already bumps
haml.js by about %25 in terms of performance
2010-03-31 10:20:24 -07:00
visionmedia ff8b3c10f3 Updated haml submodule 2010-03-31 10:14:29 -07:00
visionmedia f442555d8f Updated haml submodule 2010-03-31 08:10:28 -07:00
visionmedia bdf9f882ad Merge branch 'better-haml' 2010-03-31 07:51:00 -07:00
visionmedia 1e5c5bfe00 Switching around partial filename 2010-03-31 07:50:11 -07:00
visionmedia 0e78fdfcb4 Templates using haml.js instead of haml-js 2010-03-29 10:23:22 -07:00
visionmedia 4f532f86dc Added haml.js submodule; removed haml-js 2010-03-29 09:21:33 -07:00
visionmedia 92844825cb Always httpOnly session cookie 2010-03-29 08:37:57 -07:00
visionmedia 6b13fc99b0 Merge branch 'flip-views' 2010-03-29 08:25:23 -07:00
visionmedia 0f7aa26757 Added deprecation warning for views 2010-03-29 08:25:18 -07:00
visionmedia a06f963263 Merge branch 'flipViewExt' of git://github.com/aheckmann/express into flip-views 2010-03-29 07:58:13 -07:00
visionmedia d31de1e654 Defaulting httpOnly for Session cookies, however overridable 2010-03-29 07:55:37 -07:00
Aaron Heckmann 46a0301022 session cookie now httpOnly 2010-03-27 00:21:37 -04:00
Aaron Heckmann 73d26036ef updated specs and example apps
all tests passing
2010-03-27 00:00:49 -04:00
Aaron Heckmann b9637c9d7d started flipping view extensions 2010-03-27 00:00:49 -04:00
visionmedia 08497683bf Fixed setting of multiple cookies. Closes #199 2010-03-26 16:08:39 -07:00
visionmedia 44a50f6e58 Moved require("http") out of Server#run() 2010-03-26 07:16:55 -07:00
visionmedia 4c2b4e5c66 Removed unused require 2010-03-26 07:16:12 -07:00
visionmedia d33c38f671 Merge branch 'integration' 2010-03-25 15:57:08 -07:00
Aaron Heckmann fbfba21854 multipart parse only when needed 2010-03-25 18:03:32 -04:00
visionmedia b2093d6f10 Merge branch 'partial-index-and-length' 2010-03-25 12:47:22 -07:00
visionmedia 736a0190c1 Passing __index__, __length__, __isFirst__, __isLast__ to collection partials. Closes #254 2010-03-25 12:47:17 -07:00
visionmedia a74c259c38 Request instance created before body parsing. Closes #262
Merge branch 'request'
2010-03-25 11:48:35 -07:00
visionmedia d3dedd6312 Fixed post param issue 2010-03-25 11:44:55 -07:00
visionmedia 920eab0ef9 Fixed mocks to work with new routing api 2010-03-25 11:37:56 -07:00
visionmedia d576085e8d . 2010-03-25 11:09:50 -07:00
visionmedia 62b9a9e287 Docs 2010-03-25 10:49:25 -07:00
visionmedia 56ffe1d62a Merge branch 'upload-limit' 2010-03-25 10:49:00 -07:00
visionmedia 14acbcb5f1 Added "max upload size" setting 2010-03-25 10:48:55 -07:00
visionmedia 9a0011bf49 Updated ext. Closes #256
Should be fixed now :)
2010-03-25 09:29:52 -07:00
visionmedia 7b9f18b097 Added Request#render() callback function. Closes #258 2010-03-25 08:53:47 -07:00
visionmedia f8e4333157 Merge branch 'integration' 2010-03-25 08:40:47 -07:00
visionmedia b5c933aa94 fn -> callback 2010-03-25 08:40:18 -07:00
visionmedia c441af3f2c Typo 2010-03-25 08:39:48 -07:00
visionmedia e885421a67 Merge branch 'master' of git://github.com/aheckmann/express into integration 2010-03-25 08:38:54 -07:00
visionmedia ce4fe24a93 Updated to JSpec 4.0.0 2010-03-25 08:25:38 -07:00
visionmedia 1c0e2ceba5 error() is passed the exception 2010-03-25 08:24:19 -07:00
Aaron Heckmann a435e8ec47 fn -> callback 2010-03-25 11:22:48 -04:00
visionmedia b958393135 Added DSL level error() route support 2010-03-25 08:21:49 -07:00
visionmedia a6dd697a68 Added DSL level notFound() route support 2010-03-25 08:13:22 -07:00
visionmedia a4833e7b35 Added specs for Request#notFound() 2010-03-25 07:58:28 -07:00
visionmedia 54fa643c10 More Request#error() specs 2010-03-25 07:54:07 -07:00
visionmedia 6047cd4542 Added specs for Request#error() 2010-03-25 07:51:19 -07:00
visionmedia 8c5f7df280 Merge branch 'errors' 2010-03-25 07:35:44 -07:00
visionmedia ff1250c33c Added publish Request#notFound() 2010-03-25 07:35:39 -07:00
visionmedia 4297c10fe9 Removed Express.error(), Added public Request#error() 2010-03-25 07:18:55 -07:00
visionmedia ab6ad94ec3 Request#halt() accepts callback function as 3rd/4th arg 2010-03-24 16:05:34 -07:00
visionmedia 759183461f Merge branch 'error-handling' 2010-03-24 15:44:50 -07:00
visionmedia f850fa6bc9 Misc error handling improvements 2010-03-24 15:44:46 -07:00
visionmedia 5a242d35b7 Removed unused variable 2010-03-24 15:15:45 -07:00
visionmedia abf5d66e01 Merge branch 'error-handling' 2010-03-24 15:08:51 -07:00
visionmedia e92360e0d4 Caching notFound / showException modules 2010-03-24 15:08:06 -07:00
visionmedia b5c0fdc013 Express.error() now acts as the core exception handler 2010-03-24 14:54:13 -07:00
visionmedia 906ef02c5e request response event is now fired in reverse.
This does a few things:
  a. Allows plugins such as Logger to properly "wrap" all
     other plugins when it is the first passed to use().

  b. Lessons the chance of a plugin failure effecting another plugin
2010-03-24 14:45:46 -07:00
visionmedia 7550decbc0 Handle when a plugin response fails 2010-03-24 14:32:08 -07:00
visionmedia acd88c3c8a next -> callback 2010-03-24 12:56:26 -07:00
visionmedia a3854a2de1 Styling 2010-03-24 12:54:52 -07:00
visionmedia 880aca5d83 Removed set("session cookie") in favour of use(Session, { cookie: { ... }}) 2010-03-24 12:53:33 -07:00
visionmedia 2d1c98a5a7 Docs for stable / edge 2010-03-24 08:33:43 -07:00
visionmedia fefa06ba21 Merge branch 'route-wildcards' 2010-03-23 10:41:31 -07:00
visionmedia 18faa91c94 Added preprocessing of route param wildcards using param(). Closes #251
Async is yet to come
2010-03-23 10:41:25 -07:00
visionmedia 9f23c7b31a Added specs for param() 2010-03-23 10:31:46 -07:00
visionmedia eee6926bab Added more route wildcard specs 2010-03-23 10:11:52 -07:00
visionmedia 6806f952d6 Merge branch 'pre-cache-views' 2010-03-23 09:53:23 -07:00
visionmedia faaef54b42 Pre-caching views in memory. Closes #253
This mirrors the partial caching functionality. This
causes a slight overhead at boot time, however prevents
the need to perform cache checks per render() call.
2010-03-23 09:53:17 -07:00
visionmedia c29852fae7 Started pre caching of views 2010-03-23 09:42:23 -07:00
visionmedia 7d100dae97 Merge branch 'integration' 2010-03-22 15:34:50 -07:00
visionmedia 206e800963 Added assertion to ensure that partials dir is relative to set("views") 2010-03-22 15:34:46 -07:00
visionmedia 555a334315 Re-using variables 2010-03-22 15:33:08 -07:00
visionmedia d2c5def108 Using set("views") when setting set("partials")
So that /partials is always relative by default
2010-03-22 15:31:39 -07:00
visionmedia 98323c530e partialscache -> partials 2010-03-22 15:30:34 -07:00
Aaron Heckmann b36510dbb9 preload partials
add settings
bug fix, partial now works when cache view contents is true
2010-03-22 18:16:40 -04:00
Aaron Heckmann 50e533c32b add cache view partials 2010-03-22 16:40:35 -04:00
visionmedia 33277c3d37 Removed utils.mixin() use Object#mergeDeep()
jQuery is messy, and this implementation is way to
slow for SSJS, and just gross :)
2010-03-22 11:28:23 -07:00
visionmedia 28f2ad0109 Merge branch 'partials' 2010-03-22 10:21:42 -07:00
visionmedia 5d10ee4e61 Chat sample app using partials as an example 2010-03-22 10:21:37 -07:00
visionmedia b4190ada0c Added partial "as" option 2010-03-22 10:16:12 -07:00
visionmedia 60d314552d Partial collection should not introduce newlines 2010-03-22 10:13:25 -07:00
visionmedia c971d54543 Added partial collection support 2010-03-22 10:12:52 -07:00
visionmedia 346f019fa8 Started view partial support 2010-03-22 10:05:05 -07:00
visionmedia d61c2480b6 Updated ext submodule 2010-03-22 09:12:24 -07:00
visionmedia e30b5b86da Removed Request#_blendInNodeRequest() 2010-03-22 09:12:09 -07:00
visionmedia bd74fe24fd Merge branch 'net2_fixes' of git://github.com/ciaranj/express 2010-03-22 09:05:58 -07:00
visionmedia b30eaa8ee3 Updated support to v0.1.33 2010-03-22 08:34:05 -07:00
ciaranj 75c530516a Various minor fixes required to make express work post the net2 merge branch 2010-03-20 12:26:37 +00:00
visionmedia 3b49821e82 Release 0.8.0 2010-03-19 16:51:43 -07:00
visionmedia febf443960 Fixed session reaper 2010-03-19 15:57:25 -07:00
visionmedia f5da81e782 Removed a comment from chat app 2010-03-19 14:38:26 -07:00
visionmedia 9cb23ac584 Merge branch 'coffee' 2010-03-19 14:14:52 -07:00
visionmedia 6ff3100c1f Added coffeescript example app. Closes #242 2010-03-19 14:14:46 -07:00
visionmedia e3ea3723bf Added dir for coffeescript example 2010-03-19 13:56:31 -07:00
visionmedia 33443d9b41 Merge branch 'integration' 2010-03-19 10:13:26 -07:00
visionmedia e0ef61659f Merge branch 'async-session' into integration 2010-03-19 10:12:56 -07:00
visionmedia 655ad77bce fn -> callback 2010-03-19 09:55:06 -07:00
visionmedia c09e546240 Removed session reaper try/catch
we can revisit this issue later,
and deal with it across Cache as well
2010-03-19 09:53:33 -07:00
visionmedia a08b14a89e Misc Session refactoring 2010-03-19 09:52:18 -07:00
visionmedia 12e90b0eef Merge branch 'session_with_callbacks' of git://github.com/ciaranj/express into async-session 2010-03-19 09:41:30 -07:00
visionmedia 7606f1bcbd Merge branch 'async-cache' 2010-03-19 09:38:47 -07:00
visionmedia cabb43b187 Finished converting async cache api. Closes #240 2010-03-19 09:38:42 -07:00
visionmedia b85cda3f6e Cache api is now async 2010-03-19 09:19:22 -07:00
visionmedia b92f1b7497 Async cache specs 2010-03-19 09:04:31 -07:00
visionmedia 273a51a335 Release 0.7.6 2010-03-19 08:55:52 -07:00
ciaranj 212dc88b89 Squashed commit of the changes I've put in place to support callback
methods on the session object.

This code changes the behaviour of the session handling in a fairly key
way:

If an unknown session id is seen then a *new* session is created with a *new* session
id.  The previous behaviour was to create a new session with the existing id.
2010-03-19 13:55:51 +00:00
visionmedia ce82a91d14 Revert "Fail to test CI"
This reverts commit 52a08401c7.
2010-03-18 16:20:53 -07:00
visionmedia 52a08401c7 Fail to test CI 2010-03-18 16:20:12 -07:00
visionmedia 706c0cb033 View support listed in features already 2010-03-18 16:19:39 -07:00
visionmedia 1567a613a6 Closes #63.
Merge branch 'cli'
2010-03-18 13:02:00 -07:00
visionmedia d767d80d20 Added make install 2010-03-18 13:01:28 -07:00
visionmedia 1103a9d510 Moved a helper function to the helpers section 2010-03-18 13:00:34 -07:00
visionmedia c34af4b97a Started executable 2010-03-18 12:57:44 -07:00
visionmedia 8dcee4d338 Request#render() only calls cache.get() once 2010-03-18 11:40:48 -07:00
visionmedia fc1e69ea73 Namespacing View caches with "view:" 2010-03-18 11:40:04 -07:00
visionmedia db460117c1 Namespacing Static caches with "static:" 2010-03-18 11:39:23 -07:00
visionmedia 1c514df0fd Fixed ternary style 2010-03-18 11:37:34 -07:00
visionmedia 780ec5cbec Docs 2010-03-18 11:27:15 -07:00
visionmedia 3721873b62 Both example apps now use Static 2010-03-18 11:23:07 -07:00
visionmedia 3bdc77ca91 Deprecated Request#sendfile() and express/static 2010-03-18 11:22:40 -07:00
visionmedia b8f54d64a4 Closes #88.
Merge branch 'runtime-routes'
2010-03-18 11:17:16 -07:00
visionmedia 90d48b320a Added "GET /public/*" to Static plugin, defaulting to <root>/public 2010-03-18 11:16:55 -07:00
visionmedia a797342800 Static is now a plugin 2010-03-18 11:09:25 -07:00
visionmedia 31e608e0ae Added Request#sendfile() specs 2010-03-18 10:58:50 -07:00
visionmedia ebcfb0e577 Removed Server#running 2010-03-18 10:31:40 -07:00
visionmedia 161c9f9142 Closes #52
fs.readFile becomes fs.readFileSync when testing

Merge branch 'view-specs'
2010-03-18 10:17:03 -07:00
visionmedia 2a099da726 Fixed a few specs 2010-03-18 10:16:14 -07:00
visionmedia 5118397004 Added auto content-type view spec 2010-03-18 10:14:10 -07:00
visionmedia 546c340656 Added view custom context spec 2010-03-18 10:12:20 -07:00
visionmedia 3034cc7c52 View context spec 2010-03-18 10:10:10 -07:00
visionmedia d89381ff78 Added layout local var access spec 2010-03-18 10:04:25 -07:00
visionmedia 86cac58100 Added view locals spec 2010-03-18 09:54:29 -07:00
visionmedia 8fc062007e Added spec for invalid view engine 2010-03-18 09:51:48 -07:00
visionmedia 9fd92e3306 Added view caching specs 2010-03-18 09:50:16 -07:00
visionmedia a4cbbe5dd4 Spec for layout: false 2010-03-18 09:36:57 -07:00
visionmedia da7ea9655f Spec for custom layout 2010-03-18 09:36:12 -07:00
visionmedia 00a8c642dd Started view specs 2010-03-18 09:32:32 -07:00
visionmedia e1152f6e56 Typo 2010-03-18 09:12:46 -07:00
visionmedia 7cf9c6842a Merge branch 'fix-views' 2010-03-18 09:08:06 -07:00
visionmedia d375535cd9 Fixed set("views"). Closes #239
This actually should have never worked (if it even did).
My bad :)
2010-03-18 09:08:02 -07:00
visionmedia c5ea868f47 Merge branch 'bugfix' of git://github.com/aheckmann/express 2010-03-18 08:02:23 -07:00
visionmedia 884d333805 Added Request#isXHR. Closes #229
Merge branch 'integration'
2010-03-18 07:55:30 -07:00
visionmedia 3b9921a15b Added Request#isXHR specs 2010-03-18 07:54:34 -07:00
Aaron Heckmann 17b753d95e add missing space to combined log format 2010-03-17 22:38:45 -04:00
Aaron Heckmann 6a8781b5e2 add request.isXHR 2010-03-17 22:16:13 -04:00
Aaron Heckmann 0dfaf01749 add git clone to git command 2010-03-17 21:40:13 -04:00
visionmedia 088aa83e22 Updated feature list 2010-03-17 16:07:53 -07:00
visionmedia 934adb1e9f Release 0.7.5 2010-03-16 19:38:21 -07:00
visionmedia 50e0593de6 Request#flash() without args now returns all flashes 2010-03-16 19:34:11 -07:00
visionmedia f2637c6421 Fixed Request#flash() specs 2010-03-16 19:22:10 -07:00
visionmedia a52dacea68 Updated ext 2010-03-16 19:12:15 -07:00
visionmedia f56a33d22d Release 0.7.4 2010-03-16 16:14:48 -07:00
visionmedia 2aa858b0fb Merge branch 'class' 2010-03-16 16:12:42 -07:00
visionmedia c3b8ba4b9a NewClass -> Class, removed js-oo dependency 2010-03-16 16:12:14 -07:00
visionmedia ac96b8c0e1 Update class.js submodule with Class#include() 2010-03-16 16:10:39 -07:00
visionmedia a329a9f4d8 Converted more to use class.js 2010-03-16 15:53:32 -07:00
visionmedia 63cc7eb44d Converted more to use class.js 2010-03-16 15:50:17 -07:00
visionmedia 6505f32221 Started conversion of js-oo Class -> class.js 2010-03-16 15:46:22 -07:00
visionmedia 22088260f4 Fixed session reaper 2010-03-16 13:25:39 -07:00
visionmedia d893009a8d Release 0.7.3 2010-03-16 13:17:41 -07:00
visionmedia b62e1741be Fixed requiring of haml / sass 2010-03-16 13:16:55 -07:00
visionmedia 903c2aa642 Added package.json 2010-03-16 08:31:33 -07:00
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 8e91d2039a Started high level HTTP api 2010-02-05 15:16:48 -08:00
87 arquivos alterados com 2464 adições e 2688 exclusões
+2
Ver Arquivo
@@ -1 +1,3 @@
.DS_Store
*.seed
*.log
+12 -9
Ver Arquivo
@@ -1,12 +1,15 @@
[submodule "spec/support/libxmljs"]
path = spec/support/libxmljs
url = git://github.com/sprsquish/libxmljs.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
[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/class"]
path = lib/support/class
url = git://github.com/visionmedia/class.js.git
[submodule "lib/support/haml"]
path = lib/support/haml
url = git://github.com/visionmedia/haml.js.git
[submodule "lib/support/multipart"]
path = lib/support/multipart
url = git://github.com/isaacs/multipart-js.git
+180
Ver Arquivo
@@ -1,4 +1,184 @@
0.9.0 / 2010-04-14
==================
* Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js
* Fixed bug preventing falsey params (such as ?page=0). Closes #286
* Fixed setting of multiple cookies. Closes #199
* Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml)
* Changed; session cookie is now httpOnly
* Changed; Request is no longer global
* Changed; Event is no longer global
* Changed; "sys" module is no longer global
* Changed; moved Request#download to Static plugin where it belongs
* Updated support to node --version 0.1.90
* Updated dependencies
* Added "magic" variables to collection partials (__index__, __length__, __isFirst__, __isLast__). Closes #254
* Request instance created before body parsing. Closes #262
* Fixed post param issue
* Fixed mocks to work with new routing api
* .
* Docs
* Merge branch 'upload-limit'
* Added "max upload size" setting
* Updated ext. Closes #256
* Added Request#render() callback function. Closes #258
* Merge branch 'integration'
* fn -> callback
* Typo
* Merge branch 'master' of git://github.com/aheckmann/express into integration
* Updated to JSpec 4.0.0
* error() is passed the exception
* fn -> callback
* Added DSL level error() route support
* Added DSL level notFound() route support
* Added specs for Request#notFound()
* More Request#error() specs
* Added specs for Request#error()
* Merge branch 'errors'
* Added publish Request#notFound()
* Removed Express.error(), Added public Request#error()
* Request#halt() accepts callback function as 3rd/4th arg
* Merge branch 'error-handling'
* Misc error handling improvements
* Removed unused variable
* Merge branch 'error-handling'
* Caching notFound / showException modules
* Express.error() now acts as the core exception handler
* request response event is now fired in reverse.
* Handle when a plugin response fails
* next -> callback
* Styling
* Removed set("session cookie") in favour of use(Session, { cookie: { ... }})
* Docs for stable / edge
* Merge branch 'route-wildcards'
* Added preprocessing of route param wildcards using param(). Closes #251
* Added specs for param()
* Added more route wildcard specs
* Merge branch 'pre-cache-views'
* Pre-caching views in memory. Closes #253
* Started pre caching of views
* Merge branch 'integration'
* Added assertion to ensure that partials dir is relative to set("views")
* Re-using variables
* Using set("views") when setting set("partials")
* partialscache -> partials
* preload partials add settings bug fix, partial now works when cache view contents is true
* add cache view partials
* Removed utils.mixin() use Object#mergeDeep()
* Merge branch 'partials'
* Chat sample app using partials as an example
* Added partial "as" option
* Partial collection should not introduce newlines
* Added partial collection support
* Started view partial support
* Updated ext submodule
* Removed Request#_blendInNodeRequest()
* Merge branch 'net2_fixes' of git://github.com/ciaranj/express
* Updated support to v0.1.33
* Various minor fixes required to make express work post the net2 merge branch
0.8.0 / 2010-03-19
==================
* Added coffeescript example app. Closes #242
* Changed; cache api now async friendly. Closes #240
* Removed deprecated 'express/static' support. Use 'express/plugins/static'
0.7.6 / 2010-03-19
==================
* Added Request#isXHR. Closes #229
* Added `make install` (for the executable)
* Added `express` executable for setting up simple app templates
* Added "GET /public/*" to Static plugin, defaulting to <root>/public
* Added Static plugin
* Fixed; Request#render() only calls cache.get() once
* Fixed; Namespacing View caches with "view:"
* Fixed; Namespacing Static caches with "static:"
* Fixed; Both example apps now use the Static plugin
* Fixed set("views"). Closes #239
* Fixed missing space for combined log format
* Deprecated Request#sendfile() and 'express/static'
* Removed Server#running
0.7.5 / 2010-03-16
==================
* Added Request#flash() support without args, now returns all flashes
* Updated ext submodule
0.7.4 / 2010-03-16
==================
* Fixed session reaper
* Changed; class.js replacing js-oo Class implementation (quite a bit faster, no browser cruft)
0.7.3 / 2010-03-16
==================
* Added package.json
* Fixed requiring of haml / sass due to kiwi removal
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
==================
+10 -16
Ver Arquivo
@@ -1,20 +1,15 @@
NODE = node
COFFEE = coffee
all: test
install: bin/express
install bin/express /usr/local/bin/express
init:
@git submodule init && git submodule update
test: init spec/support/libxmljs/libxmljs.node
test:
@$(NODE) spec/node.js all
test-independant: init
@$(NODE) spec/node.js independant
test-dependant: init spec/support/libxmljs/libxmljs.node
@$(NODE) spec/node.js dependant
app: app-chat
app-chat:
@@ -23,11 +18,10 @@ app-chat:
app-upload:
@$(NODE) examples/upload/app.js
benchmark:
@$(NODE) benchmarks/collection.js
@$(NODE) benchmarks/views.js
app-coffee-upload: compile-coffee
@$(NODE) examples/coffee-upload/app.js
spec/support/libxmljs/libxmljs.node:
@scons -C spec/support/libxmljs libxmljs.node
compile-coffee:
@$(COFFEE) examples/coffee-upload/app.coffee
.PHONY: init test benchmark app
.PHONY: install test app
+38 -53
Ver Arquivo
@@ -12,90 +12,73 @@
* Sexy DSL with robust sinatra-like routing
* High performance
* Session support
* Cache API
* RESTful HTTP client
* 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
* Light-weight JavaScript class implementation via [class.js](http://github.com/visionmedia/class.js/)
* Persistent flash messages
* Route passing
* View support (ejs, haml, sass, etc)
* Full test coverage
* Logger plugin with several formats
* Upload size restrictions
* Extremely readable specs
* Cookie support
## 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 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 itself 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
Below is a minimal app example when express is already within your load path.
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)
set('root', __dirname)
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 frozen
to spec/lib and does not require separate installation.
To run all specifications run the following command. This will ensure
git submodules are initialized and updated, as well as building test
related dependencies such as libxmljs.
to spec/lib and **does not** require separate installation.
$ make test
To run independent specs (which do not require building of external apis etc) use:
$ make test-independant
To run dependent specs (which require building of external apis etc) use:
$ make test-dependant
Run individual suites:
$ node spec/node.js core
@@ -103,14 +86,16 @@ Run individual suites:
$ node spec/node.js routing
...
Express is currently being developed with node --version:
v0.1.27
The latest release of Express is compatible with node --version:
v0.1.90
EDGE Express we do our best to keep up to date with node's EDGE
## More Information
* [JavaScript Extensions &amp; Utilities](http://github.com/visionmedia/ext.js)
* [JavaScript Sass](http://github.com/visionmedia/sass.js)
* [Scons Build System](http://www.scons.org/) (some development dependencies rely on this, ex libxmljs)
* Featured in [Advanced JavaScript e-book](http://www.dev-mag.com/2010/02/18/advanced-javascript/) for only $4
## Contributors
-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]', 100, 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)
})
})
-56
Ver Arquivo
@@ -1,56 +0,0 @@
require.paths.unshift('lib')
require.paths.unshift('benchmarks')
process.mixin(GLOBAL, require('sys'))
process.mixin(GLOBAL, require('benchmark'))
require('express')
print = puts
engine = {
ejs: require('ejs'),
haml: require('haml'),
sass: require('sass')
}
options = { locals: { article: { title: 'Foo', body: 'bar' }}}
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\
#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(){
benchmark('ejs', function(){
engine.ejs.render(ejs, options)
})
benchmark('haml', function(){
engine.haml.render(haml, options)
})
benchmark('sass', function(){
engine.sass.render(sass)
})
})
Arquivo executável
+62
Ver Arquivo
@@ -0,0 +1,62 @@
#!/usr/bin/env node
var arg,
dir = '.',
fs = require('fs'),
sys = require('sys'),
args = process.argv.slice(2)
// Parse arguments
while (arg = args.shift())
switch (arg) {
case '-h':
case '--help':
sys.print('Usage: express [options] [dir]\n')
break
default:
dir = arg
}
// Helpers
function confirm(msg, fn) {
sys.print(msg)
var stdin = process.openStdin()
stdin.setEncoding('utf8')
stdin.addListener('data', function(chunk){
switch (chunk.trim().toLowerCase()) {
case 'yes':
case 'y':
return stdin.destroy(), fn(true)
case 'no':
case 'n':
return stdin.destroy(), fn(false)
default:
sys.print(msg)
}
})
}
function createTemplateIn(dir, mode) {
fs.writeFile(dir + '/app.js', '// your app here')
fs.mkdir(dir + '/views', mode)
fs.mkdir(dir + '/public', mode, function(err){
if (err) throw err
fs.mkdir(dir + '/public/javascripts', mode)
fs.mkdir(dir + '/public/stylesheets', mode)
fs.mkdir(dir + '/public/images', mode)
})
}
// Setup template
fs.readdir(dir, function(err, files){
if (err) throw err
if (files.length)
confirm(dir + ' is not empty, continue? ', function(ok){
if (ok) createTemplateIn(dir, 0666)
})
else
createTemplateIn(dir, 0666)
})
+21 -22
Ver Arquivo
@@ -3,33 +3,36 @@ require.paths.unshift('lib')
require('express')
require('express/plugins')
var messages = [],
utils = require('express/utils'),
http = require('express/http')
configure(function(){
var fiveMinutes = 300000,
oneMinute = 60000
use(Logger)
use(MethodOverride)
use(ContentLength)
use(CommonLogger)
use(Cookie)
use(Cache, { lifetime: fiveMinutes, reapInterval: oneMinute })
use(Session, { lifetime: fiveMinutes, reapInterval: oneMinute })
use(Cache, { lifetime: (5).minutes, reapInterval: (1).minute })
use(Session, { lifetime: (15).minutes, reapInterval: (1).minute })
use(Static)
set('root', __dirname)
})
var messages = [],
utils = require('express/utils')
get('/', function(){
this.redirect('/chat')
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()
}
var self = this
Session.store.length(function(err, len){
self.render('chat.html.haml', {
locals: {
title: 'Chat',
messages: messages,
name: self.session.name,
usersOnline: len
}
})
})
})
@@ -54,12 +57,8 @@ get('/chat/messages', function(){
}, 100)
})
get('/public/*', function(file){
this.sendfile(__dirname + '/public/' + file)
})
get('/*.css', function(file){
this.render(file + '.sass.css', { layout: false })
this.render(file + '.css.sass', { layout: false })
})
get('/error/view', function(){
@@ -1,8 +1,7 @@
%h1 Chat
%img.bubble{ src: '/public/images/bubble.png' }
%ul#messages
:each msg in messages
%li= msg
!= this.partial('message.html.haml', { collection: messages })
%form{ method: 'post' }
%input{ type: 'hidden', name: '_method', value: 'put' }
%input{ type: 'text', name: 'name', value: name || 'guest' }
@@ -5,7 +5,8 @@
%script{ src: '/public/javascripts/app.js' }
%link{ rel: 'stylesheet', href: '/style.css' }
%body
#wrapper= body
#online
#wrapper
!= body
#online
Online:
%strong= usersOnline
@@ -0,0 +1 @@
%li= message
+38
Ver Arquivo
@@ -0,0 +1,38 @@
require.paths.unshift 'lib'
require 'express'
require 'express/plugins'
sys: require 'sys'
configure ->
use MethodOverride
use ContentLength
use Cookie
use Session
use Flash
use Logger
use Static
set 'root', __dirname
set 'views', __dirname + '/../upload/views'
get '/', ->
@redirect('/upload')
get '/upload', ->
@render 'upload.html.haml', {
locals: {
flashes: @flash 'info'
}
}
post '/upload', ->
@param('images').each (image) =>
sys.puts image.filename + ' -> ' + image.tempfile
@flash 'info', 'Uploaded ' + image.filename
@redirect '/upload'
get '/*.css', (file) ->
@render file + '.css.sass', { layout: no }
run()
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+19 -11
Ver Arquivo
@@ -3,11 +3,18 @@ require.paths.unshift('lib')
require('express')
require('express/plugins')
var sys = require('sys')
configure(function(){
use(MethodOverride)
use(ContentLength)
use(CommonLogger)
use(Cookie)
use(Session)
use(Flash)
use(Logger)
use(Static)
set('root', __dirname)
set('max upload size', (5).megabytes)
})
get('/', function(){
@@ -15,22 +22,23 @@ get('/', function(){
})
get('/upload', function(){
this.render('upload.haml.html')
this.render('upload.html.haml', {
locals: {
flashes: this.flash('info')
}
})
})
post('/upload', function(){
$(this.param('images')).each(function(image){
puts('uploaded ' + image.filename + ' to ' + image.tempfile)
})
post('/upload', function(){
this.param('images').each(function(image){
sys.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 })
this.render(file + '.css.sass', { layout: false })
})
run()
@@ -5,4 +5,10 @@
%script{ src: '/public/javascripts/app.js' }
%link{ rel: 'stylesheet', href: '/style.css' }
%body
#wrapper= body
#wrapper
%h1 Upload
- if (flashes)
%ul.messages.info
- each msg in flashes
%li= msg
.body!= body
@@ -56,4 +56,13 @@ form
.panel
:float left
:width 100%
:margin-bottom 15px
:margin-bottom 15px
.messages
:margin 0
:padding 0
:border 1px solid #eee
=box-shadow 2px 2px 5px #eee
li
:padding 5px 10px
:list-style none
@@ -1,7 +1,6 @@
%h1 Upload
:if typeof images !== 'undefined'
- if (typeof images !== 'undefined')
.images
:each img in images
- each img in images
%img{ src: img }
%h2 Singles
@@ -9,11 +8,11 @@
%input{ type: 'file', name: 'images[0]' }
%input{ type: 'file', name: 'images[1]' }
%input{ type: 'file', name: 'images[2]' }
.panel
%div.panel
%input{ type: 'submit', value: 'Upload' }
%h2 Multiple
%form{ method: 'post', enctype: 'multipart/form-data' }
%input{ type: 'file', name: 'images[]', multiple: 'multiple' }
.panel
%div.panel
%input{ type: 'submit', value: 'Upload' }
+4 -3
Ver Arquivo
@@ -1,7 +1,8 @@
require.paths.unshift(__dirname + '/support/js-oo/lib')
require.paths.unshift(__dirname + '/support/ejs/lib')
require.paths.unshift(__dirname + '/support/ext/lib')
require.paths.unshift(__dirname + '/support/haml/lib')
require.paths.unshift(__dirname + '/support/sass/lib')
require('oo')
require.paths.unshift(__dirname + '/support/multipart/lib')
require('ext')
Class = require('support/class/lib/class').Class
require('express/core')
-543
Ver Arquivo
@@ -1,543 +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)
}
+120 -108
Ver Arquivo
@@ -1,30 +1,28 @@
// 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/plugin'))
process.mixin(require('express/dsl'))
/**
* Module dependencies.
*/
var multipart = require('multipart'),
events = require('events'),
File = require('file').File,
utils = require('express/utils')
var Request = require('express/request').Request,
normalizePath = require('express/request').normalizePath,
multipart = require('old'),
utils = require('express/utils'),
http = require('http'),
sys = require('sys'),
fs = require('fs')
global.merge(require('express/plugin'))
global.merge(require('express/dsl'))
// --- Route
Route = Class({
Route = new Class({
/**
* Initialize a route with the given _method_,
* _path_, and callback _fn_.
* _path_, and _callback_.
*
* The given _path_ becomes #originalPath,
* #path is then a normalized version converted
@@ -32,16 +30,16 @@ Route = Class({
*
* @param {string} method
* @param {string} path
* @param {function} fn
* @param {function} callback
* @param {hash} options
* @api private
*/
init: function(method, path, fn, options){
constructor: function(method, path, callback, options){
this.method = method
this.originalPath = path
this.path = this.normalize(path)
this.fn = fn
this.callback = callback
},
/**
@@ -71,7 +69,7 @@ Route = Class({
var self = this
this.keys = []
if (path instanceof RegExp) return path
return new RegExp('^' + utils.escapeRegexp(normalizePath(path), '.')
return new RegExp('^' + RegExp.escape(normalizePath(path), '.')
.replace(/\*/g, '(.+)')
.replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
self.keys.push(key)
@@ -86,7 +84,7 @@ Route = Class({
// --- Router
Router = Class({
Router = new Class({
/**
* Initialize with _request_ and parse url.
@@ -95,7 +93,7 @@ Router = Class({
* @api private
*/
init: function(request) {
constructor: function(request) {
this.request = request
},
@@ -106,14 +104,21 @@ Router = Class({
* @api private
*/
route: function(){
var route = this.matchingRoute()
if (route)
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))
route: function() {
var body,
route = this.matchingRoute()
if (route) {
body = route.callback.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
this.request.halt()
this.request.notFound()
},
/**
@@ -123,11 +128,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
},
/**
@@ -149,23 +156,26 @@ 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){
var val = this.request.captures[++i]
if (key in Express.params)
if ((val = Express.params[key].call(this.request, val)) === false)
this.request.passed = true
this.request.params.path[key] = this.request.captures[i] = val
}, this)
}
})
// --- Server
Server = Class({
Server = new Class({
/**
* Default port number.
@@ -204,97 +214,98 @@ Server = Class({
if (host !== undefined) this.host = host
if (port !== undefined) this.port = port
if (backlog !== undefined) this.backlog = backlog
require('http')
.createServer(function(request, response){
http
.createServer(function(req, response){
var request, pendingFiles = 0
req.setBodyEncoding('binary')
request = new Request(req, response)
request.body = ''
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) })
})
function callback(err) {
if (err)
request.error(err)
else if (!pendingFiles)
self.route(request)
}
if (request.header('content-type') &&
request.header('content-type').includes('multipart/form-data')) {
var stream,
contentLength = parseInt(request.header('content-length')),
maxBodyLength = set('max upload size')
if (maxBodyLength && contentLength > maxBodyLength)
return callback(new Error('upload size limit exceeded'))
stream = multipart.parse(req)
stream
.addListener('partBegin', function(part) {
if (part.filename)
++pendingFiles,
part.tempfile = '/tmp/express-' + Date.now() + utils.uid(),
part.fileStream = fs.createWriteStream(part.tempfile),
part.fileStream.addListener('error', callback)
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.end(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('body', function(chunk){ request.body += chunk })
.addListener('complete', function(){ self.route(request, response) })
req
.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')
sys.puts('Express started at http://' + this.host + ':' + this.port + '/ in ' + Express.environment + ' mode')
},
/**
* Route the given _request_ and _response_.
* Route the given _request_.
*
* @param {object} request
* @param {object} response
* @param {Request} request
* @api private
*/
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)
}
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
}
route: function(request){
var self = this
request.trigger('request', function(err) {
try {
if (err) throw err
if (request.response.finished) return
if (typeof (body = (new Router(request)).route()) === 'string')
request.halt(200, body, function(err){
if (err) request.error(err)
})
} catch (err) {
request.error(err)
}
})
}
})
// --- Express
Express = {
version: '0.3.0',
version: '0.9.0',
config: [],
routes: [],
plugins: [],
settings: {},
params: {},
server: new Server
}
@@ -318,5 +329,6 @@ configure('test', function(){
configure('production', function(){
enable('cache view contents')
enable('cache view partials')
enable('cache static files')
})
+64 -25
Ver Arquivo
@@ -10,10 +10,10 @@
*/
function route(method) {
return function(path, options, fn){
return function(path, options, callback){
if (options instanceof Function)
fn = options, options = {}
Express.routes.push(new Route(method, path, fn, options))
callback = options, options = {}
Express.routes.push(new Route(method, path, callback, options))
}
}
@@ -28,11 +28,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,8 +68,8 @@ exports.disable = function(option) {
*/
exports.run = function() {
configure(Express.environment = process.ENV.EXPRESS_ENV || 'development')
$(Express.plugins).each(function(plugin){
configure(Express.environment = process.env.EXPRESS_ENV || 'development')
Express.plugins.each(function(plugin){
if ('init' in plugin.klass)
plugin.klass.init(plugin.options)
})
@@ -77,7 +77,7 @@ exports.run = function() {
}
/**
* Configure _environment_ with _fn_.
* Configure _env_ with _callback_.
*
* Global configuration, disregards which
* environment is active:
@@ -96,22 +96,61 @@ exports.run = function() {
*
* configure('development')
*
* @param {string, function} environment
* @param {function} fn
* @param {string, function} env
* @param {function} callback
* @api public
*/
exports.configure = function(environment, fn) {
if (environment instanceof Function)
fn = environment, environment = 'all'
if (fn instanceof Function)
return Express.config.push([environment, fn])
if (typeof environment != 'string')
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')
Express.config[i][1].call(Express)
exports.configure = function(env, callback) {
if (env instanceof Function)
callback = env, env = 'all'
if (callback instanceof Function)
return Express.config.push([env, callback])
if (typeof env !== 'string')
throw new TypeError('environment required')
Express.config.each(function(conf){
if (conf[0] === env ||
conf[0] === 'all')
conf[1].call(Express)
})
}
/**
* Pre-process param _key_ with _callback_.
*
* @param {string} key
* @param {function} callback
* @api public
*/
exports.param = function(key, callback) {
if (typeof key !== 'string')
throw new TypeError('param key must be a string')
if (typeof callback !== 'function')
throw new TypeError('param must pass a function to process "' + key + '"')
Express.params[key] = callback
}
/**
* Register a "Not Found" route with the given _callback_.
*
* @param {function} callback
* @api public
*/
exports.notFound = function(callback) {
Express.notFound = callback
}
/**
* Register an "error" route with the given _callback_.
*
* @param {function} callback
* @api public
*/
exports.error = function(callback) {
Express.error = callback
}
// --- Routing API
@@ -119,4 +158,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)
/**
* Module dependencies.
*/
var libxml = require('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.parseHtmlString(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).prevSibling()])
},
/**
* Return the next element.
*
* @return {ElementCollection}
* @api public
*/
next: function() {
return $([this.at(0).nextSibling()])
},
/**
* 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)
}
+3 -3
Ver Arquivo
@@ -1,7 +1,7 @@
// Express - Event - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
exports.Event = Class({
exports.Event = new Class({
/**
* Initialize with event _name_ and optional _data_.
@@ -11,9 +11,9 @@ exports.Event = Class({
* @api private
*/
init: function(name, data) {
constructor: 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
}
})
+92
Ver Arquivo
@@ -0,0 +1,92 @@
// Express - HTTP - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var http = require('http'),
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} callback
* @param {number} redirects
* @api private
*/
function request(method, url, data, headers, callback, 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)
callback(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, callback, redirects)
else
callback(new Error('maximum number of redirects reached'))
else {
res.setBodyEncoding('utf8')
res
.addListener('data', function(chunk){ buf += chunk })
.addListener('end', function(){ callback(null, buf, res) })
}
})
req.end()
}
/**
* 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(),
callback = args.pop(),
data = args.shift(),
headers = args.shift()
if (typeof callback !== 'function')
throw new TypeError('http client requires a callback function')
return request(method.toUpperCase(), url, data, headers, callback)
}
}
// --- 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')
+2 -2
Ver Arquivo
@@ -5,7 +5,7 @@
* Module dependencies.
*/
var utils = require('express/utils')
var extname = require('path').extname
/**
* Mime type lookup table.
@@ -364,7 +364,7 @@ exports.types = {
exports.type = function(path) {
return exports.types[path] ||
exports.types[utils.extname(path)] ||
exports.types[extname(path).substr(1)] ||
set('default mime type') ||
'application/octet-stream'
}
+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.url.params) + ' \n\
' + hash(request.params.get) + ' \n\
</table> \n\
<h3>POST</h3> \n\
<table id="post-params"> \n\
' + hash(request.url.post) + ' \n\
' + hash(request.params.post) + ' \n\
</table> \n\
</div> \n\
</body> \n\
+4 -16
Ver Arquivo
@@ -14,7 +14,7 @@
exports.use = function(plugin, options) {
if (Express.environment === 'test' && 'init' in plugin)
plugin.init(options)
$(Express.plugins).each(function(other, i){
Express.plugins.each(function(other, i){
if (other.klass === plugin)
delete Express.plugins[i]
})
@@ -26,19 +26,7 @@ exports.use = function(plugin, options) {
// --- Plugin
exports.Plugin = Class({
/**
* Initialize with _options_.
*
* @param {hash} options
* @api private
*/
init: function(options) {
if (options)
process.mixin(this, options)
},
exports.Plugin = new Class({
/**
* Trigger handler for the given _event_.
@@ -47,9 +35,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)
}
})
+9 -9
Ver Arquivo
@@ -1,12 +1,12 @@
// 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'))
process.mixin(require('express/plugins/method-override'))
global.merge(require('express/plugins/hooks'))
global.merge(require('express/plugins/static'))
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'))
+7 -1
Ver Arquivo
@@ -1,8 +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: {
@@ -15,7 +21,7 @@ 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.header('content-type').includes('application/x-www-form-urlencoded'))
request.params.post = queryString.parseQuery(request.body)
}
}
+59 -43
Ver Arquivo
@@ -1,24 +1,30 @@
// Express - Cache - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var Request = require('express/request').Request
// --- Cache
var Cache = Class({
var Cache = new Class({
/**
* Initialize cache with _key_ and _val_.
*/
init: function(key, val) {
constructor: function(key, val) {
this.key = key
this.val = val
this.created = Number(new Date)
this.created = Date.now()
}
})
// --- Store
exports.Store = Class({
exports.Store = new Class({
/**
* Ensure that the given _key_ is a string.
@@ -26,11 +32,13 @@ exports.Store = Class({
*
* @param {string} key
* @param {string} val
* @param {function} callback
* @api public
*/
set: function(key, val) {
if (typeof key !== 'string') throw new Error(this.name + ' store #set() key must be a string')
set: function(key, val, callback) {
if (typeof key !== 'string')
throw new Error(this.name + ' store #set() key must be a string')
},
/**
@@ -38,11 +46,13 @@ exports.Store = Class({
* Override in subclass to provide data-store specific functionality.
*
* @param {string} key
* @param {function} callback
* @api public
*/
get: function(key) {
if (typeof key !== 'string') throw new Error(this.name + 'store #get() key must be a string')
get: function(key, callback) {
if (typeof key !== 'string')
throw new Error(this.name + 'store #get() key must be a string')
},
/**
@@ -71,22 +81,24 @@ exports.Store.Memory = exports.Store.extend({
* Initialize data.
*/
init: function() {
constructor: function() {
this.data = {}
},
/**
* Set the given _key_ to _val_, returning _val_.
* Set the given _key_ to _val_.
*
* @param {string} key
* @param {string} val
* @param {function} callback
* @return {string}
* @api public
*/
set: function(key, val) {
this.__super__(key, val)
return this.data[key] = new Cache(key, val), val
set: function(key, val, callback) {
exports.Store.prototype.set.apply(this, arguments)
this.data[key] = new Cache(key, val)
if (callback) callback(val)
},
/**
@@ -94,31 +106,32 @@ exports.Store.Memory = exports.Store.extend({
*
* Examples:
*
* cache.get('page:front')
* cache.get('page:front', function(val){})
* // => '<html>...</html>'
*
* cache.get('page:*')
* cache.get('page:*', function(vals){})
* // => { 'page:front': '<html>...</html>',
* 'page:users': '<html>...</html>',
* ... }
*
* @param {string} key
* @param {function} callback
* @return {mixed}
* @api public
*/
get: function(key) {
this.__super__(key)
get: function(key, callback) {
exports.Store.prototype.get.apply(this, arguments)
if (key.indexOf('*') === -1)
return this.data[key] instanceof Cache ?
this.data[key].val :
null
return callback(this.data[key] instanceof Cache
? this.data[key].val
: null)
var regexp = this.normalize(key)
return $(this.data).reduce({}, function(vals, cache){
callback(this.data.reduce(function(vals, cache){
if (regexp.test(cache.key))
vals[cache.key] = cache.val
return vals
})
}, {}))
},
/**
@@ -126,21 +139,25 @@ exports.Store.Memory = exports.Store.extend({
*
* Examples:
*
* cache.clear('page:front')
* cache.clear('page:*')
* cache.clear('page:front', function(){})
* cache.clear('page:*', function(){})
*
* @param {string} key
* @param {function} callback
* @api public
*/
clear: function(key) {
clear: function(key, callback) {
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]
delete this.data[key]
else {
var regexp = this.normalize(key)
for (var key in this.data)
if (this.data.hasOwnProperty(key))
if (regexp.test(key))
delete this.data[key]
}
callback()
},
/**
@@ -150,13 +167,14 @@ exports.Store.Memory = exports.Store.extend({
* @api private
*/
reap: function(ms) {
reap: function(ms, callback) {
var self = this,
threshold = Number(new Date(Number(new Date) - ms))
$(this.data).each(function(cache){
threshold = +new Date(Date.now() - ms)
this.data.each(function(cache){
if (cache.created < threshold)
self.clear(cache.key)
self.clear(cache.key, function(){})
})
if (callback) callback()
},
/**
@@ -194,7 +212,7 @@ exports.Cache = Plugin.extend({
*/
init: function(options) {
process.mixin(this, options)
this.merge(options || {})
this.store = new (this.dataStore || exports.Store.Memory)(options)
Request.include({ cache: this.store })
this.startReaper()
@@ -203,16 +221,14 @@ exports.Cache = Plugin.extend({
/**
* Start reaper.
*
* @param {function} callback
* @api private
*/
startReaper: function() {
var self = this,
oneDay = 86400000,
oneHour = 3600000
setInterval(function(){
self.store.reap(self.lifetime || oneDay)
}, self.reapInterval || self.reapEvery || oneHour)
startReaper: function(callbac) {
setInterval(function(self){
self.store.reap(self.lifetime || (1).day)
}, this.reapInterval || this.reapEvery || (1).hour, this)
}
}
})
-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.url.pathname || '/') +
' HTTP/' + event.request.httpVersion + '"',
event.request.response.status,
event.request.response.headers['content-length'] || 0].join(' '))
}
}
})
+17 -13
Ver Arquivo
@@ -1,6 +1,12 @@
// Express - Cookie - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var Request = require('express/request').Request
/**
* Parse an HTTP _cookie_ string into a hash.
*
@@ -10,11 +16,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 +35,11 @@ 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')
val = val.toGMTString()
return val === true ? key : key + '=' + val
}).toArray().join('; ')
}).join('; ')
}
// --- Cookie
@@ -93,9 +96,9 @@ exports.Cookie = Plugin.extend({
request: function(event) {
event.request.response.cookies = []
event.request.cookies = event.request.headers.cookie ?
exports.parseCookie(event.request.headers.cookie) :
{}
event.request.cookies = event.request.headers.cookie
? exports.parseCookie(event.request.headers.cookie)
: {}
},
/**
@@ -103,8 +106,9 @@ exports.Cookie = Plugin.extend({
*/
response: function(event) {
if (event.response.cookies.length)
event.request.header('set-cookie', event.response.cookies.join(', '))
if (event.response.cookies &&
event.response.cookies.length)
event.request.header('set-cookie', event.response.cookies.join('\nset-cookie: '))
}
}
})
+17 -1
Ver Arquivo
@@ -1,6 +1,14 @@
// Express - Flash - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var Request = require('express/request').Request
// --- Flash
exports.Flash = Plugin.extend({
extend: {
@@ -18,6 +26,9 @@ exports.Flash = Plugin.extend({
* it will persist in the session until outputted.
* The _val_ pushed is returned.
*
* When no arguments are given, all flashes are
* returned keyed by their type.
*
* Example:
*
* this.flash('info', 'email sent')
@@ -30,12 +41,17 @@ exports.Flash = Plugin.extend({
*
* @param {string} key
* @param {string} val
* @return {string}
* @return {mixed}
* @api public
*/
flash: function(key, val) {
if (!this.session.flash) this.session.flash = {}
if (!key) {
var flashes = this.session.flash
this.session.flash = {}
return flashes
}
if (!(key in this.session.flash)) this.session.flash[key] = []
if (val)
return this.session.flash[key].push(val), val
+20 -14
Ver Arquivo
@@ -1,28 +1,32 @@
// Express - Hooks - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Callbacks.
*/
exports.callbacks = { before: [], after: [] }
/**
* Add a _fn_ to be excuted before a request.
* Add a _callback_ to be excuted before a request.
*
* @param {function} fn
* @param {function} callback
* @api public
*/
exports.before = function(fn) {
exports.callbacks.before.push(fn)
exports.before = function(callback) {
exports.callbacks.before.push(callback)
}
/**
* Add a _fn_ to be excuted after a request.
* Add a _callback_ to be excuted after a request.
*
* @param {function} fn
* @param {function} callback
* @api public
*/
exports.after = function(fn) {
exports.callbacks.after.push(fn)
exports.after = function(callback) {
exports.callbacks.after.push(callback)
}
// --- Hooks
@@ -35,8 +39,10 @@ exports.Hooks = Plugin.extend({
*/
init: function() {
process.mixin(GLOBAL, { before: exports.before,
after: exports.after })
global.merge({
before: exports.before,
after: exports.after
})
}
},
@@ -49,8 +55,8 @@ exports.Hooks = Plugin.extend({
*/
request: function(event) {
$(exports.callbacks.before).each(function(fn){
fn.call(event.request, event.request)
exports.callbacks.before.each(function(callback){
callback.call(event.request, event.request)
})
},
@@ -59,8 +65,8 @@ exports.Hooks = Plugin.extend({
*/
response: function(event) {
$(exports.callbacks.after).each(function(fn){
fn.call(event.request, event.request)
exports.callbacks.after.each(function(callback){
callback.call(event.request, event.request)
})
}
}
+81
Ver Arquivo
@@ -0,0 +1,81 @@
// 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.%d" %s %d %0.3f',
event.request.socket.remoteAddress,
(new Date).format('%d/%b/%Y %H:%M:%S'),
event.request.method.uppercase,
event.request.url.pathname || '/',
event.request.httpVersionMajor,
event.request.httpVersionMinor,
event.request.response.status,
event.request.response.headers['content-length'] || 0,
(Date.now() - 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(Date.now() - 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 = Date.now()
},
/**
* Output log data.
*/
response: function(event) {
formats[exports.Logger.format || 'common'](event, this.start || Date.now())
sys.print('\n')
}
}
})
-51
Ver Arquivo
@@ -1,51 +0,0 @@
// 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: {
/**
* Start timer.
*/
request: function(event) {
this.start = Number(new Date)
},
/**
* Output duration.
*/
response: function(event) {
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)
}
}
})
+9 -1
Ver Arquivo
@@ -1,6 +1,14 @@
// Express - Redirect - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var Request = require('express/request').Request
// --- Redirect
exports.Redirect = Plugin.extend({
extend: {
@@ -32,7 +40,7 @@ exports.Redirect = Plugin.extend({
*/
redirect: function(url, code) {
if (url == 'back' || url == 'home') url = this[url]
if (url === 'back' || url === 'home') url = this[url]
this.header('location', url)
this.halt(code || 303)
}
+69 -40
Ver Arquivo
@@ -5,17 +5,18 @@
* Module dependencies.
*/
var utils = require('express/utils')
var Request = require('express/request').Request,
utils = require('express/utils')
// --- Session
var Session = Class({
exports.Base = new Class({
/**
* Initialize session _sid_.
*/
init: function(sid) {
constructor: function(sid) {
this.id = sid
this.touch()
},
@@ -27,13 +28,13 @@ var Session = Class({
*/
touch: function() {
this.lastAccess = Number(new Date)
this.lastAccess = Date.now()
}
})
// --- Store
exports.Store = Class({
exports.Store = new Class({
/**
* Convert to '[NAME Store]'.
@@ -61,80 +62,101 @@ exports.Store.Memory = exports.Store.extend({
* Initialize in-memory session store.
*/
init: function() {
constructor: function() {
this.store = {}
},
/**
* Fetch session with the given _sid_ or
* a new Session is returned.
* a new Session is created.
*
* @param {int} sid
* @return {Session}
* @param {number} sid
* @param {function} callback
* @api private
*/
fetch: function(sid) {
return this.store[sid] || new Session(sid)
fetch: function(sid, callback) {
if (sid && this.store[sid])
callback(null, this.store[sid])
else
this.generate(callback)
},
/**
* Commit _session_ data.
*
* @param {Session} session
* @param {function} callback
* @api private
*/
commit: function(session) {
return this.store[session.id] = session
commit: function(session, callback) {
this.store[session.id] = session
if (callback) callback(null, session)
},
/**
* Clear all sessions.
*
* @param {function} callback
* @api public
*/
clear: function() {
clear: function(callback) {
this.store = {}
if (callback) callback()
},
/**
* Destroy session using the given _sid_.
*
* @param {int} sid
* @param {function} callback
* @api public
*/
destroy: function(sid) {
destroy: function(sid, callback) {
delete this.store[sid]
if (callback) callback(sid)
},
/**
* Return the number of sessions currently stored.
* Pass the number of sessions currently stored.
*
* @return {int}
* @param {function} callback
* @api public
*/
length: function() {
return $(this.store).length()
length: function(callback) {
callback(null, this.store.values.length)
},
/**
* Reap sessions older than _ms_.
*
* @param {int} ms
* @param {function} callback
* @api private
*/
reap: function(ms) {
var self = this,
threshold = Number(new Date(Number(new Date) - ms))
$(this.store).each(function(session, sid){
reap: function(ms, callback) {
var threshold = +new Date(Date.now() - ms)
this.store.each(function(session, sid){
if (session.lastAccess < threshold)
self.destroy(sid)
})
this.destroy(sid)
}, this)
if (callback) callback()
},
/**
* Creates and passes a shiny new session.
*
* @param {function} callback
* @api public
*/
generate: function(callback) {
callback(null, new exports.Base(utils.uid()))
}
})
@@ -151,13 +173,16 @@ exports.Session = Plugin.extend({
* - 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
* - cookie session specific cookie options passed to Request#cookie()
*
* @param {hash} options
* @api private
*/
init: function(options) {
process.mixin(this, options)
this.cookie = {}
this.merge(options || {})
this.cookie.httpOnly = true
this.store = new (this.dataStore || exports.Store.Memory)(options)
this.startReaper()
},
@@ -169,12 +194,9 @@ exports.Session = Plugin.extend({
*/
startReaper: function() {
var self = this,
oneDay = 86400000,
oneHour = 3600000
setInterval(function(){
self.store.reap(self.lifetime || oneDay)
}, self.reapInterval || self.reapEvery || oneHour)
setInterval(function(self) {
self.store.reap(self.lifetime || (1).day)
}, this.reapInterval || this.reapEvery || (1).hour, this)
}
},
@@ -186,20 +208,27 @@ exports.Session = Plugin.extend({
* 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()
request: function(event, callback) {
var sid = event.request.cookie('sid')
exports.Session.store.fetch(sid, function(err, session) {
if (err) return callback(err)
if (session.id != sid)
event.request.cookie('sid', session.id, exports.Session.cookie)
event.request.session = session
event.request.session.touch()
callback()
})
return true
},
/**
* Delegate to store, allowing it to save sessions changes.
*/
response: function(event) {
exports.Session.store.commit(event.request.session)
response: function(event, callback) {
if (event.request.session)
return exports.Session.store.commit(event.request.session, callback),
true
}
}
})
+131
Ver Arquivo
@@ -0,0 +1,131 @@
// Express - Static - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var Request = require('express/request').Request,
path = require('path'),
fs = require('fs')
// --- File
exports.File = new Class({
/**
* Initialize with file _path_.
*
* @param {string} path
* @api public
*/
constructor: function(path) {
this.path = path
if (path.indexOf('..') != -1)
Error.raise('InvalidPathError', "`" + path + "' is not a valid path")
},
/**
* Transfer static file to the given _request_.
*
* - 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
*/
sendTo: function(request) {
var file = this.path
function sendFromDisc() {
path.exists(file, function(exists){
if (!exists) return request.halt()
fs.stat(file, function(err, stats){
if (err) throw err
if (!stats.isFile()) return request.halt()
fs.readFile(file, 'binary', function(err, content){
if (err) throw err
request.contentType(file)
if (set('cache static files'))
request.cache.set('static:' + file, { type: file, content: content })
request.halt(200, content, 'binary')
})
})
})
}
if (set('cache static files'))
request.cache.get('static:' + file, function(cache){
if (cache)
request.contentType(cache.type),
request.halt(200, cache.content, 'binary')
else
sendFromDisc()
})
else
sendFromDisc()
}
})
// --- Static
exports.Static = Plugin.extend({
extend: {
/**
* Initialize routes and request extensions.
*
* Options:
*
* - path path from which to serve static files. Defaults to <root>/public
*
* @param {hash} options
* @api private
*/
init: function(options) {
options = options || {}
options.path = options.path || set('root') + '/public'
// Routes
get('/public/*', function(file){
this.sendfile(options.path + '/' + file)
})
// Request
Request.include({
/**
* Transfer static file at the given _path_.
*
* @param {string} path
* @return {Request}
* @api public
*/
sendfile: function(path) {
(new exports.File(path)).sendTo(this)
return this
},
/**
* Transfer static _file_ as an attachment.
* The basename of _file_ is used as the attachment filename.
*
* @param {string} file
* @return {Request}
* @api public
*/
download: function(file) {
return this.attachment(path.basename(file)).sendfile(file)
}
})
}
}
})
+114 -41
Ver Arquivo
@@ -5,17 +5,37 @@
* Module dependencies.
*/
var posix = require('posix'),
utils = require('express/utils')
var Request = require('express/request').Request,
extname = require('path').extname,
fs = require('fs')
/**
* Supported template engines.
* Cache supported template engine exports.
*/
var engine = {
ejs: require('ejs'),
haml: require('haml'),
sass: require('sass')
var engines = {}
/**
* View cache.
*/
var cache = { views: {}, partials: {}}
/**
* Cache view files where _type_ is
* "partials" or "views".
*
* @param {string} type
* @api public
*/
function cacheFiles(type) {
var dir = set(type)
fs.readdirSync(dir).each(function(file){
file = dir + '/' + file
if (!fs.statSync(file).isFile()) return
cache[type][file] = fs.readFileSync(file)
})
}
// --- View
@@ -31,66 +51,119 @@ exports.View = Plugin.extend({
// Settings
set('views', function(){ return set('root') + '/views' })
if (!set('views'))
set('views', function(){ return set('root') + '/views' })
if (!set('partials'))
set('partials', function(){ return set('views') + '/partials' })
// Cache views in memory
if (set('cache view contents'))
cacheFiles('views')
if (set('cache view partials'))
cacheFiles('partials')
// Request
Request.include({
/**
* Render _view_ partial with _options_.
* View Request#render() for additional options.
*
* Options:
* - as: String name for the id used to which "collection" assign it's current value.
* - collection: Array of objects, the name is derived from
* the view name itself. For example 'video.haml.html'
* will have an object "video" available to it.
*
* @param {string} view
* @param {hash} options
* @settings 'partials', 'cache view partials'
* @return {string}
* @api public
*/
partial: function(view, options) {
var options = options || {}
options.partial = true
options.layout = false
if (options.collection) {
var name = options.as || view.split('.').first,
len = options.collection.length
options.locals = options.locals || {}
options.locals.__length__ = len
return options.collection.map(function(val, i){
options.locals.__isFirst__ = i === 0
options.locals.__index__ = i
options.locals.__isLast__ = i === len - 1
options.locals[name] = val
return this.render(view, options)
}, this).join('')
} else
return this.render(view, options)
},
/**
* Render _view_ with _options_.
*
* Views are looked up relative to the'views' path setting.
* View filenames should conform to ANY.ENGINE.TYPE so for example
* 'layout.ejs.html', 'ejs' represents the template engine, 'html'
* View filenames should conform to NAME.TYPE.ENGINE so for example
* 'layout.html.ejs', 'ejs' represents the template engine, 'html'
* represents the type of content being rendered, which is then passed
* to contentType().
*
* Engines must export a render() method accepting the template string
* and a hash of options.
* and a hash of options. Engines can respond to the options listed below
* as well as their own arbitrary ones. The "filename" option is always
* passed as the path to the given _view_, allowing engines to perform
* better error reporting.
*
* Options:
*
* - layout: The layout to use, none when falsey. Defaults to 'layout'
* - context: Most engines support an evaluation context (the 'this' keyword)
* - 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.
*
* Optionally you may also pass a _callback_ function which
* will be called instead of responding with the 200 status code.
*
* @param {string} view
* @param {hash} options
* @param {object} options
* @param {function} callback
* @settings 'views', 'cache view contents'
* @api public
*/
render: function(view, options) {
var self = this,
options = options || {},
path = set('views') + '/' + view,
type = path.split('.').slice(-2)[0],
ext = utils.extname(path),
render: function(view, options, callback) {
var options = options || {},
type = options.partial ? 'partials' : 'views',
path = set(type) + '/' + view,
parts = view.split('.'),
engine = parts.last,
contentType = parts.slice(-2)[0],
layout = options.layout === undefined ? 'layout' : options.layout
self.contentType(ext)
function render(content) {
content = engine[type].render(content, options)
if (layout)
self.render(layout + '.' + type + '.' + ext, process.mixin(true, options, {
layout: false,
locals: {
body: content
}
}))
else
self.halt(200, content)
}
if (set('cache view contents') && self.cache.get(path))
render(self.cache.get(path))
options.filename = path
if (set('cache view contents'))
options.cache = true
var content = cache[type][path] || fs.readFileSync(path)
options.context = options.context || this
content = (engines[engine] = engines[engine] || require(engine)).render(content, options)
if (type === 'views') this.contentType(contentType)
if (layout)
this.render([layout, contentType, engine].join('.'), options.mergeDeep({
layout: false,
locals: { body: content }
}), callback)
else if (type === 'partials')
return content
else if (callback)
callback.call(this, null, content)
else
posix.cat(path).addCallback(function(content){
set('cache view contents') ?
render(self.cache.set(path, content)) :
render(content)
}).addErrback(function(e){
throw e
})
this.halt(200, content)
}
})
}
+162 -87
Ver Arquivo
@@ -5,30 +5,14 @@
* Module dependencies.
*/
var StaticFile = require('express/static').File,
var Event = require('express/event').Event,
showExceptions = require('express/pages/show-exceptions'),
notFound = require('express/pages/not-found'),
statusBodies = require('http').STATUS_CODES,
queryString = require('querystring'),
mime = require('express/mime'),
url = require('url')
// --- 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.url.pathname) + ' did not respond with a body string'
}
})
// --- Helpers
/**
@@ -46,7 +30,7 @@ exports.normalizePath = function(path) {
// --- Request
exports.Request = Class({
exports.Request = new Class({
/**
* Initialize with node's _request_ and _response_ objects.
@@ -61,18 +45,18 @@ exports.Request = Class({
* @param {object} response
* @api private
*/
init: function(request, response) {
process.mixin(true, this, request)
constructor: function(request, response) {
this.merge(request)
response.headers = {}
this.response = response
this.response = response
this.url = url.parse(this.url)
this.url.pathname = exports.normalizePath(this.url.pathname)
this.params = this.params || {}
this.params.path = {}
this.url.pathname = exports.normalizePath(this.url.pathname)
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){
this.plugins = Express.plugins.map(function(plugin){
return new plugin.klass(plugin.options)
})
},
@@ -88,17 +72,17 @@ 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.lowercase]
: this.response.headers[key.lowercase] = val
},
/**
* Return a param _key_ value or null.
* Return the param _key_ value or undefined.
*
* - Checks route populated path parameters
* - Checks POST parameters
* - Checks GET parameters
* - Checks POST parameters
* - Checks route populated path parameters
*
* @param {string} key
* @return {string}
@@ -106,9 +90,12 @@ exports.Request = Class({
*/
param: function(key) {
return this.params.get[key] ||
this.params.post[key] ||
this.params.path[key]
if (key in this.params.get)
return this.params.get[key]
if (key in this.params.post)
return this.params.post[key]
if (key in this.params.path)
return this.params.path[key]
},
/**
@@ -132,20 +119,30 @@ exports.Request = Class({
accepts: function() {
var accept = this.header('accept')
return accept ?
$(arguments).any(function(path){
var type = mime.type(path)
if (!accept) return true
return arguments.any(function(path){
var type = mime.type(path)
return accept.indexOf(type) !== -1 ||
accept.indexOf(type.split('/')[0]+'/*') !== -1
}) :
true
accept.indexOf(type.split('/')[0] + '/*') !== -1
})
},
/**
* Check if the request was an xmlHttpRequest (ajax).
*
* @return {bool}
* @api public
*/
get isXHR() {
return (this.header('x-requested-with') || '').lowercase === 'xmlhttprequest'
},
/**
* Set response status to _code_.
*
* @param {int} code
* @return {Request)
* @return {Request}
* @api public
*/
@@ -155,26 +152,30 @@ exports.Request = Class({
},
/**
* Immediately respond with response _code_, _body_ and optional _encoding_.
* Immediately respond with response _code_, _body_.
*
* The status _code_ defaults to to 404, and _body_ will
* default to the default body associated with the response
* _code_.
*
* When an invalid status _code_ is passed, InvalidStatusCode
* will be thrown.
* Optionally an _encoding_ may be passed, followed by
* a _callback_ function.
*
* @param {int} code
* @param {string} body
* @param {string} encoding
* @param {function} callback
* @see statusBodies
* @api public
*/
halt: function(code, body, encoding) {
halt: function(code, body, encoding, callback) {
if (encoding instanceof Function)
callback = encoding,
encoding = null
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)
},
/**
@@ -186,14 +187,33 @@ 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(err) {
if (err)
if (callback !== undefined) callback(err)
else self.error(err)
self.response.writeHeader(self.response.status, self.response.headers)
self.response.write(self.response.body, encoding)
self.response.end()
}, true)
},
/**
* Pass control to the next matching route, or
* the given _path_.
*
* NOTE: _path_ may be the request pathname only,
* and may not contain a query string etc.
*
* @param {string} path
* @api public
*/
pass: function(path) {
this.passed = path || true
return this
},
/**
@@ -211,38 +231,41 @@ exports.Request = Class({
},
/**
* Trigger even _name_ with optional _data_.
* Trigger event _name_ with optional _data_ and _callback_ function.
* The _callback_ function may be the second or third argument.
*
* @param {string} name
* @param {hash} data
* @param {object} data
* @param {function} callback
* @param {bool} reverse
* @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))
})
trigger: function(name, data, callback, reverse) {
if (data instanceof Function)
reverse = callback,
callback = data,
data = null
data = data || {}
data.merge({ request: this, response: this.response })
var self = this,
complete = 0,
total = this.plugins.length,
plugins = reverse
? self.plugins.reverse()
: self.plugins
;(function next(err) {
if (err || complete === total)
callback(err)
else {
if (plugins.at(complete++).trigger(new Event(name, data), next) !== true)
next()
}
})()
return this
},
/**
* Transfer static file at the given _path_ as an attachment.
* The basename of _path_ is used as the attachment filename.
*
* @param {string} path
* @return {Request}
* @api public
*/
download: function(path) {
return this.attachment(basename(path)).sendfile(path)
},
/**
* Set Content-Disposition header to 'attachment',
* with optional file _path_.
@@ -253,22 +276,74 @@ 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
},
/**
* Transfer static file at the given _path_.
* Handle exceptions.
*
* When an error route is defined via the DSL it
* will then be called regardless of any setting that
* may be present. ("throw exceptions" will still work)
*
* error(function(err){
* this.halt(500, 'your app sucks!')
* })
*
* When "html" is accepted, and "show exceptions" is enabled
* the show-exceptions page will be shown. Otherwise
* the request will halt with default 500 status body.
*
* When "throw exceptions" is enabled the error will be
* re-thrown, terminating the process (unless otherwise caught).
*
* @param {Error} err
* @return {Request}
* @settings 'throw exceptions', 'show exceptions'
* @api public
*/
error: function(err) {
if (Express.error)
Express.error.call(this, err)
else if (this.accepts('html') && set('show exceptions'))
this.halt(500, showExceptions.render(this, err))
else
this.halt(500)
if (set('throw exceptions'))
throw err
return this
},
/**
* Halt with 404 Not Found.
*
* When a notFound route is defined via the DSL it
* will then be called regardless of any settings that
* may be present.
*
* notFound(function(){
* this.halt(404, 'Sorry your page cannot be found')
* })
*
* When "html" is accepted, and "helpful 404" is enabled
* the not-found page will be shown, otherwise the
* request will halt with default 404 status body.
*
* @param {string} path
* @return {Request}
* @api public
*/
sendfile: function(path) {
(new StaticFile(path)).send(this)
notFound: function() {
if (Express.notFound)
Express.notFound.call(this)
else if (this.accepts('html') && set('helpful 404'))
this.halt(404, notFound.render(this))
else
this.halt()
return this
}
})
+69 -20
Ver Arquivo
@@ -1,9 +1,57 @@
// Express - MockRequest - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
/**
* Module dependencies.
*/
var Request = require('express/request').Request,
path = require('path'),
fs = require('fs')
/**
* Sync fs.readFile()
*/
fs.readFile = function(path, encoding, callback) {
if (encoding instanceof Function)
callback = encoding,
encoding = null
try {
callback(null, fs.readFileSync(path, encoding))
} catch (e) {
callback(e)
}
}
/**
* Sync path.exists()
*/
path.exists = function(path, callback) {
try {
fs.statSync(path)
callback(true)
} catch (e) {
callback(false)
}
}
/**
* Sync fs.stat()
*/
fs.stat = function(path, callback) {
try {
callback(null, fs.statSync(path))
} catch (e) {
callback(e)
}
}
// --- MockRequest
var MockRequest = Class({
var MockRequest = new Class({
/**
* Default HTTP version.
@@ -20,12 +68,10 @@ var MockRequest = Class({
* @api private
*/
init: function(method, path, options) {
constructor: function(method, path, options) {
this.method = method
this.url = path
this.connection = {
remoteAddress: '127.0.0.1'
}
this.connection = { remoteAddress: '127.0.0.1' }
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',
@@ -33,19 +79,19 @@ var MockRequest = Class({
'accept-language': 'en-us',
'connection': 'keep-alive'
}
process.mixin(true, this, options)
this.mergeDeep(options)
}
})
// --- MockResponse
var MockResponse = Class({
var MockResponse = new Class({
/**
* Store _code_ and _headers_.
*/
sendHeader: function(code, headers) {
writeHeader: function(code, headers) {
this.status = code
this.headers = headers
},
@@ -54,7 +100,7 @@ var MockResponse = Class({
* Store _body_.
*/
sendBody: function(body) {
write: function(body) {
this.body = body
},
@@ -62,7 +108,7 @@ var MockResponse = Class({
* Flag response as finished.
*/
finish: function() {
end: function() {
this.finished = true
}
})
@@ -81,11 +127,11 @@ var MockResponse = Class({
* @api private
*/
function request(method, path, options, fn) {
var response = new MockResponse
var request = new MockRequest(method, path, options)
Express.server.route(request, response)
return response
function request(method, path, options, callback) {
var req = new MockRequest(method, path, options),
res = new MockResponse
Express.server.route(new Request(req, res))
return res
}
/**
@@ -99,6 +145,9 @@ reset = function() {
Express.routes = []
Express.plugins = []
Express.settings = {}
Express.params = {}
delete Express.notFound
delete Express.error
configure('test')
}
@@ -111,13 +160,13 @@ reset = function() {
*/
function route(method) {
return function(path, options, fn){
return function(path, options, callback){
if (options instanceof Function)
fn = options, options = {}
if (fn instanceof Function)
Express.routes.push(new Route(method, path, fn, options))
callback = options, options = {}
if (callback instanceof Function)
Express.routes.push(new Route(method, path, callback, options))
else
return request(method, path, options, fn)
return request(method, path, options, callback)
}
}
-63
Ver Arquivo
@@ -1,63 +0,0 @@
// Express - Static - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
var path = require('path'),
posix = require('posix')
// --- InvalidPathError
InvalidPathError = ExpressError.extend({
name: 'InvalidPathError',
init: function(path) {
this.message = "`" + path + "' is not a valid path"
}
})
// --- File
exports.File = Class({
/**
* Initialize with file _path_.
*
* @param {string} path
* @api public
*/
init: function(path) {
this.path = path
if (path.indexOf('..') != -1) throw new InvalidPathError(path)
},
/**
* Transfer static file to the given _request_.
*
* - 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 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) return request.halt()
posix.stat(file).addCallback(function(stats){
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')
})
})
})
}
})
+7 -65
Ver Arquivo
@@ -28,32 +28,6 @@ exports.uid = function() {
return uid
}
/**
* 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_.
*
@@ -63,45 +37,13 @@ exports.basename = function(path) {
*/
exports.escape = function(html) {
return html.toString()
return String(html)
.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')
}
/**
* Merge param _key_ and _val_ into _params_. Key
* should be a query string key such as 'user[name]',
@@ -118,17 +60,17 @@ 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){
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]
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
}
}
+1
Submodule lib/support/class added at 5ed0e4aaec
-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 e90bcc0190
+14
Ver Arquivo
@@ -0,0 +1,14 @@
{
"name": "Express",
"description": "Sinatra inspired web development framework",
"version": "0.9.0",
"keywords": ["framework", "sinatra", "web", "rest", "restful"],
"directories": {
"lib": "lib"
},
"scripts": {
"install": "git submodule update --init",
"test": "make test"
},
"engines": { "node": ">= 0.1.30" }
}
+4
Ver Arquivo
@@ -0,0 +1,4 @@
---
name: Express
description: Sinatra inspired web development framework
version: 0.9.0
+1
Ver Arquivo
@@ -0,0 +1 @@
%h2!= this.name
+1
Ver Arquivo
@@ -0,0 +1 @@
%h2 Hello
+2
Ver Arquivo
@@ -0,0 +1,2 @@
%html
%body!= body
+2
Ver Arquivo
@@ -0,0 +1,2 @@
%title= "Viewing " + name
%body!= body
+3
Ver Arquivo
@@ -0,0 +1,3 @@
%ul
- each item in items
!= this.partial('item.html.haml', { locals: { item: item }})
+2
Ver Arquivo
@@ -0,0 +1,2 @@
%title= this.title
%body!= body
+7
Ver Arquivo
@@ -0,0 +1,7 @@
%ul
- if (__isFirst__)
%li.first= article
- if (__isLast__)
%li.last= article
- if (!__isLast__ && !__isFirst__)
%li{ class: __index__ }= article
+1
Ver Arquivo
@@ -0,0 +1 @@
%li= item
+1
Ver Arquivo
@@ -0,0 +1 @@
%li= vid
+2
Ver Arquivo
@@ -0,0 +1,2 @@
%h1= name
%p= email
Ver Arquivo
+5 -4
Ver Arquivo
@@ -25,14 +25,15 @@ JSpec
// --- Matchers
matchers : {
have_tag : "jQuery(expected, actual).length == 1",
have_tag : "jQuery(expected, actual).length === 1",
have_one : "alias have_tag",
have_tags : "jQuery(expected, actual).length > 1",
have_many : "alias have_tags",
have_child : "jQuery(actual).children(expected).length == 1",
have_any : "alias have_tags",
have_child : "jQuery(actual).children(expected).length === 1",
have_children : "jQuery(actual).children(expected).length > 1",
have_text : "jQuery(actual).text() == expected",
have_value : "jQuery(actual).val() == expected",
have_text : "jQuery(actual).text() === expected",
have_value : "jQuery(actual).val() === expected",
be_enabled : "!jQuery(actual).attr('disabled')",
have_class : "jQuery(actual).hasClass(expected)",
+206 -103
Ver Arquivo
@@ -4,12 +4,13 @@
;(function(){
JSpec = {
version : '3.2.1',
version : '4.0.0',
assert : true,
cache : {},
suites : [],
modules : [],
allSuites : [],
sharedBehaviors: [],
matchers : {},
stubbed : [],
options : {},
@@ -68,6 +69,31 @@
return JSpec.cache[path] =
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path) ||
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path + '.html')
},
/**
* Load json fixture at _path_.
*
* JSON fixtures are resolved as:
*
* - <path>
* - <path>.json
*
* @param {string} path
* @return {object}
* @api public
*/
json_fixture: function(path) {
if (!JSpec.cache['json:' + path])
JSpec.cache['json:' + path] =
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path) ||
JSpec.tryLoading(JSpec.options.fixturePath + '/' + path + '.json')
try {
return eval('(' + JSpec.cache['json:' + path] + ')')
} catch (e) {
throw 'json_fixture("' + path + '"): ' + e
}
}
},
@@ -92,7 +118,7 @@
stats: JSpec.stats,
options: options,
results: map(results.allSuites, function(suite) {
if (suite.hasSpecs())
if (suite.isExecutable())
return {
description: suite.description,
specs: map(suite.specs, function(spec) {
@@ -126,10 +152,10 @@
*/
DOM : function(results, options) {
var id = option('reportToId') || 'jspec'
var report = document.getElementById(id)
var failuresOnly = option('failuresOnly')
var classes = results.stats.failures ? 'has-failures' : ''
var id = option('reportToId') || 'jspec',
report = document.getElementById(id),
failuresOnly = option('failuresOnly'),
classes = results.stats.failures ? 'has-failures' : ''
if (!report) throw 'JSpec requires the element #' + id + ' to output its reports'
function bodyContents(body) {
@@ -145,7 +171,7 @@
<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())
if (displaySuite && suite.isExecutable())
return '<tr class="description"><td colspan="2">' + escape(suite.description) + '</td></tr>' +
map(suite.specs, function(i, spec) {
return '<tr class="' + (i % 2 ? 'odd' : 'even') + '">' +
@@ -170,7 +196,7 @@
*/
Terminal : function(results, options) {
failuresOnly = option('failuresOnly')
var failuresOnly = option('failuresOnly')
print(color("\n Passes: ", 'bold') + color(results.stats.passes, 'green') +
color(" Failures: ", 'bold') + color(results.stats.failures, 'red') +
color(" Duration: ", 'bold') + color(results.duration, 'green') + " ms \n")
@@ -181,7 +207,7 @@
each(results.allSuites, function(suite) {
var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran
if (displaySuite && suite.hasSpecs()) {
if (displaySuite && suite.isExecutable()) {
print(color(' ' + suite.description, 'bold'))
each(suite.specs, function(spec){
var assertionsGraph = inject(spec.assertions, '', function(graph, assertion){
@@ -200,33 +226,7 @@
})
quit(results.stats.failures)
},
/**
* Console reporter.
*
* @api public
*/
Console : function(results, options) {
console.log('')
console.log('Passes: ' + results.stats.passes + ' Failures: ' + results.stats.failures)
each(results.allSuites, function(suite) {
if (suite.ran) {
console.group(suite.description)
each(suite.specs, function(spec){
var assertionCount = spec.assertions.length + ':'
if (spec.requiresImplementation())
console.warn(spec.description)
else if (spec.passed())
console.log(assertionCount + ' ' + spec.description)
else
console.error(assertionCount + ' ' + spec.description + ', ' + spec.failure().message)
})
console.groupEnd()
}
})
}
}
},
Assertion : function(matcher, actual, expected, negate) {
@@ -418,17 +418,21 @@
* @api private
*/
Suite : function(description, body) {
Suite : function(description, body, isShared) {
var self = this
extend(this, {
body: body,
description: description,
suites: [],
sharedBehaviors: [],
specs: [],
ran: false,
hooks: { 'before' : [], 'after' : [], 'before_each' : [], 'after_each' : [] },
shared: isShared,
hooks: { 'before' : [], 'after' : [],
'before_each' : [], 'after_each' : [],
'before_nested' : [], 'after_nested' : []},
// Add a spec to the suite
// Add a spec to the suite
addSpec : function(description, body) {
var spec = new JSpec.Spec(description, body)
@@ -437,16 +441,30 @@
spec.suite = this
},
// Add a hook to the suite
// Add a before hook to the suite
addBefore : function(options, body) {
body.options = options || {}
this.befores.push(body)
},
// Add an after hook to the suite
addAfter : function(options, body) {
body.options = options || {}
this.afters.unshift(body)
},
// Add a hook to the suite
addHook : function(hook, body) {
this.hooks[hook].push(body)
},
// Add a nested suite
addSuite : function(description, body) {
var suite = new JSpec.Suite(description, body)
addSuite : function(description, body, isShared) {
var suite = new JSpec.Suite(description, body, isShared)
JSpec.allSuites.push(suite)
suite.name = suite.description
suite.description = this.description + ' ' + suite.description
@@ -454,15 +472,17 @@
suite.suite = this
},
// Invoke a hook in context to this suite
// Invoke a hook in context to this suite
hook : function(hook) {
if (this.suite) this.suite.hook(hook)
if (hook != 'before' && hook != 'after')
if (this.suite) this.suite.hook(hook)
each(this.hooks[hook], function(body) {
JSpec.evalBody(body, "Error in hook '" + hook + "', suite '" + self.description + "': ")
})
},
// Check if nested suites are present
hasSuites : function() {
@@ -481,7 +501,15 @@
return !any(this.specs, function(spec){
return !spec.passed()
})
}
},
isShared : function(){
return this.shared
},
isExecutable : function() {
return !this.isShared() && this.hasSpecs()
}
})
},
@@ -629,7 +657,7 @@
},
describe : function(description, body) {
return JSpec.currentSuite.addSuite(description, body)
return JSpec.currentSuite.addSuite(description, body, false)
},
it : function(description, body) {
@@ -639,19 +667,31 @@
before : function(body) {
return JSpec.currentSuite.addHook('before', body)
},
after : function(body) {
return JSpec.currentSuite.addHook('after', body)
},
before_each : function(body) {
return JSpec.currentSuite.addHook('before_each', body)
},
after_each : function(body) {
return JSpec.currentSuite.addHook('after_each', body)
},
before_nested : function(body) {
return JSpec.currentSuite.addHook('before_nested', body)
},
after_nested : function(body){
return JSpec.currentSuite.addhook('after_nested', body)
},
shared_behaviors_for : function(description, body){
return JSpec.currentSuite.addSuite(description, body, true)
},
should_behave_like : function(description) {
return JSpec.shareBehaviorsOf(description)
}
@@ -728,8 +768,7 @@
evalHook : function(module, name, args) {
hook('evaluatingHookBody', module, name)
try { return module[name].apply(module, args) }
catch(e) { error('Error in hook ' + module.name + '.' + name + ': ', e) }
return module[name].apply(module, args)
},
/**
@@ -752,19 +791,69 @@
},
/**
* Find a suite by its description or name.
* Find a shared example suite by its description or name.
* First searches parent tree of suites for shared behavior
* before falling back to global scoped nested behaviors.
*
* @param {string} description
* @return {Suite}
* @api private
*/
findSuite : function(description) {
return find(this.allSuites, function(suite){
return suite.name == description || suite.description == description
})
findSharedBehavior : function(description) {
var behavior
return (behavior = JSpec.findLocalSharedBehavior(description))
? behavior
: JSpec.findGlobalSharedBehavior(description)
},
/**
* Find a shared example suite within the current suite's
* parent tree by its description or name.
*
* @param {string} description
* @return {Suite}
* @api private
*/
findLocalSharedBehavior : function(description) {
var behavior,
currentSuite = JSpec.currentSuite.suite
while (currentSuite)
if (behavior = find(currentSuite.suites, JSpec.suiteDescriptionPredicate(description)))
return behavior
else
currentSuite = currentSuite.suite
},
/**
* Find a shared example suite within the global
* scope by its description or name.
*
* @param {string} description
* @return {Suite}
* @api private
*/
findGlobalSharedBehavior : function(description) {
return find(JSpec.suites, JSpec.suiteDescriptionPredicate(description))
},
/**
* Build a predicate that will match a suite based on name or description
*
* @param {string} description
* @return {function}
* @api private
*/
suiteDescriptionPredicate : function(description) {
return function(suite){
return suite.name === description ||
suite.description === description
}
},
/**
* Share behaviors (specs) of the given suite with
* the current suite.
@@ -774,24 +863,13 @@
*/
shareBehaviorsOf : function(description) {
if (suite = this.findSuite(description)) this.copySpecs(suite, this.currentSuite)
else throw 'failed to share behaviors. ' + puts(description) + ' is not a valid Suite name'
var suite = JSpec.findSharedBehavior(description)
if (suite)
JSpec.evalBody(suite.body)
else
throw new Error("failed to find shared behaviors named `" + description + "'")
},
/**
* Copy specs from one suite to another.
*
* @param {Suite} fromSuite
* @param {Suite} toSuite
* @api public
*/
copySpecs : function(fromSuite, toSuite) {
each(fromSuite.specs, function(spec){
spec.assertions = []
toSuite.specs.push(spec)
})
},
/**
* Convert arguments to an array.
@@ -1301,12 +1379,27 @@
*/
describe : function(description, body) {
var suite = new JSpec.Suite(description, body)
var suite = new JSpec.Suite(description, body, false)
hook('addingSuite', suite)
this.allSuites.push(suite)
this.suites.push(suite)
},
/**
* Add a shared example suite to JSpec.
*
* @param {string} description
* @param {body} function
* @api public
*/
shared_behaviors_for : function(description, body) {
var suite = new JSpec.Suite(description, body, true)
hook('addingSuite', suite)
this.allSuites.push(suite)
this.suites.push(suite)
},
/**
* Return the contents of a function body.
*
@@ -1333,9 +1426,8 @@
var matchers = this.matchers
var context = this.context || this.defaultContext
var contents = this.contentsOf(body)
hook('evaluatingBody', dsl, matchers, context, contents)
try { with (dsl){ with (context) { with (matchers) { eval(contents) }}} }
catch(e) { error(errorMessage, e) }
hook('evaluatingBody', dsl, matchers, context, contents)
with (dsl){ with (context) { with (matchers) { eval(contents) }}}
},
/**
@@ -1355,8 +1447,9 @@
split('__END__')[0].
replace(/([\w\.]+)\.(stub|destub)\((.*?)\)$/gm, '$2($1, $3)').
replace(/describe\s+(.*?)$/gm, 'describe($1, function(){').
replace(/shared_behaviors_for\s+(.*?)$/gm, 'shared_behaviors_for($1, function(){').
replace(/^\s+it\s+(.*?)$/gm, ' it($1, function(){').
replace(/^ *(before_each|after_each|before|after)(?= |\n|$)/gm, 'JSpec.currentSuite.addHook("$1", function(){').
replace(/^ *(before_nested|after_nested|before_each|after_each|before|after)(?= |\n|$)/gm, 'JSpec.currentSuite.addHook("$1", function(){').
replace(/^\s*end(?=\s|$)/gm, '});').
replace(/-\{/g, 'function(){').
replace(/(\d+)\.\.(\d+)/g, function(_, a, b){ return range(a, b) }).
@@ -1419,25 +1512,28 @@
*/
runSuite : function(suite) {
this.currentSuite = suite
this.evalBody(suite.body)
suite.ran = true
hook('beforeSuite', suite), suite.hook('before')
each(suite.specs, function(spec) {
hook('beforeSpec', spec)
suite.hook('before_each')
JSpec.runSpec(spec)
hook('afterSpec', spec)
suite.hook('after_each')
})
if (suite.hasSuites()) {
each(suite.suites, function(suite) {
JSpec.runSuite(suite)
})
}
hook('afterSuite', suite), suite.hook('after')
this.stats.suitesFinished++
},
if (!suite.isShared())
{
this.currentSuite = suite
this.evalBody(suite.body)
suite.ran = true
hook('beforeSuite', suite), suite.hook('before'), suite.hook('before_nested')
each(suite.specs, function(spec) {
hook('beforeSpec', spec)
suite.hook('before_each')
JSpec.runSpec(spec)
hook('afterSpec', spec)
suite.hook('after_each')
})
if (suite.hasSuites()) {
each(suite.suites, function(suite) {
JSpec.runSuite(suite)
})
}
hook('afterSuite', suite), suite.hook('after_nested'), suite.hook('after')
this.stats.suitesFinished++
}
},
/**
* Report a failure for the current spec.
@@ -1620,7 +1716,7 @@
return request.responseText
}
else
error("failed to load `" + file + "'")
throw new Error("failed to load `" + file + "'")
},
/**
@@ -1637,12 +1733,19 @@
return this
}
}
// --- Node.js support
if (typeof GLOBAL === 'object' && typeof exports === 'object')
quit = process.exit,
print = require('sys').puts,
readFile = require('fs').readFileSync
// --- Utility functions
var main = this
var find = JSpec.any
var utils = 'haveStopped stub hookImmutable hook destub map any last pass fail range each option inject select \
var main = this,
find = JSpec.any,
utils = 'haveStopped stub hookImmutable hook destub map any last pass fail range each option inject select \
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() }
@@ -1770,4 +1873,4 @@
}
})
})()
})()
+2
Ver Arquivo
@@ -3,6 +3,8 @@
(function(){
var lastRequest
// --- Original XMLHttpRequest
var OriginalXMLHttpRequest = 'XMLHttpRequest' in this ?
+5 -26
Ver Arquivo
@@ -1,21 +1,12 @@
require.paths.unshift('spec', 'lib', 'spec/lib', 'spec/support/libxmljs')
require.paths.unshift('spec', 'lib', 'spec/lib')
require("jspec")
require("express")
require("express/spec")
print = require('sys').puts
quit = process.exit
print = puts
readFile = function(path) {
var result
require('posix')
.cat(path, "utf8")
.addCallback(function(contents){ result = contents })
.addErrback(function(){ throw new Error("failed to read file `" + path + "'") })
.wait()
return result
}
readFile = require('fs').readFileSync
function run(specs) {
specs.forEach(function(spec){
@@ -30,12 +21,9 @@ specs = {
'utils',
'request',
'mime',
'static',
'collection',
'plugins',
'plugins.cache',
'plugins.view',
'plugins.common-logger',
'plugins.content-length',
'plugins.method-override',
'plugins.body-decoder',
@@ -44,22 +32,13 @@ specs = {
'plugins.cookie',
'plugins.session',
'plugins.flash',
],
dependant: [
'element-collection'
]
'plugins.static',
]
}
switch (process.ARGV[2]) {
case 'independant':
run(specs.independant)
break
case 'dependant':
run(specs.dependant)
break
case 'all':
run(specs.independant)
run(specs.dependant)
break
default:
run([process.ARGV[2]])
-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
+72 -35
Ver Arquivo
@@ -33,18 +33,25 @@ describe 'Express'
describe '#set()'
describe 'given a key and value'
it 'should set the cache data'
store.set('foo', 'bar')
store.get('foo').should.eql 'bar'
var result
store.set('foo', 'bar', function(){
store.get('foo', function(val){
result = val
})
})
result.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'
var result
store.set('foo', 'bar', function(){
store.set('foo', 'baz', function(){
store.get('foo', function(val){
result = val
})
})
})
result.should.eql 'baz'
end
end
@@ -56,30 +63,41 @@ describe 'Express'
describe 'given an abitrary value'
it 'should serialize as JSON'
store.set('user', { name: 'tj' }).should.eql { name: 'tj' }
var result
store.set('user', { name: 'tj' }, function(val){
result = val
})
result.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' }
var result
store.set('user', { name: 'tj' }, function(){
store.get('user', function(val){
result = val
})
})
result.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' }
it 'should pass a set of caches'
var result
store.set('user:1', 'a', function(){
store.set('user:2', 'b', function(){
store.set('foo', 'bar', function(){
store.get('user:*', function(val){
result = val
})
})
})
})
result.should.eql { 'user:1': 'a', 'user:2': 'b' }
end
end
end
@@ -87,19 +105,34 @@ describe 'Express'
describe '#clear()'
describe 'given a key'
it 'should delete previous data'
store.set('foo', 'bar')
store.clear('foo')
store.get('foo').should.be_null
var result
store.set('foo', 'bar', function(){
store.clear('foo', function(){
store.get('foo', function(val){
result = val
})
})
})
result.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
var results = []
store.set('user:one', '1', function(){
store.set('user:two', '2', function(){
store.clear('user:*', function(){
store.get('user:one', function(val){
results.push(val)
store.get('user:two', function(val){
results.push(val)
})
})
})
})
})
results.should.eql [null, null]
end
end
end
@@ -107,12 +140,16 @@ describe 'Express'
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.data['user:one'].created = Number((5).minutes.ago)
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
store.data['user:two'].created = Number((2).seconds.ago)
store.reap((1).minute)
store.get('user:one', function(val){
val.should.be_null
})
store.get('user:two', function(val){
val.should.not.be_null
})
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
+5 -5
Ver Arquivo
@@ -17,13 +17,13 @@ describe 'Express'
compileCookie('foo', 'bar', options).should.eql 'foo=bar; path=/; domain=.vision-media.ca'
end
it 'should currectly format any Date objects'
it 'should correctly format any Date objects'
var options = {
expires: new Date('May 25, 1987 11:13:00'),
expires: new Date(Date.parse('May 25, 1987 11:13:00 PDT')),
path: '/foo',
domain: '.vision-media.ca'
}
compileCookie('foo', 'bar', options).should.eql 'foo=bar; expires=Mon, 25-May-1987 11:13:00 GMT; path=/foo; domain=.vision-media.ca'
compileCookie('foo', 'bar', options).should.eql 'foo=bar; expires=Mon, 25 May 1987 18:13:00 GMT; path=/foo; domain=.vision-media.ca'
end
it 'should convert true to a key without a value'
@@ -76,13 +76,13 @@ describe 'Express'
get('/user').headers['set-cookie'].should.eql 'SID=732423sdfs73243; path=/; secure'
end
it 'should join multiple cookies'
it 'should set multiple cookies'
get('/user', function(){
this.cookie('SID', '732423sdfs73243', { path: '/', secure: true })
this.cookie('foo', 'bar')
return ''
})
get('/user').headers['set-cookie'].should.eql 'SID=732423sdfs73243; path=/; secure, foo=bar; path=/'
get('/user').headers['set-cookie'].should.eql 'SID=732423sdfs73243; path=/; secure\nset-cookie: foo=bar; path=/'
end
end
end
+61 -16
Ver Arquivo
@@ -6,27 +6,72 @@ describe 'Express'
use(require('express/plugins/session').Session)
use(require('express/plugins/flash').Flash)
Session.store.clear()
var sess = new Base(123)
Session.store.commit(sess, function(){})
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...
describe '#flash()'
describe 'given a type and msg'
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').join(', ') })
post('/', headers)
post('/', headers)
get('/', headers).body.should.eql 'email sent, email sent'
end
end
it 'should return the message pushed'
get('/', function(){ return this.flash('info', 'email sent') })
get('/').body.should.eql 'email sent'
describe 'given a type'
describe 'when no messages have been pushed'
it 'should return null'
var headers = { headers: { cookie: 'sid=123' }}
get('/', function(){ return this.flash('info') || 'empty' })
get('/', headers).body.should.eql 'empty'
end
end
describe 'when messages have been pushed'
it 'should persist only until flushed'
var headers = { headers: { cookie: 'sid=123' }}
post('/', function(){ return this.flash('info', 'email sent') })
get('/', function(){ return (this.flash('info') || ['nope!']).join(', ') })
post('/', headers)
post('/', headers)
get('/', headers).body.should.eql 'email sent, email sent'
get('/', headers).body.should.eql 'nope!'
end
end
describe 'given no arguments'
it 'should provide access to all types'
var flash, headers = { headers: { cookie: 'sid=123' }}
post('/', function(){ return this.flash('info', 'email sent') })
post('/error', function(){ return this.flash('error', 'email failed to send') })
get('/', function(){ return flash = this.flash(), '' })
post('/', headers)
post('/', headers)
post('/error', headers)
get('/')
flash.should.eql { info: ['email sent', 'email sent'], error: ['email failed to send'] }
end
it 'should persist only until flushed'
var flash, headers = { headers: { cookie: 'sid=123' }}
post('/', function(){ return this.flash('info', 'email sent') })
post('/error', function(){ return this.flash('error', 'email failed to send') })
get('/', function(){ return flash = this.flash(), '' })
post('/', headers)
post('/', headers)
post('/error', headers)
get('/', headers)
flash.should.eql { info: ['email sent', 'email sent'], error: ['email failed to send'] }
get('/', headers)
flash.should.eql {}
end
end
end
end
end
+33 -8
Ver Arquivo
@@ -4,6 +4,7 @@ describe 'Express'
reset()
use(require('express/plugins/cookie').Cookie)
use(Session = require('express/plugins/session').Session)
Base = require('express/plugins/session').Base
Session.store.clear()
end
@@ -14,17 +15,28 @@ describe 'Express'
get('/login').headers['set-cookie'].should.match(/^sid=(\w+);/)
end
end
describe 'when sid cookie is present'
describe 'when existing sid cookie is present'
it 'should not set sid'
Session.store.commit(new Base(123))
get('/login', function(){ return '' })
get('/login', { headers: { cookie: 'sid=123' }}).headers.should.not.have_property 'set-cookie'
end
end
describe 'when unknown sid cookie is present'
it 'should set new sid'
get('/login', function(){ return '' })
var headers= get('/login', { headers: { cookie: 'sid=123' }}).headers
headers.should.have_property 'set-cookie'
headers['set-cookie'].should.not.be '123'
end
end
describe 'session Store.Memory'
before_each
memory = new (require('express/plugins/session').Store.Memory)
Session.store= memory = new (require('express/plugins/session').Store.Memory)
end
it 'should persist'
@@ -34,6 +46,7 @@ describe 'Express'
get('/login', function(){
return this.session.name
})
memory.commit(new Base(123))
var headers = { headers: { cookie: 'sid=123' }}
post('/login', headers)
get('/login', headers).status.should.eql 200
@@ -49,14 +62,22 @@ describe 'Express'
describe '#fetch()'
describe 'when the session does not exist'
it 'should return a new Session'
memory.fetch('1').should.have_property 'lastAccess'
var result
memory.fetch('1', function(error,session){
result= session
})
result.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
var result
memory.commit(new Base('1'))
memory.fetch('1', function(error, session){
result = session
})
result.id.should.eql '1'
end
end
end
@@ -73,9 +94,13 @@ describe 'Express'
describe '#length()'
it 'should return the number of session'
var length
memory.commit({ id: '1' })
memory.commit({ id: '2' })
memory.length().should.eql 2
memory.length(function(error, len) {
length = len
})
length.should.eql 2
end
end
+104
Ver Arquivo
@@ -0,0 +1,104 @@
describe 'Express'
describe 'StaticFile'
before
StaticFile = require('express/plugins/static').File
use(require('express/plugins/static').Static, { path: 'spec/fixtures' })
end
describe '#constructor'
it 'should accept and assign #path'
(new StaticFile('/foo/bar')).path.should.eql '/foo/bar'
end
it 'should throw an InvalidPathError when .. is found'
// 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
describe 'GET /public/*'
it 'should transfer static files'
get('/public/user.json').body.should.include '"name":'
end
end
describe '#sendfile()'
describe 'when the file exists'
it 'should transfer the file'
get('/public/*', function(file){
this.sendfile('spec/fixtures/' + file)
})
get('/public/user.json').body.should.include '"name":'
end
it 'should automatically set the content type based on extension'
get('/public/*', function(file){
this.sendfile('spec/fixtures/' + file)
})
get('/public/user.json').headers['content-type'].should.eql 'application/json'
end
end
describe 'when "cache static files" is enabled'
it 'should transfer the file'
enable('cache static files')
get('/public/*', function(file){
this.sendfile('spec/fixtures/' + file)
})
get('/cached', function(){
var self = this
this.cache.get('static:spec/fixtures/user.json', function(val){
self.halt(200, val ? 'yes' : 'no')
})
})
get('/cached').body.should.eql 'no'
get('/public/user.json').body.should.include '"name":'
get('/cached').body.should.eql 'yes'
get('/cached').body.should.eql 'yes'
end
it 'should automatically set the content type based on extension'
enable('cache static files')
get('/public/*', function(file){
this.sendfile('spec/fixtures/' + file)
})
get('/public/user.json').headers['content-type'].should.eql 'application/json'
get('/public/user.json').headers['content-type'].should.eql 'application/json'
get('/public/user.json').headers['content-type'].should.eql 'application/json'
end
end
end
describe '#download()'
describe 'when the file exists'
it 'should set attachment filename'
get('/report', function(){
this.download('report.pdf')
return 'foo'
})
get('/report').headers['content-disposition'].should.eql 'attachment; filename="report.pdf"'
end
it 'should transfer the file'
get('/public/*', function(file){
this.download('spec/fixtures/' + file)
})
get('/public/user.json').body.should.include '"name":'
end
it 'should automatically set the content type based on extension'
get('/public/*', function(file){
this.download('spec/fixtures/' + file)
})
get('/public/user.json').headers['content-type'].should.eql 'application/json'
end
end
end
end
end
+189
Ver Arquivo
@@ -11,5 +11,194 @@ describe 'Express'
set('views').should.eql 'spec/views'
end
end
describe 'set("partials")'
it 'should default to <views>/partials'
set('root', 'spec')
set('partials').should.eql 'spec/views/partials'
set('views', 'magicland')
set('partials').should.eql 'magicland/partials'
end
end
describe '#partial()'
before_each
set('views', 'spec/fixtures')
set('partials', 'spec/fixtures/partials')
end
describe 'given a valid view name'
it 'should render a partial'
get('/', function(){
this.render('list.html.haml', { locals: { items: ['foo', 'bar'] }})
})
get('/').body.should.include '<ul>'
get('/').body.should.include '<li>foo'
get('/').body.should.include '<li>bar'
end
it 'should render collections'
get('/', function(){
return this.partial('item.html.haml', {
collection: ['foo', 'bar']
})
})
get('/').body.should.include '<li>foo'
get('/').body.should.include '<li>bar'
end
it 'should render collections with a given object name'
get('/', function(){
return this.partial('video.html.haml', {
collection: ['im a movie', 'im another movie'],
as: 'vid'
})
})
get('/').body.should.include '<li>im a movie'
get('/').body.should.include '<li>im another movie'
end
it 'should pass __isFirst__, __isLast__, and __index__ to partials as locals'
get('/', function(){
return this.partial('article.html.haml', {
collection: ['a', 'b', 'c']
})
})
get('/').body.should.include '<li class="first">a'
get('/').body.should.include '<li class="1">b'
get('/').body.should.include '<li class="last">c'
end
end
end
describe '#render()'
before_each
set('views', 'spec/fixtures')
end
describe 'given a callback'
it 'should be passed the rendered content'
get('/', function(){
this.render('hello.html.haml', {}, function(err, content){
if (err) this.error(err)
else this.halt(203, content)
})
})
get('/').body.should.include '<html>\n<body>'
get('/').status.should.eql 203
end
end
describe 'given a valid view name'
describe 'and layout of the same type exists'
it 'should render the layout and view'
get('/', function(){
this.render('hello.html.haml')
})
get('/').body.should.include '<html>\n<body>'
get('/').body.should.include '<h2>Hello'
end
it 'should default context to the current request'
get('/', function(){
this.title = 'Welcome'
this.render('page.html.haml', { layout: false })
})
get('/').body.should.include '<title>Welcome'
end
it 'should set the content type based on the last path segment'
get('/', function(){
this.render('hello.html.haml')
})
get('/').headers['content-type'].should.eql 'text/html'
end
end
describe 'and layout of the same type does not exist'
it 'should throw an error'
get('/', function(){
this.render('hello.html.haml', { layout: 'front' })
})
-{ get('/') }.should.throw_error 'No such file or directory'
end
end
describe 'given a custom layout name'
it 'should render the layout and view'
get('/', function(){
this.title = 'Express'
this.render('hello.html.haml', { layout: 'page' })
})
get('/').body.should.include '<title>Express'
get('/').body.should.include '<h2>Hello'
end
end
describe 'when layout: false'
it 'should render the view only'
get('/', function(){
this.render('hello.html.haml', { layout: false })
})
get('/').body.should.not.include '<body>'
get('/').body.should.include '<h2>Hello'
end
end
describe 'when engine cannot be found'
it 'should throw an error'
get('/', function(){
this.render('user.html.invalid')
})
-{ get('/') }.should.throw_error "Cannot find module 'invalid'"
end
end
describe 'when locals are passed'
it 'should have direct access to locals'
get('/user', function(){
this.render('user.html.haml', {
locals: {
name: 'tj',
email: 'tj@vision-media.ca'
}
})
})
get('/user').body.should.include '<h1>tj'
get('/user').body.should.include '<p>tj@vision-media.ca'
end
it 'should have direct access to locals within the layout'
get('/user', function(){
this.render('user.html.haml', {
layout: 'layout.user',
locals: {
name: 'tj',
email: 'tj@vision-media.ca'
}
})
})
get('/user').body.should.include '<title>Viewing tj'
get('/user').body.should.include '<h1>tj'
get('/user').body.should.include '<p>tj@vision-media.ca'
end
end
describe 'when context is passed'
it 'should evaluate in context to that object'
get('/article', function(){
this.render('article.html.haml', {
context: {
name: 'Writing a Node.js Web Application'
}
})
})
get('/article').body.should.include '<h2>Writing a Node.js Web Application'
end
end
end
end
end
end
+169 -15
Ver Arquivo
@@ -39,6 +39,23 @@ describe 'Express'
end
end
describe '#isXHR'
it 'should return false unless X-Requested-With is "XMLHttpRequest"'
get('/', function(){ return this.isXHR ? 'yay' : 'nope' })
get('/').body.should.eql 'nope'
end
it 'should return true when X-Requested-With is "XMLHttpRequest"'
get('/', function(){ return this.isXHR ? 'yay' : 'nope' })
get('/', { headers: { 'x-requested-with': 'XMLHttpRequest' }}).body.should.eql 'yay'
end
it 'should be case insensitive'
get('/', function(){ return this.isXHR ? 'yay' : 'nope' })
get('/', { headers: { 'x-requested-with': 'xmlhttprequest' }}).body.should.eql 'yay'
end
end
describe '#accepts()'
describe 'when the Accept header is present'
it 'should return true if the mime type is acceptable'
@@ -55,7 +72,7 @@ describe 'Express'
describe 'when the Accept header is not present'
it 'should return true'
get('/user', function(){ return this.accepts('jpeg').toString() })
get('/user', { headers: { accept: null }}).body.should.eql 'true'
get('/user', { headers: { accept: '' }}).body.should.eql 'true'
end
end
@@ -107,18 +124,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: use throw_error when fixed...
get('/user', function(){ this.halt(123123) })
try { get('/user') }
catch (e) {
e.should.be_an_instance_of ExpressError
e.should.be_an_instance_of InvalidStatusCode
}
end
end
end
describe '#contentType()'
@@ -197,9 +202,11 @@ describe 'Express'
it 'should work with a query string'
get('/user', function(){
return String(this.param('page') || 'First page')
var page = this.param('page')
return page === undefined ? 'First page' : String(page)
})
get('/user').body.should.eql 'First page'
get('/user?page=0').body.should.eql '0'
get('/user?page=2').body.should.eql '2'
get('/user?foo[]=bar&page=5').body.should.eql '5'
end
@@ -210,6 +217,153 @@ 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
describe '#error()'
describe 'when an error route is defined'
it 'should be called'
var err
disable('throw exceptions')
global.error(function(e){
err = e
this.halt(500, 'FAIL!')
})
get('/', function(){ this.error(new Error('whoop')) })
get('/').body.should.eql 'FAIL!'
err.message.should.eql 'whoop'
end
end
describe 'when accepting "html"'
describe 'with "show exceptions" enabled'
it 'should render the show-exceptions page'
disable('throw exceptions')
enable('show exceptions')
get('/', function(){
this.error(new Error('fail!'))
})
get('/').body.should.include '<em>500</em> Error: fail!'
get('/').status.should.eql 500
end
end
describe 'when not accepting "html"'
it 'should render the default 500 status body'
var headers = { headers: { accept: 'text/plain' }}
disable('throw exceptions')
enable('show exceptions')
get('/', function(){
this.error(new Error('fail!'))
})
get('/', headers).body.should.eql 'Internal Server Error'
get('/', headers).status.should.eql 500
end
end
describe 'with "throw exceptions" enabled'
it 'should re-throw the exception'
enable('show exceptions')
enable('throw exceptions')
get('/', function(){
this.error(new Error('fail!'))
})
-{ get('/') }.should.throw_error Error, 'fail!'
end
end
end
describe 'with "show exceptions" disabled'
it 'should render the default 500 status body'
disable('throw exceptions')
get('/', function(){
this.error(new Error('fail!'))
})
get('/').body.should.eql 'Internal Server Error'
get('/').status.should.eql 500
end
end
describe 'with "throw exceptions" enabled'
it 'should re-throw the exception'
enable('throw exceptions')
get('/', function(){
this.error(new Error('fail!'))
})
-{ get('/') }.should.throw_error Error, 'fail!'
end
end
end
describe '#notFound()'
describe 'when a notFound route is defined'
it 'should be called'
notFound(function(){
this.halt(404, 'Sorry your page was not found')
})
get('/', function(){ this.notFound() })
get('/').body.should.eql 'Sorry your page was not found'
end
end
describe 'when accepting "html"'
describe 'with "helpful 404" enabled'
it 'should render the not-found page'
enable('helpful 404')
get('/', function(){ this.notFound() })
get('/').body.should.include '<em>404</em> Not Found'
get('/').status.should.eql 404
end
end
end
describe 'when not accepting "html"'
describe 'with "helpful 404" enabled'
it 'should render defaulat 404 status body'
var headers = { headers: { accept: 'text/plain' }}
enable('helpful 404')
get('/', function(){ this.notFound() })
get('/', headers).body.should.eql 'Not Found'
get('/', headers).status.should.eql 404
end
end
end
describe 'when "helpful 404" is disabled'
it 'should render defaulat 404 status body'
var headers = { headers: { accept: 'text/plain' }}
enable('helpful 404')
get('/', function(){ this.notFound() })
get('/', headers).body.should.eql 'Not Found'
get('/', headers).status.should.eql 404
end
end
end
end
end
+73 -13
Ver Arquivo
@@ -4,6 +4,70 @@ describe 'Express'
reset()
end
describe 'param()'
describe 'given no args'
it 'should throw an error'
-{ param() }.should.throw_error TypeError
end
end
describe 'given a non-string as the first argument'
it 'should throw an error'
-{ param(12) }.should.throw_error TypeError
end
end
describe 'given no callback function'
it 'should throw an error'
-{ param('foo') }.should.throw_error TypeError
end
end
describe 'given a non-function as the second argument'
it 'should throw an error'
-{ param('foo', 2) }.should.throw_error TypeError
end
end
describe 'with key / callback function'
it 'should pre-process a value before it is passed to a route'
param('user_id', function(val){
val = parseInt(val)
return isNaN(val) ? false : val
})
get('/user/:user_id', function(id){
return String(typeof id === 'number')
})
get('/user/99').body.should.eql 'true'
end
it 'should pass when false is explictly returned'
param('user_id', function(val){
val = parseInt(val)
return isNaN(val) ? false : val
})
get('/user/:user_id', function(id){
return String(typeof id === 'number')
})
get('/user/:name', function(name){
return name
})
get('/user/99').body.should.eql 'true'
get('/user/tj').body.should.eql 'tj'
end
it 'should evaluate in context to the request'
param('user_id', function(val){
this.pass()
})
get('/user/:user_id', function(id){
return String(typeof id === 'number')
})
get('/user/99').status.should.eql 404
end
end
end
describe 'route'
describe 'with callback function'
it 'should respond with a body string'
@@ -84,19 +148,6 @@ describe 'Express'
end
end
describe 'with no response body'
it 'should throw a InvalidResponseBody'
// TODO: use throw_error when fixed...
get('/user', function(){
this.respond()
})
try { get('/user') }
catch (e) {
e.should.be_an_instance_of InvalidResponseBody
}
end
end
describe 'with regular expression'
it 'should match'
get(/^\/user\/(\d+)\/(\w+)/, function(id, operation){
@@ -187,6 +238,15 @@ describe 'Express'
get('/report.pdf').body.should.eql 'yay'
end
it 'should match when mid-segment'
get('/user-:name-:id', function(name, id){
return name + ' ' + id
})
get('/user-tj-1').body.should.eql 'tj 1'
get('/user--1').status.should.eql 404
get('/user-tj-').status.should.eql 404
end
it 'should not match without value'
get('/report.:format', function(){
return 'yay'
-22
Ver Arquivo
@@ -1,22 +0,0 @@
describe 'Express'
describe 'StaticFile'
before
StaticFile = require('express/static').File
end
describe '#init'
it 'should accept and assign #path'
(new StaticFile('/foo/bar')).path.should.eql '/foo/bar'
end
it 'should throw an InvalidPathError when .. is found'
// TODO: use throw_error when fixed...
try { new StaticFile('/../foobar') }
catch (e) {
e.should.be_an_instance_of InvalidPathError
}
end
end
end
end
+2 -36
Ver Arquivo
@@ -4,21 +4,6 @@ describe 'Express'
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'
@@ -32,31 +17,12 @@ describe 'Express'
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'
utils.mergeParam('user[names][firstName]', 'tj', params)
params.user.names.firstName.should.eql 'tj'
end
end