url helpers #20

Open
tj opened this Issue Jun 2, 2011 · 18 comments

Comments

Projects
None yet
4 participants
@tj
Member

tj commented Jun 2, 2011

No description provided.

@pacovell

This comment has been minimized.

Show comment Hide comment
@pacovell

pacovell Jun 27, 2011

Are you referring to rails-style route helpers?

Along the lines of:
/**

  • resource: lists @ /lists
    1. lists_path() === /lists
    1. list_path(list) === /lists/
    1. edit_list_path(list) == /lists//edit
    1. lists_url() === http://:/lists
  • resource: tasks @ /lists//tasks
    1. tasks_path(list) === /lists//tasks
    1. task_path(list, task) === /lists//tasks/
    1. edit_task_path(list, task) === /lists//tasks//edit
      */
      ?

I am working on these, would be happy to style them for better integration into express-resource. Right now I am attaching them as dynamicHelpers, but I haven't worked out the best way to make them available in the controllers.

Are you referring to rails-style route helpers?

Along the lines of:
/**

  • resource: lists @ /lists
    1. lists_path() === /lists
    1. list_path(list) === /lists/
    1. edit_list_path(list) == /lists//edit
    1. lists_url() === http://:/lists
  • resource: tasks @ /lists//tasks
    1. tasks_path(list) === /lists//tasks
    1. task_path(list, task) === /lists//tasks/
    1. edit_task_path(list, task) === /lists//tasks//edit
      */
      ?

I am working on these, would be happy to style them for better integration into express-resource. Right now I am attaching them as dynamicHelpers, but I haven't worked out the best way to make them available in the controllers.

@tj

This comment has been minimized.

Show comment Hide comment
@tj

tj Jun 27, 2011

Member

kinda yeah. controllers can just use regular modules, no need for helpers really, and most helpers are view related so controllers shouldn't really be using them anyway.

Member

tj commented Jun 27, 2011

kinda yeah. controllers can just use regular modules, no need for helpers really, and most helpers are view related so controllers shouldn't really be using them anyway.

@pacovell

This comment has been minimized.

Show comment Hide comment
@pacovell

pacovell Jun 27, 2011

If we write the functions to construct a route from the resource, those can be useful in both the view and the controller:

  • Controller:
    res.redirect(list_path(list))
  • View (Jade format)
    a(href=list_path(list)) My List

It would be nice to get two-for-one, at least if people want it. I think this can be done cleanly by writing a module for the controllers and then a wrapper module for the dynamicViewHelper that people can use if desired, but I am new to this architecture so please "redirect" to your vision.

Bigger picture, am I understanding the types of methods you are thinking of, or did you have something else in mind?

If we write the functions to construct a route from the resource, those can be useful in both the view and the controller:

  • Controller:
    res.redirect(list_path(list))
  • View (Jade format)
    a(href=list_path(list)) My List

It would be nice to get two-for-one, at least if people want it. I think this can be done cleanly by writing a module for the controllers and then a wrapper module for the dynamicViewHelper that people can use if desired, but I am new to this architecture so please "redirect" to your vision.

Bigger picture, am I understanding the types of methods you are thinking of, or did you have something else in mind?

@tj

This comment has been minimized.

Show comment Hide comment
@tj

tj Jun 27, 2011

Member

yeah, but it's JS so you'll never have something "clean" like list_path() automatically injected into the scope, you'll have req.list_path() or something, but yeah I agree there that it would be handy.

Member

tj commented Jun 27, 2011

yeah, but it's JS so you'll never have something "clean" like list_path() automatically injected into the scope, you'll have req.list_path() or something, but yeah I agree there that it would be handy.

@japj

This comment has been minimized.

Show comment Hide comment
@japj

japj Jun 28, 2011

this ticket looks like something I am also looking for in relation to a django 'reverse' use I have at the moment, see also https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse

Basically in django I have something like:

url(r'^(?P<version>[0-9.]+)/(?P<product>[^/]+)/*$', name="specific-product");

And later I have something like:

fullUrl = request.build_absolute_uri(reverse("specific-product", args=["foo", "bar"]))

