Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pat: support generating URLs from a Pattern #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

metcalf
Copy link
Contributor

@metcalf metcalf commented Nov 29, 2015

Adds support for generating paths from a Pattern and a mapping from named matches to string values. This is useful in writing API clients as it allows for constructing URLs that exactly match the routing table with semantically meaningful arguments.

that would match the given pattern with the provided variables. You must
provide a mapping for all named matches defined in the Pattern.
*/
func (p *Pattern) URLPath(vars map[pattern.Variable]string) (string, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm completely unattached to this method name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might just call it Path (or URL and return a url.URL). Seems a bit simpler.

We could also try to make it round-trip with Match and return a full-blown http.Request but that seems like it's a bit much.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One other thing: this takes map[pattern.Variable]string, while much of the rest of the pattern ecosystem produces and consumes map[pattern.Variable]interface{}. I know it's kind of ugly (and if I end up regretting one thing from this redesign I feel like it'll probably be that type), but we should probably accept that type here as well (and type assertion our way to victory)

func (p *Pattern) URLPath(vars map[pattern.Variable]string) (string, error) {
var missing []string

path := patternRe.ReplaceAllStringFunc(p.raw, func(s string) string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably prefer looping through pats and literals here: it's a little bit ugly, but I suspect it'll be more efficient, and the error handling becomes a bit nicer since we don't need to deal with escaping the callback.

@zenazn
Copy link
Member

zenazn commented Dec 8, 2015

Could you also rebase this on master so we pick up Travis tests?

Adds support for generating paths from a Pattern and a mapping from
named matches to string values. This is useful in writing API clients
as it allows for constructing URLs that exactly match the routing table
with semantically meaningful arguments.
@metcalf
Copy link
Contributor Author

metcalf commented Dec 18, 2015

@zenazn: Responded to comments and rebased on master to pick up tests.

The only comment I disagree with is changing the interface to accept a map[pattern.Variable]interface{}. The only place I see map[pattern.Variable]interface{} is in using AllVariables. I think I agree with that choice -- a custom pattern may want to implement e.g. coercion integer types and can implement typed getters (as you do with Pattern.Param).

In this case, it doesn't make much semantic sense to consider the output of Context.Value(AllVariables) as the input to generate a new URL since those variables are generated from the URL in the first place. Further, Pattern.Param implies a mapping from pattern.Variable to string for this package. I think this is a case where pat represents a specialization that is more clearly rooted in strings.

variables. You must provide a mapping for all named matches defined in
the Pattern.
*/
func (p *Pattern) URL(vars map[pattern.Variable]string) (url.URL, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you normally pass around *url.URL

@zenazn
Copy link
Member

zenazn commented Dec 18, 2015

The biggest reason to use the AllVariables type is not because it makes this package better (it doesn't: it makes it worse), but because it allows you to write abstractions across ~all pattern types (or at least the ones that opt-in to this mechanism).

So to be fair, I don't know that this even a thing I want, but that's the thing at stake here. I agree with you that it makes little sense for this package in particular, but a stronger goal of mine is to build a good ecosystem of stable and flexible types.

@metcalf
Copy link
Contributor Author

metcalf commented Dec 21, 2015

Thinking about it more, I think AllVariables should probably be a url.Values or some other string-string mapping.

The counter point would be providing support for things like "converters" in Flask (concretely, supporting /foo/<int:id>/bar/<bool:flag>). If I were implementing something like that in my pattern matcher, I'd probably expose it using typed getters like IntParam that can handle the conversion from strings to the desired type. If I didn't want to do that conversion every time the getter is called, I could cache my own conversions in the request context but I don't see a need to share that between patterns in a standard way.

Realistically I don't think I'd implement the "converter" semantic this way in Go at all. Instead I'd compose goji with another converter like Gorilla's schema. Using a common type like url.Values would enhance the composability of the pattern ecosystem with little cost to its power.

@metcalf
Copy link
Contributor Author

metcalf commented Dec 21, 2015

(oh... and I'm a little braindead getting over the flu so please tell me if that last comment was rambling and incomprehensible)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants