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/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