uri_template 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +53 -0
- data/README.md +56 -0
- data/lib/uri_template/colon.rb +88 -29
- data/lib/uri_template/expression.rb +33 -0
- data/lib/uri_template/literal.rb +53 -0
- data/lib/uri_template/rfc6570/expression/named.rb +72 -0
- data/lib/uri_template/rfc6570/expression/unnamed.rb +76 -0
- data/lib/uri_template/rfc6570/expression.rb +374 -0
- data/lib/uri_template/rfc6570/regex_builder.rb +117 -0
- data/lib/uri_template/rfc6570.rb +18 -536
- data/lib/uri_template/token.rb +64 -0
- data/lib/uri_template/utils.rb +47 -10
- data/lib/uri_template.rb +189 -88
- data/uri_template.gemspec +4 -4
- metadata +14 -10
- data/CHANGELOG +0 -46
- data/README +0 -67
- data/lib/uri_template/draft7.rb +0 -266
data/README
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
= URITemplate - a uri template library
|
2
|
-
|
3
|
-
{<img src="https://secure.travis-ci.org/hannesg/uri_template.png" />}[http://travis-ci.org/hannesg/uri_template] {<img src="https://gemnasium.com/hannesg/uri_template.png" />}[https://gemnasium.com/hannesg/uri_template]
|
4
|
-
|
5
|
-
With URITemplate you can generate URI based on simple templates. The syntax if defined by RFC 6570 ( http://tools.ietf.org/html/rfc6570 ).
|
6
|
-
|
7
|
-
For backward compatibility, there is an implementation based on draft 7 of the uri template spec ( http://tools.ietf.org/html/draft-gregorio-uritemplate-07 ), too. For the syntax read that spec. Draft 2 of that specification is implemented as addressable gem.
|
8
|
-
|
9
|
-
It's currently planed to add newer versions of that spec when they emerge. Therefore the module URITemplate just defines one abstract method ( expand ) and some methods to access the actual implementations.
|
10
|
-
|
11
|
-
From version 0.2.0, it will use escape_utils if available. This will significantly boost uri-escape/unescape performance if more characters need to be escaped ( may be slightly slower in trivial cases. working on that ... ), but does not run everywhere. To enable this, do the following:
|
12
|
-
|
13
|
-
````ruby
|
14
|
-
# escape_utils has to be loaded when uri_templates is loaded
|
15
|
-
gem 'escape_utils'
|
16
|
-
require 'escape_utils'
|
17
|
-
|
18
|
-
gem 'uri_template'
|
19
|
-
require 'uri_template'
|
20
|
-
|
21
|
-
UriTemplate::Utils.using_escape_utils? #=> true
|
22
|
-
|
23
|
-
|
24
|
-
== Examples
|
25
|
-
|
26
|
-
require 'uri_template'
|
27
|
-
|
28
|
-
tpl = URITemplate.new('http://{host}{/segments*}/{file}{.extensions*}')
|
29
|
-
|
30
|
-
# This will give: http://www.host.com/path/to/a/file.x.y
|
31
|
-
tpl.expand('host'=>'www.host.com','segments'=>['path','to','a'],'file'=>'file','extensions'=>['x','y'])
|
32
|
-
|
33
|
-
# This will give: { 'host'=>'www.host.com','segments'=>['path','to','a'],'file'=>'file','extensions'=>['x','y']}
|
34
|
-
tpl.extract('http://www.host.com/path/to/a/file.x.y')
|
35
|
-
|
36
|
-
|
37
|
-
== Benchmarks
|
38
|
-
|
39
|
-
* System: Core 2 Duo T9300, 4 gb ram, ubuntu 12.04 64 bit, ruby 1.9.3, 100_000 repetitions
|
40
|
-
* Implementation: RFC6570 ( version 0.3.0 ) vs. Addressable ( 2.2.8 )
|
41
|
-
* Results marked with * means that the template object is reused.
|
42
|
-
|
43
|
-
Addressable | Addressable* | RFC6570 | RFC6570* |
|
44
|
-
--Expansion-----------------------------------------------------------------------------------------
|
45
|
-
Empty string 8.505 | 8.439 | 0.539 | 0.064 |
|
46
|
-
One simple variable 19.717 | 19.721 | 3.031 | 1.169 |
|
47
|
-
One escaped variable 21.873 | 22.017 | 3.573 | 1.705 |
|
48
|
-
One missing variable 9.676 | 11.981 | 2.633 | 0.352 |
|
49
|
-
Path segments 29.901 | 31.698 | 4.929 | 3.051 |
|
50
|
-
Arguments 36.584 | 36.531 | 9.540 | 5.982 |
|
51
|
-
Full URI 109.102 | 116.458 | 19.806 | 10.548 |
|
52
|
-
Segments and Arguments 108.103 | 107.750 | 14.059 | 7.593 |
|
53
|
-
total 343.461 | 354.596 | 58.109 | 30.464 |
|
54
|
-
--Extraction----------------------------------------------------------------------------------------
|
55
|
-
Empty string 21.422 | 21.843 | 3.122 | 0.804 |
|
56
|
-
One simple variable 39.860 | 43.840 | 10.671 | 2.874 |
|
57
|
-
One escaped variable 51.321 | 50.790 | 10.963 | 2.040 |
|
58
|
-
One missing variable 26.125 | 26.320 | 9.400 | 1.847 |
|
59
|
-
Path segments 62.712 | 64.191 | 18.502 | 4.518 |
|
60
|
-
Arguments 81.350 | 81.258 | 20.762 | 5.401 |
|
61
|
-
Full URI 145.339 | 141.795 | 45.306 | 11.096 |
|
62
|
-
Segments and Arguments 124.431 | 122.885 | 34.208 | 8.126 |
|
63
|
-
Segments and Arguments ( not extractable ) 34.183 | 34.200 | 26.542 | 0.410 |
|
64
|
-
total 586.743 | 587.122 | 179.476 | 37.116 |
|
65
|
-
|
66
|
-
SUCCESS - URITemplate was faster in every test!
|
67
|
-
|
data/lib/uri_template/draft7.rb
DELETED
@@ -1,266 +0,0 @@
|
|
1
|
-
# -*- encoding : utf-8 -*-
|
2
|
-
# This program is free software: you can redistribute it and/or modify
|
3
|
-
# it under the terms of the Affero GNU General Public License as published by
|
4
|
-
# the Free Software Foundation, either version 3 of the License, or
|
5
|
-
# (at your option) any later version.
|
6
|
-
#
|
7
|
-
# This program is distributed in the hope that it will be useful,
|
8
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
-
# GNU General Public License for more details.
|
11
|
-
#
|
12
|
-
# You should have received a copy of the GNU General Public License
|
13
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
14
|
-
#
|
15
|
-
# (c) 2011 - 2012 by Hannes Georg
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
# A uri template which should comply with the uri template spec draft 7 ( http://tools.ietf.org/html/draft-gregorio-uritemplate-07 ).
|
20
|
-
# This class is here for backward compatibility. There is already a newer draft of the spec and an rfc.
|
21
|
-
class URITemplate::Draft7 < URITemplate::RFC6570
|
22
|
-
|
23
|
-
TYPE = :draft7
|
24
|
-
|
25
|
-
CHARACTER_CLASSES = URITemplate::RFC6570::CHARACTER_CLASSES
|
26
|
-
|
27
|
-
Utils = URITemplate::Utils
|
28
|
-
|
29
|
-
class Expression < URITemplate::RFC6570::Expression
|
30
|
-
|
31
|
-
def extract(position,matched)
|
32
|
-
name, expand, max_length = @variable_specs[position]
|
33
|
-
if matched.nil?
|
34
|
-
return [[ name , matched ]]
|
35
|
-
end
|
36
|
-
if expand
|
37
|
-
#TODO: do we really need this? - this could be stolen from rack
|
38
|
-
ex = self.class.hash_extractor(max_length)
|
39
|
-
rest = matched
|
40
|
-
splitted = []
|
41
|
-
found_value = false
|
42
|
-
# 1 = name
|
43
|
-
# 2 = value
|
44
|
-
# 3 = rest
|
45
|
-
until rest.size == 0
|
46
|
-
match = ex.match(rest)
|
47
|
-
if match.nil?
|
48
|
-
raise "Couldn't match #{rest.inspect} againts the hash extractor. This is definitly a Bug. Please report this ASAP!"
|
49
|
-
end
|
50
|
-
if match.post_match.size == 0
|
51
|
-
rest = match[3].to_s
|
52
|
-
else
|
53
|
-
rest = ''
|
54
|
-
end
|
55
|
-
if match[1]
|
56
|
-
found_value = true
|
57
|
-
splitted << [ match[1][0..-2], decode(match[2] + rest , false) ]
|
58
|
-
else
|
59
|
-
splitted << [ match[2] + rest, nil ]
|
60
|
-
end
|
61
|
-
rest = match.post_match
|
62
|
-
end
|
63
|
-
if !found_value
|
64
|
-
return [ [ name, splitted.map{|n,v| decode(n , false) } ] ]
|
65
|
-
else
|
66
|
-
return [ [ name, splitted ] ]
|
67
|
-
end
|
68
|
-
elsif self.class::NAMED
|
69
|
-
return [ [ name, decode( matched[1..-1] ) ] ]
|
70
|
-
end
|
71
|
-
|
72
|
-
return [ [ name, decode( matched ) ] ]
|
73
|
-
end
|
74
|
-
|
75
|
-
def to_r_source
|
76
|
-
source = []
|
77
|
-
first = true
|
78
|
-
vs = @variable_specs.size - 1
|
79
|
-
i = 0
|
80
|
-
if self.class::NAMED
|
81
|
-
@variable_specs.each{| var, expand , max_length |
|
82
|
-
value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*'}"
|
83
|
-
if expand
|
84
|
-
#if self.class::PAIR_IF_EMPTY
|
85
|
-
pair = "(?:#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self.class::PAIR_CONNECTOR)})?#{value}"
|
86
|
-
|
87
|
-
if first
|
88
|
-
source << "((?:#{pair})(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*)"
|
89
|
-
else
|
90
|
-
source << "((?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*)"
|
91
|
-
end
|
92
|
-
else
|
93
|
-
if self.class::PAIR_IF_EMPTY
|
94
|
-
pair = "#{Regexp.escape(var)}(#{Regexp.escape(self.class::PAIR_CONNECTOR)}#{value})"
|
95
|
-
else
|
96
|
-
pair = "#{Regexp.escape(var)}(#{Regexp.escape(self.class::PAIR_CONNECTOR)}#{value}|)"
|
97
|
-
end
|
98
|
-
|
99
|
-
if first
|
100
|
-
source << "(?:#{pair})"
|
101
|
-
else
|
102
|
-
source << "(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})?"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
first = false
|
107
|
-
i = i+1
|
108
|
-
}
|
109
|
-
else
|
110
|
-
@variable_specs.each{| var, expand , max_length |
|
111
|
-
last = (vs == i)
|
112
|
-
if expand
|
113
|
-
# could be list or map, too
|
114
|
-
value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*'}"
|
115
|
-
|
116
|
-
pair = "(?:#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self.class::PAIR_CONNECTOR)})?#{value}"
|
117
|
-
|
118
|
-
value = "#{pair}(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*"
|
119
|
-
elsif last
|
120
|
-
# the last will slurp lists
|
121
|
-
if self.class::CHARACTER_CLASS[:grabs_comma]
|
122
|
-
value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
|
123
|
-
else
|
124
|
-
value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
|
125
|
-
end
|
126
|
-
else
|
127
|
-
value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
|
128
|
-
end
|
129
|
-
if first
|
130
|
-
source << "(#{value})"
|
131
|
-
first = false
|
132
|
-
else
|
133
|
-
source << "(?:#{Regexp.escape(self.class::SEPARATOR)}(#{value}))?"
|
134
|
-
end
|
135
|
-
i = i+1
|
136
|
-
}
|
137
|
-
end
|
138
|
-
return '(?:' + Regexp.escape(self.class::PREFIX) + source.join + ')?'
|
139
|
-
end
|
140
|
-
|
141
|
-
protected
|
142
|
-
|
143
|
-
def transform_array(name, ary, expand , max_length)
|
144
|
-
if expand
|
145
|
-
ary.map{|value| escape(value) }
|
146
|
-
elsif ary.none?
|
147
|
-
[]
|
148
|
-
else
|
149
|
-
[ (self.class::NAMED ? escape(name)+self.class::PAIR_CONNECTOR : '' ) + ary.map{|value| escape(value) }.join(self.class::LIST_CONNECTOR) ]
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
module ClassMethods
|
154
|
-
|
155
|
-
def hash_extractor(max_length)
|
156
|
-
@hash_extractors ||= {}
|
157
|
-
@hash_extractors[max_length] ||= begin
|
158
|
-
value = "#{self::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
|
159
|
-
pair = "(#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self::PAIR_CONNECTOR)})?(#{value})"
|
160
|
-
source = "\\A#{Regexp.escape(self::SEPARATOR)}?" + pair + "(\\z|#{Regexp.escape(self::SEPARATOR)}(?!#{Regexp.escape(self::SEPARATOR)}))"
|
161
|
-
Regexp.new( source , Utils::KCODE_UTF8)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
end
|
166
|
-
|
167
|
-
extend ClassMethods
|
168
|
-
|
169
|
-
class Reserved < self
|
170
|
-
|
171
|
-
CHARACTER_CLASS = URITemplate::RFC6570::CHARACTER_CLASSES[:unreserved_reserved_pct]
|
172
|
-
OPERATOR = '+'.freeze
|
173
|
-
BASE_LEVEL = 2
|
174
|
-
|
175
|
-
def escape(x)
|
176
|
-
Utils.escape_uri(Utils.object_to_param(x))
|
177
|
-
end
|
178
|
-
|
179
|
-
def unescape(x)
|
180
|
-
Utils.unescape_uri(x)
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
184
|
-
|
185
|
-
class Fragment < self
|
186
|
-
|
187
|
-
CHARACTER_CLASS = URITemplate::RFC6570::CHARACTER_CLASSES[:unreserved_reserved_pct]
|
188
|
-
PREFIX = '#'.freeze
|
189
|
-
OPERATOR = '#'.freeze
|
190
|
-
BASE_LEVEL = 2
|
191
|
-
|
192
|
-
def escape(x)
|
193
|
-
Utils.escape_uri(Utils.object_to_param(x))
|
194
|
-
end
|
195
|
-
|
196
|
-
def unescape(x)
|
197
|
-
Utils.unescape_uri(x)
|
198
|
-
end
|
199
|
-
|
200
|
-
end
|
201
|
-
|
202
|
-
class Label < self
|
203
|
-
|
204
|
-
SEPARATOR = '.'.freeze
|
205
|
-
PREFIX = '.'.freeze
|
206
|
-
OPERATOR = '.'.freeze
|
207
|
-
BASE_LEVEL = 3
|
208
|
-
|
209
|
-
end
|
210
|
-
|
211
|
-
class Path < self
|
212
|
-
|
213
|
-
SEPARATOR = '/'.freeze
|
214
|
-
PREFIX = '/'.freeze
|
215
|
-
OPERATOR = '/'.freeze
|
216
|
-
BASE_LEVEL = 3
|
217
|
-
|
218
|
-
end
|
219
|
-
|
220
|
-
class PathParameters < self
|
221
|
-
|
222
|
-
SEPARATOR = ';'.freeze
|
223
|
-
PREFIX = ';'.freeze
|
224
|
-
NAMED = true
|
225
|
-
PAIR_IF_EMPTY = false
|
226
|
-
OPERATOR = ';'.freeze
|
227
|
-
BASE_LEVEL = 3
|
228
|
-
|
229
|
-
end
|
230
|
-
|
231
|
-
class FormQuery < self
|
232
|
-
|
233
|
-
SEPARATOR = '&'.freeze
|
234
|
-
PREFIX = '?'.freeze
|
235
|
-
NAMED = true
|
236
|
-
OPERATOR = '?'.freeze
|
237
|
-
BASE_LEVEL = 3
|
238
|
-
|
239
|
-
end
|
240
|
-
|
241
|
-
class FormQueryContinuation < self
|
242
|
-
|
243
|
-
SEPARATOR = '&'.freeze
|
244
|
-
PREFIX = '&'.freeze
|
245
|
-
NAMED = true
|
246
|
-
OPERATOR = '&'.freeze
|
247
|
-
BASE_LEVEL = 3
|
248
|
-
|
249
|
-
end
|
250
|
-
|
251
|
-
|
252
|
-
end
|
253
|
-
|
254
|
-
# @private
|
255
|
-
OPERATORS = {
|
256
|
-
'' => Expression,
|
257
|
-
'+' => Expression::Reserved,
|
258
|
-
'#' => Expression::Fragment,
|
259
|
-
'.' => Expression::Label,
|
260
|
-
'/' => Expression::Path,
|
261
|
-
';' => Expression::PathParameters,
|
262
|
-
'?' => Expression::FormQuery,
|
263
|
-
'&' => Expression::FormQueryContinuation
|
264
|
-
}
|
265
|
-
|
266
|
-
end
|