« index

Coverage for /Users/kris/q-io/http-apps/route.js : 87%

123 lines | 108 run | 15 missing | 0 partial | 27 blocks | 15 blocks run | 12 blocks missing

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

  var Q = require("q");
  var StatusApps = require("./status");
  
  /**
   * Makes a  Q-JSGI app that only responds when there is nothing left
   * on the path to route.  If the there is unprocessed data on the
   * path, the returned app either forwards to the `notFound` app or
   * returns a `404 Not Found` response.
   *
   * @param {App} app a Q-JSGI application to
   * respond to this end of the routing chain.
   * @param {App} notFound (optional) defaults
   * to the `notFound` app.
   * @returns {App}
   */
  exports.Cap = function (app, notFound) {
      notFound = notFound || StatusApps.notFound;
      return function (request, response) {
          // TODO Distinguish these cases
          if (request.pathInfo === "" || request.pathInfo === "/") {
              return app(request, response);
          } else {
              return notFound(request, response);
          }
      };
  };
  
  /**
   * Wraps an app with a function that will observe incoming requests
   * before giving the app an opportunity to respond.  If the "tap"
   * function returns a response, it will be used in lieu of forwarding
   * the request to the wrapped app.
   */
  exports.Tap = function (app, tap) {
      return function (request, response) {
          var self = this, args = arguments;
          return Q.when(tap.apply(this, arguments), function (response) {
              if (response) {
                  return response;
              } else {
                  return app.apply(self, args);
              }
          });
      };
  };
  
  /**
   * Wraps an app with a "trap" function that intercepts and may
   * alter or replace the response of the wrapped application.
   */
  exports.Trap = function (app, trap) {
      return function (request, response) {
          return Q.when(app.apply(this, arguments), function (response) {
              if (response) {
                  response.headers = response.headers || {};
                  return trap(response, request) || response;
              }
          });
      };
  };
  
  /**
   * Makes a Q-JSGI app that branches requests based on the next
   * unprocessed path component.
   * @param {Object * App} paths a mapping from path components (single
   * file or directory names) to Q-JSGI applications for subsequent
   * routing.  The mapping may be a plain JavaScript `Object` record,
   * which must own the mapping properties, or an object that has
   * `has(key)` and `get(key)` methods in its prototype chain.
   * @param {App} notFound a Q-JSGI application
   * that handles requests for which the next file name does not exist
   * in paths.
   * @returns {App}
   */
  exports.Branch = function (paths, notFound) {
      if (!paths)
          paths = {};
      if (!notFound)
          notFound = StatusApps.notFound;
      return function (request, response) {
          if (!/^\//.test(request.pathInfo)) {
              return notFound(request, response);
          }
          var path = request.pathInfo.slice(1);
          var parts = path.split("/");
          var part = decodeURIComponent(parts.shift());
          if (Object.has(paths, part)) {
              request.scriptName = request.scriptName + part + "/";
              request.pathInfo = path.slice(part.length);
              return Object.get(paths, part)(request, response);
          }
          return notFound(request, response);
      };
  };
  
  /**
   * Returns the response of the first application that returns a
   * non-404 response status.
   *
   * @param {Array * App} apps a cascade of applications to try
   * successively until one of them returns a non-404 status.
   * @returns {App}
   */
  exports.FirstFound = function (cascade) {
      return function (request, response) {
          var i = 0, ii = cascade.length;
          function next() {
              var response = cascade[i++](request, response);
              if (i < ii) {
                  return Q.when(response, function (response) {
                      if (response.status === 404) {
                          return next();
                      } else {
                          return response;
                      }
                  });
              } else {
                  return response;
              }
          }
          return next();
      };
  };
« index | cover.io