snodgrass23 / base12

12factor.net web application boilerplate for node.js built on express.js
170 stars 26 forks source link

Needs resourceful routing (or express-resource to be upgraded to Express 3) #14

Closed hunterloftis closed 11 years ago

hunterloftis commented 12 years ago

Considering an API like this -

routes.js:

  var ac = app.controllers;

  app.resource('projects', ac.project, function() {
    this.resource('pages', ac.page, function() {
      this.resource('comments', ac.comment);
    });
    this.resource('reviews', ac.review);
  })
  .map('stats', ac.project.stats)
  .map('collaborators', ac.project.collaborators);
hunterloftis commented 12 years ago

Of course, since app knows about all of its controllers, we could optionally leave off the controller arg and have:

  app.resource('projects', function() {
    this.resource('pages', function() {
      this.resource('comments');
    });
    this.resource('reviews');
  })
  .map('stats', 'statistics')  // optional second arg if the method name doesn't match the mapped route
  .map('collaborators');
hunterloftis commented 12 years ago

Here's a comparison with a functional style declaration. Anyone have a strong argument for a preference? I kind of like the functional style because all resources at the same depth are indented the same amount. Also, non-resources that are embedded into the same route (the custom maps like /first/sub1) are on the same level.

  /**
   * should create 26 routes:
   *
   * /first (GET, POST)
   * /first/:first (GET, PUT, DELETE)
   * /first/:first/second (GET, POST)
   * /first/:first/second/:second (GET, PUT, DELETE)
   * /first/:first/second/:second/third (GET, POST)
   * /first/:first/second/:second/third/:third (GET, PUT, DELETE)
   * /first/:first/second/:second/third/innersub (GET)
   * /first/:first/ii (GET, POST)
   * /first/:first/ii/:ii (GET, PUT, DELETE)
   * /first/sub1 (GET, POST, PUT, DELETE)
   * /first/subtwo (POST)
   */

  // A
  app.resource('first', function() {
    this.resource('second', function() {
      this.resource('third').get('innersub');
    });
    this.resource('ii');
  })
  .all('sub1')
  .post('subtwo', 'sub2');

  // B
  app.resource(function(resource) {
    resource('first',
      resource('second',
        resource('third',
          get('innersub')
        )
      ),
      resource('ii'),
      all('sub1'),
      post('subtwo', 'sub2')
    );
  });
hunterloftis commented 12 years ago

Would love to make it easy to do something like this:

http://weblog.jamisbuck.org/2007/2/5/nesting-resources

hunterloftis commented 12 years ago

I dig the idea of turning 2-level-max nesting on by default:

  /**
   * /first (GET, POST)
   * /first/:first (GET, PUT, DELETE)
   * /first/:first/second (GET, POST)
   * /second/:second (GET, PUT, DELETE)
   * /second/:second/third (GET, POST)
   * /third/:third (GET, PUT, DELETE)
   * /second/:second/third/innersub (GET) <-- or should it be /third/:third/innersub? Where should mappings go when they're nested?
   * /first/:first/ii (GET, POST)
   * /ii/:ii (GET, PUT, DELETE)
   * /first/sub1 (GET, POST, PUT, DELETE) <-- or should it be /first/:first/sub1?
   * /first/sub2 (POST) <-- ditto
   */
katowulf commented 12 years ago

Of course, since app knows about all of its controllers, we could optionally leave off the controller arg and have:

I understand that this is a rails-inspired construct, but couldn't this be taken even one step farther? If it already knows about it's controllers, why even have to put the app.resource() in at all?

Why not just define app.controllers.first, app.controllers.second, et al and have them automagically become resources?

Anyone have a strong argument for a preference?

I don't have a strong argument, but I prefer //A (non-functional) as this is easier, from my experience, to wrap my primitive brain around : )

In any case, I'll be uber-excited to get my hands on this addition!

hunterloftis commented 12 years ago
// A
  app.resource('first', middleware1, middleware2, function() {
    this.resource('second', function() {
      this.resource('third', function() {
        this.get('innersub');
      );
    });
    this.resource('ii');
    this.all('sub1');
    this.post('subtwo', 'sub2');
  });

@katowulf, I agree that A seems more natural - so @dbecher suggested this to keep what I like from B (the levels). Should be merged in just a day or two!

snodgrass23 commented 11 years ago

express has been updated which includes resourceful routing