and fullUrl is then basically "http://server:port/baseurl/foo/bar" which I currently return in an existing rest api.

Specially important here is that 'my code' would not need to know about server, port or baseurl. Just 'specific-product' and the fact that it expects 2 parameters (version and product).

japj commented Jun 28, 2011

this ticket looks like something I am also looking for in relation to a django 'reverse' use I have at the moment, see also https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse

Basically in django I have something like:

url(r'^(?P<version>[0-9.]+)/(?P<product>[^/]+)/*$', name="specific-product");

And later I have something like:

fullUrl = request.build_absolute_uri(reverse("specific-product", args=["foo", "bar"]))

and fullUrl is then basically "http://server:port/baseurl/foo/bar" which I currently return in an existing rest api.

Specially important here is that 'my code' would not need to know about server, port or baseurl. Just 'specific-product' and the fact that it expects 2 parameters (version and product).

@tj

This comment has been minimized.

Show comment Hide comment
@tj

tj Jun 29, 2011

Member

yikes! what a verbose way to get a url :s I know what you mean though, rails (and I guess every mvc framework) has similar.

Member

tj commented Jun 29, 2011

yikes! what a verbose way to get a url :s I know what you mean though, rails (and I guess every mvc framework) has similar.

@pacovell

This comment has been minimized.

Show comment Hide comment
@pacovell

pacovell Jul 8, 2011

japj, if I understand you correctly, you are actually using two segments of the path to identify a unique resource (foo+bar), and that is how your example differs from mine, am I right?

In normal RESTful routing, you would have
/resource_name/:id/sub_resource_name/:sub_resource_id

So I think you need
/resource_name/:id_part_1/:id_part_2

Correct? I have not looked into the guts of express-resource enough to know if this is a major change or not. If express-resource can accommodate it reasonably, then I suspect I could maneuver the URL generation code I have in mind to handle it as well. TJ?

pacovell commented Jul 8, 2011

japj, if I understand you correctly, you are actually using two segments of the path to identify a unique resource (foo+bar), and that is how your example differs from mine, am I right?

In normal RESTful routing, you would have
/resource_name/:id/sub_resource_name/:sub_resource_id

So I think you need
/resource_name/:id_part_1/:id_part_2

Correct? I have not looked into the guts of express-resource enough to know if this is a major change or not. If express-resource can accommodate it reasonably, then I suspect I could maneuver the URL generation code I have in mind to handle it as well. TJ?

@pacovell

This comment has been minimized.

Show comment Hide comment
@pacovell

pacovell Jul 12, 2011

OK, I have this a first pass, please have a look at donedotcom/express-resource@cd8e08c (and donedotcom/express-resource@544123a, I missed the test resources the first time around).

I've made a few design decisions that I am happy to take other directions on so that we can get this integrated:
1 - Top level object (app.resource.path.forum(forumObj))
2 - A bit hacky way to delete when resources become nested (but this is maybe a necessary evil)
3 - I chose to call the unnamed top level object (if present) "roots" so the plural works. This is a little weird (resource.app.path.roots()) and I am open to better suggestions. Without plural you have overloaded methods. This is related to how express resource handles this route -- for example, if this.name were still "forum" and the this.param were :forum even if it's the unnamed root object, we could implement this naturally as well. IMO, this would be preferable overall because I may choose to move it later and I should not need to change my code from :id to :forum,etc, to do that.

I am still new to JS so please feel free to aggressively comment on implementation decisions.

OK, I have this a first pass, please have a look at donedotcom/express-resource@cd8e08c (and donedotcom/express-resource@544123a, I missed the test resources the first time around).

I've made a few design decisions that I am happy to take other directions on so that we can get this integrated:
1 - Top level object (app.resource.path.forum(forumObj))
2 - A bit hacky way to delete when resources become nested (but this is maybe a necessary evil)
3 - I chose to call the unnamed top level object (if present) "roots" so the plural works. This is a little weird (resource.app.path.roots()) and I am open to better suggestions. Without plural you have overloaded methods. This is related to how express resource handles this route -- for example, if this.name were still "forum" and the this.param were :forum even if it's the unnamed root object, we could implement this naturally as well. IMO, this would be preferable overall because I may choose to move it later and I should not need to change my code from :id to :forum,etc, to do that.

