uritemplate
uritemplate copied to clipboard
Potential bug/Unspecified behaviour
Let's look at a template like this:
http://example.com/dictionary/{term:1}
We have two different behaviours depending upon the value used to expand term
.
>>> import uritemplate
>>> u = uritemplate.URITemplate('http://example.com/dictionary/{term:1}')
>>> u.expand(term='foo')
'http://example.com/dictionary/f'
>>> u.expand(term=['foo', 'bar', 'bogus'])
'http://example.com/dictionary/foo,bar,bogus'
A simplified version of this is simply
{term:1}
In other words:
>>> import uritemplate
>>> u = uritemplate.URITemplate('{term:1}')
>>> u.expand(term='foo')
'f'
>>> u.expand(term=['foo', 'bar', 'bogus'])
'foo,bar,bogus'
All versions of uritemplate
(and uritemplate.py
) exhibit this behaviour and the RFC does not provide clear guidance.
I have not investigated how other implementations in other languages handle this, though. There do not appear to be any examples in the RFC that combine lists with length limitations.
So jtacoma/uritemplates
behaves significantly differently:
package main
import (
"fmt"
"github.com/jtacoma/uritemplates"
)
func main() {
uritemplateStr := "{term}"
template, _ := uritemplates.Parse(uritemplateStr)
values := make(map[string]interface{})
values["term"] = [3]string{"foo", "bar", "bogus"}
expanded, _ := template.Expand(values)
fmt.Println(expanded)
}
When run produces:
%5Bfoo%20bar%20bogus%5D
Which un-encoded is
[foo bar bogus]
Which seems to be pretty egregiously wrong. (It produces the same output with {term*}
.)
That said, if we modify this to use {term:1}
we get only %5B
as the output.
Ruby's Addressable has different behaviour:
puts Addressable::Template.new('{term:1}').expand({
'term' => ['foo', 'bar', 'bogus'],
}).to_s
prints
f,b,b
I'm not sure I disagree with that either.
Meanwhile URIjs
raises an error for this. That may be the most reasonable path forward.
var URITemplate = require('urijs/src/URITemplate');
console.log(URITemplate('{term:1}').expand({term: ['foo', 'bar', 'bogus']}))
Actually, this is addressed in the specification itself in 2.4.1:
Prefix modifiers are not applicable to variables that have composite values.
With "composite values" being defined in the subsequent section (2.4.2) to be "either a list of values or an associative array of (name, value) pairs".
Hence, the correct behavior would be to do what URIjs
does.
Edit: of course, unless the confusion is specifically about what "not applicable" means, i.e. whether to ignore it (current behavior as implemented in this library) or simply be explicitly unsupported (like URIjs
).
Reading some more, the normative section on expansion implementation hints (Appendix A), the prefix modifier is only applicable if the variable value is a string; in all the "else if" options, the prefix modifier is simply not mentioned (i.e. not applicable). This non-normative "suggestion" may mean that the current implementation is sufficient.
However, section 3 does not exclude the fact that what URIjs
does is out of spec, depend on how that is read - fourth paragraph reads "such as an operator or value modifier that the template processor does not recognize or does not yet support" - given that there isn't an exhaustive definition for "error" defined, the template processor can just do what was described there.