dispatch-plus.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /* appjet:version 0.1 */
  2. /* appjet:library */
  3. // Copyright (c) 2009, Herbert Vojčík
  4. // Licensed by MIT license (http://www.opensource.org/licenses/mit-license.php)
  5. /** Decoded segments of full path */
  6. request.fullPathSegments = request.path.split('/').map(decodeURIComponent);
  7. /** Decoded full path */
  8. request.fullPath = request.fullPathSegments.join('/');
  9. /**@ignore*/
  10. request.fullPathSegments.of = request.fullPath;
  11. /**
  12. * <p>Enhanced dispatcher function. Dispatches according to request.fullPath. Allows for:</p>
  13. * <ul><li>nonalphanumeric characters in path. Serve /robots.txt with <code>get_robots$txt</code></li>
  14. * <li>subpath matching: <ul>
  15. * <li>Serve /foo/, /foo/bar and /foo/bar/baz from <code>get_foo_</code></li>
  16. * <li><code>get_foo_main</code> serves only the /foo/ path, but not the /foo/bar, nor /foo</li>
  17. * <li>If there are more functions matching, the most specific (the longest one) wins</li>
  18. * <li><code>request.pagePath</code> is set to path of your handler (/foo), <code>request.pathTail</code> is set to the rest(/bar).</li>
  19. * <li><code>request.fullPath</code> contains the full path in decoded form</li>
  20. * <li>request.fullPathSegments, request.pagePathSegments and request.pathTailSegments contain segments of these paths</li>
  21. * </li></ul>
  22. * <li>404 handling is different: assign your 404 handler to dispatchPlus.f404</li>
  23. * <li>If you pass arguments to dispatchPlus call, they are passed to handler function. No more need for globals to pass initial information to handler functions.</li>
  24. * </ul>
  25. */
  26. function dispatchPlus(arg1, arg2, etc) {
  27. _rejuvenateFullPathSegments();
  28. var selectorSegments = request.fullPathSegments.map(function(x) {
  29. return [x.replace(/[\W]/g,'$$'), "_"];
  30. });
  31. selectorSegments.push([selectorSegments.pop()[0] || "main"]);
  32. var handler, handlerSegments, selector = arguments.callee[request.method] || request.method.toLowerCase();
  33. selectorSegments.forEach(function(segment, index) {
  34. selector += segment.join('');
  35. var candidate = appjet._internal.global[selector];
  36. if (typeof candidate === "function") {
  37. handler = candidate;
  38. handlerSegments = index + 1;
  39. }
  40. });
  41. if (handler) {
  42. request.pagePathSegments = request.fullPathSegments.slice(0, handlerSegments);
  43. request.pathTailSegments = request.fullPathSegments.slice(handlerSegments);
  44. request.pagePath = request.pagePathSegments.join('/');
  45. request.pathTail = request.fullPath.substring(request.pagePath.length);
  46. }
  47. (handler || arguments.callee.f404).apply(null, arguments);
  48. }
  49. /** The 404 (page not found) handler. You can replace it with your own. */
  50. dispatchPlus.f404 = function() {
  51. response.setStatusCode(404);
  52. printp("Path not found: ", request.fullPath);
  53. print(request);
  54. response.stop();
  55. };
  56. /** The prefix for GET method dispatcher functions. Default is "get", you can change it. */
  57. dispatchPlus.GET = "get";
  58. /** The prefix for HEAD method dispatcher functions. Default is "get", you can change it. */
  59. dispatchPlus.HEAD = "get";
  60. /** The prefix for POST method dispatcher functions. Default is "post", you can change it. */
  61. dispatchPlus.POST = "post";
  62. /** The prefix for CRON method dispatcher functions. Default is "cron", you can change it. */
  63. dispatchPlus.CRON = "cron";
  64. function _rejuvenateFullPathSegments() {
  65. if (request.fullPathSegments.of === request.fullPath) return;
  66. request.fullPathSegments = request.fullPath.split('/');
  67. request.fullPathSegments.of = request.fullPath;
  68. }
  69. /* appjet:server */
  70. import("lib-app/dispatch-plus");
  71. /**@ignore*/
  72. function printRequest() {
  73. printp({
  74. pagePath: request.pagePath,
  75. pagePathSegments: request.pagePathSegments,
  76. fullPath: request.fullPath,
  77. fullPathSegments: request.fullPathSegments,
  78. pathTail: request.pathTail,
  79. pathTailSegments: request.pathTailSegments
  80. });
  81. };
  82. /**@ignore*/
  83. function get_(arg) {
  84. printp("Serviced by ", CODE(arguments.callee));
  85. printp("Try these links:");
  86. print(UL(
  87. LI(A({href:"/long/path/here.2"}, "/long/path/here.2")),
  88. LI(A({href:"/papľuh/path-ing/"}, "/papľuh/path-ing/"))
  89. ));
  90. printp("Hello, ", arg, "!");
  91. printRequest();
  92. }
  93. /**@ignore*/
  94. function get_papľuh_(arg) {
  95. printp("Serviced by ", CODE(arguments.callee));
  96. printp("Hello, ", arg, "!");
  97. printRequest();
  98. }
  99. /**@ignore*/
  100. var get_papľuh_path$ing = "This is not a function";
  101. /**@ignore*/
  102. function get_long_path_() {
  103. throw new Error("This should not be called.");
  104. }
  105. /**@ignore*/
  106. function get_long_path_here$2(arg) {
  107. printp("Serviced by ", CODE(arguments.callee));
  108. printp("Hello, ", arg, "!");
  109. printRequest();
  110. }
  111. /**@ignore*/
  112. function get_long_path_here$2_() {
  113. throw new Error("This should not be called.");
  114. }
  115. /**@ignore*/
  116. function get_long_path_here$2_main() {
  117. throw new Error("This should not be called.");
  118. }
  119. printp("Features of extended dispatcher:");
  120. print(UL(
  121. LI("Any nonalphanumeric characters (except the slash, of course) are replaced by '$' (dollar sign). Now you can serve /robots.txt by get_robots$txt function."),
  122. LI("You can put a handler only to a beginning part of the path, if you use name like get_beginning_. This serves all paths in the form /beginning/... The path is broken to request.pagePath (the first part) and request.pathTail (the rest)."),
  123. LI("If you end function with _main, it will not serve subpaths (it only matches to full path ending with / (a slash)."),
  124. LI("If there are more subpath handlers and full path handlers, the most specific (the one that matches the longest part of the path) is chosen. If case of get_path_ and get_path_main, the latter will be chosen to service /path/, but the former to service /path/some/more."),
  125. LI("404 is handled other way: you specify your 404 handler by assigning it to dispatchPlus.f404 before placing a call."),
  126. LI("Any arguments you put in dispatchPlus call, gets copied into your handler function.")
  127. ));
  128. printp("The statments issued in this minitest is ", CODE('dispatchPlus("world")'));
  129. dispatchPlus("world");