I am still new to JS so please feel free to aggressively comment on implementation decisions.

@pacovell

This comment has been minimized.

Show comment Hide comment
@pacovell

pacovell Jul 12, 2011

Additional changes, see the head of my fork. Bug fixes and also I renamed functions to be _path for consistency when I integrate into the dynamicViewHelper -- it turns out to be a bad idea to have functions like 'users' injected into the view scope as they can often be the same name as local objects defined by the controller.

Additional changes, see the head of my fork. Bug fixes and also I renamed functions to be _path for consistency when I integrate into the dynamicViewHelper -- it turns out to be a bad idea to have functions like 'users' injected into the view scope as they can often be the same name as local objects defined by the controller.

@pacovell

This comment has been minimized.

Show comment Hide comment
@pacovell

pacovell Jul 12, 2011

Dynamic View Helpers implemented here, not sure where/if to include them in express-resource, please advise. If nothing else, maybe we can make them an example.

https://gist.github.com/1078292

Dynamic View Helpers implemented here, not sure where/if to include them in express-resource, please advise. If nothing else, maybe we can make them an example.

https://gist.github.com/1078292

@japj

This comment has been minimized.

Show comment Hide comment
@japj

japj Jul 13, 2011

sorry for the late reply, but I have been a bit busy with work.
I am going to look at this tomorrow evening and give you feedback then, is that ok?

japj commented Jul 13, 2011

sorry for the late reply, but I have been a bit busy with work.
I am going to look at this tomorrow evening and give you feedback then, is that ok?

@pacovell

This comment has been minimized.

Show comment Hide comment
@pacovell

pacovell Jul 13, 2011

Excellent, any time you can get to it.

On Wed, Jul 13, 2011 at 2:43 AM, japj <
reply@reply.github.com>wrote:

sorry for the late reply, but I have been a bit busy with work.
I am going to look at this tomorrow evening and give you feedback then, is
that ok?

Reply to this email directly or view it on GitHub:

visionmedia#20 (comment)

Excellent, any time you can get to it.

On Wed, Jul 13, 2011 at 2:43 AM, japj <
reply@reply.github.com>wrote:

sorry for the late reply, but I have been a bit busy with work.
I am going to look at this tomorrow evening and give you feedback then, is
that ok?

Reply to this email directly or view it on GitHub:

visionmedia#20 (comment)

@japj

This comment has been minimized.

Show comment Hide comment
@japj

japj Jul 14, 2011

Regarding

/resource_name/:id/sub_resource_name/:sub_resource_id 

I think that correctly resembles how I should have modeled my rest api. (I will need to update my server and client to correctly behave that way, but it makes sense to have it behave as nested resources).

For 'id' your example currently uses integers. Would this also work when 'id' is actual a word or something like "192.168.1.2"?

I note that the path functions expect an object with 'id' to be passed to it. However since action information is provided in the req.params, it might be usefull if one can do:

app.resource.path.forum_thread(req.params.forum, req.params.thread)

Last question, since I'm not entirely familiar with how this is done in nodejs: where would one get the server/ip part for the url? i.e. when app.resource.path.forum_thread(forumObj, threadObj) returns '/forums/5/threads/50', where does one get "http://servername:port/" from to combine it to a full url?

japj commented Jul 14, 2011

Regarding

/resource_name/:id/sub_resource_name/:sub_resource_id 

I think that correctly resembles how I should have modeled my rest api. (I will need to update my server and client to correctly behave that way, but it makes sense to have it behave as nested resources).

For 'id' your example currently uses integers. Would this also work when 'id' is actual a word or something like "192.168.1.2"?

I note that the path functions expect an object with 'id' to be passed to it. However since action information is provided in the req.params, it might be usefull if one can do:

app.resource.path.forum_thread(req.params.forum, req.params.thread)

Last question, since I'm not entirely familiar with how this is done in nodejs: where would one get the server/ip part for the url? i.e. when app.resource.path.forum_thread(forumObj, threadObj) returns '/forums/5/threads/50', where does one get "http://servername:port/" from to combine it to a full url?

