ConnectAi / cs

Cornerstone, an es6 web framework built on express.js
13 stars 1 forks source link

New controller syntax #1

Closed rosshadden closed 10 years ago

rosshadden commented 10 years ago

Putting this here so we don't lose track. There are a couple simple syntax rules that when used together can allow for a huge amount of flexibility. Note that when I say "route" I mean the keys of the controller object, and by "action" I mean the values of those keys.

  1. All routes are strings. A simple one-word action like "test" would get the URL /controller/test.
  2. Routes are based on express' route syntax, and thus "a/:b/:c?" is valid, and would get the following URLs:
    • /controller/a/test
    • /controller/a/hello/world
  3. "*" is shorthand for "everything from this point". "*" as a controller route gets the following URLs:
    • /controller/aoeu
    • /controller/asdf/qwer/zxcv
  4. Actions can either be functions or arrays of functions (just like express.js), or objects where the keys are HTTP verbs and the values are functions.
  5. Routes that start with a "/" are not treated as relative to the controller they are in, but instead to the root of the application. This allows for weird URL structures that don't adhere to MVC's typical /:controller/:action/:id? mantra. A route of "/test" in a controller would get the URL /test.

Using these building blocks, these are all things that will be supported for controller actions:

var user = {
    // The index handler.
    // URL: /user
    index: function(req, res, next) {
        res.view();
    },

    // Middleware that runs for every route in user.
    // URL: /user/list, /user/whatever, etc.
    "*": function(req, res, next) {
        // If logged in, just pass control to the next relevant action.  Otherwise throw a fit.
        if (req.session.user) return next();
        res.error("You aren't logged in!  Or something.");
    },

    // A simple action.
    // URL: /user/list
    list: function(req, res, next) {
        res.view();
    },

    // A simple action with verb-specific actions.
    // URL: GET /user/edit, POST /user/edit, etc.
    edit: {
        get: function(req, res, next) {
            res.view();
        },
        post: function(req, res, next) {
            app.db.save(req.body)
                .then(res.json);
        }
    },

    // An action with a parameter (you can have infinite parameters).
    // URL: /user/show/rosshadden, /user/show/optikalefx, etc.
    "show/:username": function(req, res, next, username) {
        app.models.User.findByUsername(username)
            .then(res.json);
    },

    // An action with an optional parameter that only runs on PUT.
    // URL: PUT /user/search, PUT /user/search/hello, etc.
    "search/:keyword?": {
        put: function(req, res, next, keyword) {
            app.db.findAll(`arbitraryColumn LIKE '%${keyword}%'`)
                .then((rows) => {
                    res.json(rows);
                });
        }
    },

    // A global route with a dynamic parameter.
    // URL: /rosshadden, /optikalefx, etc.
    "/:username": function(req, res, next, username) {
        // Technically this matches /user, but I think because that route was defined first this may just work.
        // If not, you'd just have to call "next()" if username === "user".
        // Or more programmatically, if (username in app.controllers).
        app.models.User.findByUsername(username)
            .then(res.json);
    }
};
optikalefx commented 10 years ago
edit: {
    get: [
        function(req, res, next) {
             res.view();
        },
        function(req, res, next) {
             res.view();
        },
    ],
    post: function(req, res, next) {
        app.db.save(req.body)
            .then(res.json);
    }
},
optikalefx commented 10 years ago
"edit/:id": {
    get: [
        function(req, res, next, id) {
             res.view();
        },
        function(req, res, next, id) {
             res.view();
        },
    ]
}