uri_template 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
-
@@ -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