@pacovell

This comment has been minimized.

Show comment Hide comment
@pacovell

pacovell Jul 14, 2011

  • Yes, you're right that this is the more common / REST standard method for addressing nested resources.
  • :id is a placeholder, and so I imagine anything that is a valid URL would be accepted here, although it is possible that it gets munged somewhere along the line. You would want to test it. In that case, of course, your autoload function (if any) should be sure to behave appropriately.
  • Do you mean that your req.params.forum objects would be ID objects? Ie, not hashes or objects, but actually the id is the value of req.params.forum? I think this could be an easy change to check if typeof obj.id === 'undefined' and if not, just use the object value directly. Please let me know if I'm on the right track.
  • I am not sure off the top of my head about your last question -- I would poke around in the server object, since it should be in there. Then the only question is making it available to your controllers or views. If you need help with that I can suggest some info.
  • Yes, you're right that this is the more common / REST standard method for addressing nested resources.
  • :id is a placeholder, and so I imagine anything that is a valid URL would be accepted here, although it is possible that it gets munged somewhere along the line. You would want to test it. In that case, of course, your autoload function (if any) should be sure to behave appropriately.
  • Do you mean that your req.params.forum objects would be ID objects? Ie, not hashes or objects, but actually the id is the value of req.params.forum? I think this could be an easy change to check if typeof obj.id === 'undefined' and if not, just use the object value directly. Please let me know if I'm on the right track.
  • I am not sure off the top of my head about your last question -- I would poke around in the server object, since it should be in there. Then the only question is making it available to your controllers or views. If you need help with that I can suggest some info.
@japj

This comment has been minimized.

Show comment Hide comment
@japj

japj Jul 15, 2011

With req.params.forum I meant the routing behaviour from http://expressjs.com/guide.html#routing where '/user/:id' ends up as req.params.id. I think it makes sense for the app.resource.path.xxx calls that information in req.params could be passed for some of the information.

japj commented Jul 15, 2011

With req.params.forum I meant the routing behaviour from http://expressjs.com/guide.html#routing where '/user/:id' ends up as req.params.id. I think it makes sense for the app.resource.path.xxx calls that information in req.params could be passed for some of the information.

@tj

This comment has been minimized.

Show comment Hide comment
@tj

tj Jul 15, 2011

Member

yeah :foo essentially attempts to consume as much as possible within a path segment, whereas * grabs as much as it can spanning segments ("/")

Member

tj commented Jul 15, 2011

yeah :foo essentially attempts to consume as much as possible within a path segment, whereas * grabs as much as it can spanning segments ("/")

ghost pushed a commit to donedotcom/express-resource that referenced this issue Jul 27, 2011

@pacovell

This comment has been minimized.

Show comment Hide comment
@pacovell

pacovell Jul 27, 2011

I've updated my fork to behave as described above:

user_path(5) or user_path("5") will result in /users/5. user_path({id: 5}) still works, of course. If there is a collision the object property version will win out.

I've updated my fork to behave as described above:

user_path(5) or user_path("5") will result in /users/5. user_path({id: 5}) still works, of course. If there is a collision the object property version will win out.

@antitoxic

This comment has been minimized.

Show comment Hide comment
@antitoxic

antitoxic Oct 15, 2012

Hi guys, I know this is an old thread but I've been working on a router that allows named routes and generating back a URL by route name and parameters. I closely investigated how the express router works especially dispatch area which almost kept the same.

If you can have a look at https://github.com/web-napopa/node-reversable-router.

I have a single url() helper that generates the URLs. If you think though I can change the logic and create a helper per route.

I also have other features in there. Give me shout if you find them useful or not.

I got it all working with XRegExp and your methods module.

Hi guys, I know this is an old thread but I've been working on a router that allows named routes and generating back a URL by route name and parameters. I closely investigated how the express router works especially dispatch area which almost kept the same.

If you can have a look at https://github.com/web-napopa/node-reversable-router.

I have a single url() helper that generates the URLs. If you think though I can change the logic and create a helper per route.

I also have other features in there. Give me shout if you find them useful or not.

I got it all working with XRegExp and your methods module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment