-
Notifications
You must be signed in to change notification settings - Fork 1
A Simpler Library? #2
Comments
URI Templates are very powerful. But would something less powerful do the trick as well? There are different kinds of parameters in an URI:
There can only be one fragment identifier, it does not have a name. Therefore we name this parameter routes.define :user, '/users/:id'
# then:
routes.uri_for :user, id: 12 #=> /users/12
routes.uri_for :user, id: 12, fragment: 'profile' #=> /users/12#profile
routes.uri_for :user, id: 12, x: 12 #=> /users/12?x=12
routes.uri_for :user, id: 12, x: [12, 13] #=> /users/12?x=12&x=13 It is not as powerful as what Shigeru provides now, but it is probably covering most cases. It would also make it easy to require path parameters while leaving query parameters and fragment identifiers optional (see #3). It however does not allow to whitelist query parameters. |
@frodsan As I can't mention you on Twitter for some reason (have you left Twitter?), let me ping you here 😄 |
Well hello there @moonglum! I like this idea. Let me jump right in with a comment. These two things look like they're the same, but are not: routes.uri_for(:user, id: 13) vs routes.uri_for(:users, name: 'alice', country: 'argentina') The downside I see here is that the interface is the same, just passing in a hash, but the outcome is different; the first example builds The only way I can be aware of this is by going to the definition of the routes and looking at the pattern. routes.define :user, '/users/{id}'
routes.define :users, '/users{?name,country}' I propose having a routes.uri_for(:users, query: {name: 'alice', country: 'argentina'}) This helps getting rid of the Also, what do you think of a way of returning a My 2 cents :) |
Hey @tonchis – glad to hear from you ❤️ Haven't read you in a while 😉 Thanks for the feedback! Keep it coming 😄 Query ParametersThat's a very good point, yes. This would also create a parity to the other "special parameter" routes.uri_for(:user, path: { id: 13 }) On the other hand this would be quite unwieldy. The reason why I did it the way I did, is that I want to map resources to URIs. So let's imagine I have a user object. Let's say it is an Ohm::Model, then I can ask for the Hash representation via # routes.define :user, '/users/{id}'
routes.uri_for(:user, user.attributes) #=> '/users/13' If I however change my URI scheme and provide the ID as a query parameter, I would not need to change the uri_for call: # routes.define :user, '/users/{?id}'
routes.uri_for(:user, user.attributes) #=> '/users?id=13' And I think this is really, really important. Why? Because it does not matter to the model how the URIs are structured. This is not part of the models job. It is probably also not the job of the caller of the method: With query and path parameters being on the same 'level', the details of the URI are decoupled from the caller of the method. URI ObjectI experimented with URI objects along the way. As we are talking about HTTP, it would make sense to use URI::HTTP. But that's one weeeeeird thing... We are mostly talking about relative links, while we of course allow absolute links. So we might have a link like this: URI::HTTP.build({:path => '/users'}) Now let me |
Query parameters
I think in this case the named parameter takes precedence, so it feels ok to call routes.uri_for(:user, id: 13) when dealing with routes.define(:user, "/users/{:id}")
Now I understand the motivation and makes more sense to me.
Agree 100%.
Not so sure here. If the caller wants urls I think it's ok to assume it has some knowledge of their parts. And if the use case is recurrent, they can create a helper method to pass all the values in one hash. URI ObjectUgh, that URI("/users").to_s # => "/users" And just leave the protocol up to the parser in URI(router.uri_for(:users)) So it comes down to what makes better sense in the context of Shigeru ¯_(ツ)_/¯ |
If you can get a 90% solution for 10% the code, it's a deal you should never reject :-) |
Query Parameter @tonchis
URI Object @tonchisThat's a good point, yes! So URI, not URI:HTTP ☑️ Again, see below. 90% solution for 10% of the code @soveranYep, agreed! But I think that requires determining how much of the solution it would cover – and that requires determining the usage of this lib – see below 😸 Popcorn Time @elcuervoHehe ❤️ Usage for this libI think the use case for this lib is to be combined with a web framework like Tynn, Cuba or Sinatra. It asks you to define the routes next to the routes you defined yourself. For example: require "tynn"
require "shigeru/tynn"
Tynn.plugin Shigeru::Tynn # an imaginary Tynn plugin
Tynn.define do
root do
get { ... }
post { ... }
end
on 'users' do
get { ... }
on :user_id do
get { ... }
end
end
end
# This uses the Tynn plugin now
Tynn.define_route :root, '/'
Tynn.define_route :users, '/users'
Tynn.define_route :user, '/users/{id}' Now those routes are mainly used in views, for example: <nav>
<ul>
<li><a href="{{ app.uri_for :root }}">Home</a></li>
<li><a href="{{ app.uri_for :users }}">Users</a></li>
<li><a href="{{ app.uri_for :user, current_user.attributes }}">Your Profile</a></li>
</ul>
</nav> What do you think? Would that be something useful for you? Do you see other use cases? |
hey! just the other day i was writing a tiny layer on top of the webmachine router DSL that allows me to a) give names to routes, b) use those names (instead of webmachine resources) to find particular routes and c) generate URLs for them. what i currently have (i'll soon need more, i.e. support for query params) is very basic and can only handle path param interpolation of hashes. then i stumbled over this project and naturally i'm interested :) since you're asking for use cases, here's what i'm thinking would be nice: a library focusing on the definition of a set of named routes along with a proper object model (or AST) that exposes these defined routes. such api could then be used to generate route definitions for different webframeworks programmatically, and of course for (webframework independent) URL generation. imo there should be just one place/concept in a typical app that knows about routes, and knows about them "deeply". if the information is rich enough (and imo there isn't that much to it for your typical webapp), it should be easy enough to generate code to adapt to different router DSL flavors and of course to generate URLs. i realize that this might be completely out of scope for what you had in mind for this lib, but i thought i'd share my thoughts! i'd surely be happy to drop my halfbaked url generation code for something i could use to generate my webmachine routes from, plus related URLs. |
@moonglum Yes, thinking about the usage/use cases is the best approach to solve what the lib needs and what it doesn't. I see your example as the most typical use case, filling up Now, about the interface, if I were the one writing this (and I'm not), I'd provide named parameters for the My reason behind this is the fact that while it is true that routes.uri_for(:user, current_user.attributes.merge({utm_source: "foo", id_of_something_else: "bar"})) And then handling that one big hash within the The only advantage I see on having Having said all this, a lib to handle the url generation within the context of the business logic of an app is very much in need, and we can hone it as we see how it's used in the real world. @snusnu welcome! great to have you in the thread. I'd really like to see some of the code you mentioned in your use case. As I said above, real world use cases will help finding what's important. |
Sorry, was busy for the last few days 😉 Here I am, back again! Hey @snusnu, glad to get your take on it 😄 Also thanks to @railsbros-dirk and @nerdbabe for discussing it with me in person ❤️
I'm wondering how you would wire up the routes to the code that should run accordingly. Or would you do that as a part of your AST transformation? I started hacking on a mashup of @soveran's syro with ideas from here. Not sure where it will lead me 😄 Definitely interested in your code 😄
I'm not entirely sure. On the one hand the advantage of having it one central place is clear: No duplication, all knowledge is in one place. On the other hand I'm not entirely sure if we are not talking about two responsibilities:
But there is of course a connection between those two, as one is the inverse function of the other... @tonchis: Ok, so no URI 😄 About the interface of I think that by using an URI template, we can whitelist the parameters – and by that allow the user to really provide an entire object (or its representation as a hash) to the method to get an URI for that object. And in this way of thinking, it makes a lot of sense to not let the caller think about different kinds of parameters. But I'm not sure how to deal with "additional information" like utm_source in a solution like that. |
I think this is similar to defining what message to send and how to implement it. A system can define the messages needed for it to work, and then it can decide where and how to implement the methods for handling those messages. But sending a message is not tied to a method definition. In this example, sending the message is something done by the browsers, and this library defines which messages the browser should send. Then the actual routes are implemented somewhere else, with any means necessary to accomplish the desired goal. It doesn't matter which routing library you use. |
Shigeru can change entirely as long as it has not hit 1.0, there's no part of it that can't be changed apart from the goal (provide a simple and clever
uri_for
helper). So this ticket can be used to discuss any idea how this could be done differently!The text was updated successfully, but these errors were encountered: