uri_template 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/README +24 -103
- data/lib/uri_template.rb +4 -4
- data/lib/uri_template/colon.rb +11 -10
- data/lib/uri_template/draft7.rb +88 -76
- data/lib/uri_template/utils.rb +57 -10
- data/uri_template.gemspec +5 -3
- metadata +29 -7
data/CHANGELOG
CHANGED
data/README
CHANGED
@@ -21,109 +21,30 @@ Some implementations might be able to extract variables, too ( Draft 7 is! ).
|
|
21
21
|
|
22
22
|
== Benchmarks
|
23
23
|
|
24
|
-
System: Core 2 Duo
|
25
|
-
Implementation: Draft7
|
26
|
-
Results marked with * means that the template object is reused.
|
24
|
+
* System: Core 2 Duo T9300, 4 gb ram, Fedora 16 64 bit, ruby 1.9.3, 100_000 repetitions
|
25
|
+
* Implementation: Draft7 ( version 0.1.3 (pre) ) vs. Addressable ( 2.2.6 )
|
26
|
+
* Results marked with * means that the template object is reused.
|
27
|
+
|
28
|
+
Addressable | Addressable* | Draft7 | Draft7* |
|
29
|
+
--Expansion----------------------------------------------------------------------------------------
|
30
|
+
Empty string 8.601 | 8.529 | 0.451 | 0.057 |
|
31
|
+
One simple variable 19.832 | 19.675 | 2.746 | 0.928 |
|
32
|
+
One escaped variable 22.437 | 22.151 | 6.011 | 4.242 |
|
33
|
+
One missing variable 9.638 | 9.544 | 1.855 | 0.184 |
|
34
|
+
Path segments 25.353 | 25.413 | 3.171 | 1.372 |
|
35
|
+
Arguments 27.140 | 27.144 | 5.293 | 2.715 |
|
36
|
+
Full URI 75.599 | 75.680 | 11.147 | 5.246 |
|
37
|
+
Segments and Arguments 73.360 | 73.334 | 7.694 | 3.604 |
|
38
|
+
--Extraction---------------------------------------------------------------------------------------
|
39
|
+
Empty string 14.031 | 13.993 | 1.849 | 0.576 |
|
40
|
+
One simple variable 28.701 | 28.503 | 6.988 | 1.840 |
|
41
|
+
One escaped variable 33.237 | 33.358 | 8.354 | 3.155 |
|
42
|
+
One missing variable 17.201 | 17.051 | 6.318 | 1.177 |
|
43
|
+
Path segments 43.112 | 43.623 | 12.502 | 2.983 |
|
44
|
+
Arguments 54.448 | 54.090 | 13.772 | 3.394 |
|
45
|
+
Full URI 95.433 | 91.961 | 30.762 | 6.797 |
|
46
|
+
Segments and Arguments 80.508 | 79.570 | 23.204 | 5.210 |
|
47
|
+
Segments and Arguments ( not extractable ) 22.079 | 21.967 | 18.017 | 0.256 |
|
27
48
|
|
28
|
-
=== Expansion
|
29
|
-
"" vs "" => ""
|
30
|
-
user system total real
|
31
|
-
Addressable 0.070000 0.010000 0.080000 ( 0.081990)
|
32
|
-
UriTemplate 0.010000 0.000000 0.010000 ( 0.007668)
|
33
|
-
Addressable* 0.090000 0.000000 0.090000 ( 0.090763)
|
34
|
-
UriTemplate* 0.000000 0.000000 0.000000 ( 0.001633)
|
35
|
-
|
36
|
-
"{simple_string}" vs "{simple_string}" => "noneedtoescape"
|
37
|
-
user system total real
|
38
|
-
Addressable 0.560000 0.000000 0.560000 ( 0.560811)
|
39
|
-
UriTemplate 0.030000 0.000000 0.030000 ( 0.034104)
|
40
|
-
Addressable* 0.560000 0.000000 0.560000 ( 0.558118)
|
41
|
-
UriTemplate* 0.030000 0.000000 0.030000 ( 0.026850)
|
42
|
-
|
43
|
-
"{escaped_string}" vs "{escaped_string}" => "%2F%20%2F%25%2F%20%3F%2B"
|
44
|
-
user system total real
|
45
|
-
Addressable 0.560000 0.000000 0.560000 ( 0.562588)
|
46
|
-
UriTemplate 0.070000 0.000000 0.070000 ( 0.072649)
|
47
|
-
Addressable* 0.560000 0.000000 0.560000 ( 0.556001)
|
48
|
-
UriTemplate* 0.050000 0.000000 0.050000 ( 0.054520)
|
49
|
-
|
50
|
-
"{missing}" vs "{missing}" => ""
|
51
|
-
user system total real
|
52
|
-
Addressable 0.550000 0.010000 0.560000 ( 0.550707)
|
53
|
-
UriTemplate 0.040000 0.000000 0.040000 ( 0.035343)
|
54
|
-
Addressable* 0.550000 0.000000 0.550000 ( 0.556634)
|
55
|
-
UriTemplate* 0.010000 0.000000 0.010000 ( 0.003132)
|
56
|
-
|
57
|
-
"{-prefix|/|segments}" vs "{/segments*}" => "/a/b/c"
|
58
|
-
user system total real
|
59
|
-
Addressable 0.650000 0.000000 0.650000 ( 0.656996)
|
60
|
-
UriTemplate 0.030000 0.000000 0.030000 ( 0.038546)
|
61
|
-
Addressable* 0.660000 0.000000 0.660000 ( 0.655364)
|
62
|
-
UriTemplate* 0.020000 0.000000 0.020000 ( 0.018265)
|
63
|
-
|
64
|
-
"?{-join|&|one,two,three}" vs "{?one,two,three}" => "?one=1&two=2&three=3"
|
65
|
-
user system total real
|
66
|
-
Addressable 0.670000 0.000000 0.670000 ( 0.671649)
|
67
|
-
UriTemplate 0.080000 0.000000 0.080000 ( 0.078588)
|
68
|
-
Addressable* 0.670000 0.000000 0.670000 ( 0.669606)
|
69
|
-
UriTemplate* 0.030000 0.000000 0.030000 ( 0.035886)
|
70
|
-
|
71
|
-
"http://{host}/{-suffix|/|segments}?{-join|&|one,two,bogus}\#{fragment}" vs "http://{host}{/segments*}/{?one,two,bogus}{#fragment}" => "http://example.com/a/b/c/?one=1&two=2#foo"
|
72
|
-
user system total real
|
73
|
-
Addressable 0.800000 0.000000 0.800000 ( 0.802170)
|
74
|
-
UriTemplate 0.150000 0.000000 0.150000 ( 0.147875)
|
75
|
-
Addressable* 0.810000 0.000000 0.810000 ( 0.803928)
|
76
|
-
UriTemplate* 0.060000 0.000000 0.060000 ( 0.064376)
|
77
|
-
|
78
|
-
=== Extraction
|
79
|
-
"" => "" vs ""
|
80
|
-
user system total real
|
81
|
-
Addressable 0.150000 0.000000 0.150000 ( 0.144244)
|
82
|
-
UriTemplate 0.080000 0.000000 0.080000 ( 0.079467)
|
83
|
-
Addressable* 0.140000 0.000000 0.140000 ( 0.149177)
|
84
|
-
UriTemplate* 0.010000 0.000000 0.010000 ( 0.008808)
|
85
|
-
|
86
|
-
"noneedtoescape" => "{simple_string}" vs "{simple_string}"
|
87
|
-
user system total real
|
88
|
-
Addressable 0.350000 0.000000 0.350000 ( 0.345314)
|
89
|
-
UriTemplate 0.110000 0.000000 0.110000 ( 0.116090)
|
90
|
-
Addressable* 0.350000 0.000000 0.350000 ( 0.342613)
|
91
|
-
UriTemplate* 0.020000 0.000000 0.020000 ( 0.025884)
|
92
|
-
|
93
|
-
"%2F%20%2F%25%2F%20%3F%2B" => "{escaped_string}" vs "{escaped_string}"
|
94
|
-
user system total real
|
95
|
-
Addressable 0.410000 0.000000 0.410000 ( 0.404258)
|
96
|
-
UriTemplate 0.140000 0.000000 0.140000 ( 0.143897)
|
97
|
-
Addressable* 0.380000 0.000000 0.380000 ( 0.382331)
|
98
|
-
UriTemplate* 0.060000 0.000000 0.060000 ( 0.053483)
|
99
|
-
|
100
|
-
"" => "{missing}" vs "{missing}"
|
101
|
-
user system total real
|
102
|
-
Addressable 0.220000 0.000000 0.220000 ( 0.222086)
|
103
|
-
UriTemplate 0.100000 0.000000 0.100000 ( 0.104280)
|
104
|
-
Addressable* 0.230000 0.000000 0.230000 ( 0.238814)
|
105
|
-
UriTemplate* 0.020000 0.000000 0.020000 ( 0.017284)
|
106
|
-
|
107
|
-
"/a/b/c" => "{-prefix|/|segments}" vs "{/segments*}"
|
108
|
-
user system total real
|
109
|
-
Addressable 0.490000 0.000000 0.490000 ( 0.489809)
|
110
|
-
UriTemplate 0.230000 0.000000 0.230000 ( 0.232298)
|
111
|
-
Addressable* 0.490000 0.000000 0.490000 ( 0.489334)
|
112
|
-
UriTemplate* 0.120000 0.000000 0.120000 ( 0.119815)
|
113
|
-
|
114
|
-
"?one=1&two=2&three=3" => "?{-join|&|one,two,three}" vs "{?one,two,three}"
|
115
|
-
user system total real
|
116
|
-
Addressable 0.550000 0.000000 0.550000 ( 0.553224)
|
117
|
-
UriTemplate 0.210000 0.000000 0.210000 ( 0.208296)
|
118
|
-
Addressable* 0.550000 0.000000 0.550000 ( 0.551459)
|
119
|
-
UriTemplate* 0.060000 0.000000 0.060000 ( 0.059963)
|
120
|
-
|
121
|
-
"http://example.com/a/b/c/?one=1&two=2#foo" => "http://{host}/{-suffix|/|segments}?{-join|&|one,two,bogus}\#{fragment}" vs "http://{host}{/segments*}/{?one,two,bogus}{#fragment}"
|
122
|
-
user system total real
|
123
|
-
Addressable 0.980000 0.000000 0.980000 ( 0.980292)
|
124
|
-
UriTemplate 0.450000 0.000000 0.450000 ( 0.446143)
|
125
|
-
Addressable* 1.000000 0.000000 1.000000 ( 0.996237)
|
126
|
-
UriTemplate* 0.170000 0.000000 0.170000 ( 0.175239)
|
127
|
-
|
128
49
|
SUCCESS - Draft7 was faster in every test!
|
129
50
|
|
data/lib/uri_template.rb
CHANGED
@@ -250,8 +250,8 @@ module URITemplate
|
|
250
250
|
# This is detected by checking for "://".
|
251
251
|
#
|
252
252
|
def absolute?
|
253
|
+
return @absolute unless @absolute.nil?
|
253
254
|
read_chars = ""
|
254
|
-
|
255
255
|
tokens.each do |token|
|
256
256
|
if token.expression?
|
257
257
|
read_chars << "x"
|
@@ -259,13 +259,13 @@ module URITemplate
|
|
259
259
|
read_chars << token.string
|
260
260
|
end
|
261
261
|
if read_chars =~ /^[a-z]+:\/\//i
|
262
|
-
return true
|
262
|
+
return @absolute = true
|
263
263
|
elsif read_chars =~ /(?<!:|\/)\/(?!\/)/
|
264
|
-
return false
|
264
|
+
return @absolute = false
|
265
265
|
end
|
266
266
|
end
|
267
267
|
|
268
|
-
return false
|
268
|
+
return @absolute = false
|
269
269
|
end
|
270
270
|
|
271
271
|
# Opposite of {#absolute?}
|
data/lib/uri_template/colon.rb
CHANGED
@@ -28,7 +28,7 @@ class Colon
|
|
28
28
|
|
29
29
|
include URITemplate
|
30
30
|
|
31
|
-
VAR = /(?:\{:(
|
31
|
+
VAR = /(?:\{:([a-z]+)\}|:([a-z]+)(?![a-z]))/u
|
32
32
|
|
33
33
|
class Token
|
34
34
|
|
@@ -48,7 +48,7 @@ class Colon
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def to_r
|
51
|
-
return ['(
|
51
|
+
return ['([^/]*?)'].join
|
52
52
|
end
|
53
53
|
|
54
54
|
end
|
@@ -85,7 +85,7 @@ class Colon
|
|
85
85
|
elsif x.kind_of? self
|
86
86
|
return x
|
87
87
|
elsif x.kind_of? URITemplate::Draft7 and x.level == 1
|
88
|
-
return new( x.pattern.gsub(/\{(.*?)\}/){ "{:#{$1}}" } )
|
88
|
+
return new( x.pattern.gsub(/\{(.*?)\}/u){ "{:#{$1}}" } )
|
89
89
|
else
|
90
90
|
return nil
|
91
91
|
end
|
@@ -100,11 +100,11 @@ class Colon
|
|
100
100
|
# @param String uri
|
101
101
|
# @return nil,Hash
|
102
102
|
def extract(uri)
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
103
|
+
md = self.to_r.match(uri)
|
104
|
+
return nil unless md
|
105
|
+
return Hash[ *self.variables.each_with_index.map{|v,i|
|
106
|
+
[v, Utils.dpct(md[i+1])]
|
107
|
+
}.flatten(1) ]
|
108
108
|
end
|
109
109
|
|
110
110
|
def type
|
@@ -112,7 +112,7 @@ class Colon
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def to_r
|
115
|
-
@regexp ||= Regexp.new('\A' + tokens.map(&:to_r).join + '\z')
|
115
|
+
@regexp ||= Regexp.new('\A' + tokens.map(&:to_r).join + '\z', Utils::KCODE_UTF8)
|
116
116
|
end
|
117
117
|
|
118
118
|
def tokens
|
@@ -144,7 +144,8 @@ protected
|
|
144
144
|
if x.kind_of? String
|
145
145
|
Token::Static.new(x)
|
146
146
|
else
|
147
|
-
|
147
|
+
# TODO: when rubinius supports ambigious names this could be replaced with x['name'] *sigh*
|
148
|
+
Token::Variable.new(x[1] || x[2])
|
148
149
|
end
|
149
150
|
}.to_a
|
150
151
|
end
|
data/lib/uri_template/draft7.rb
CHANGED
@@ -34,26 +34,28 @@ class URITemplate::Draft7
|
|
34
34
|
Utils = URITemplate::Utils
|
35
35
|
|
36
36
|
# @private
|
37
|
-
|
37
|
+
# \/ - unicode ctrl-chars ( \p{Cc} doen't work with rbx
|
38
|
+
LITERAL = /^([^"'%<>\\^`{|}\s\p{Cntrl}]|%\h\h)+/u
|
38
39
|
|
39
40
|
# @private
|
40
41
|
CHARACTER_CLASSES = {
|
41
42
|
|
42
43
|
:unreserved => {
|
43
|
-
:unencoded => /([^A-Za-z0-9\-\._])
|
44
|
-
:class => '(
|
44
|
+
:unencoded => /([^A-Za-z0-9\-\._])/u,
|
45
|
+
:class => '(?:[A-Za-z0-9\-\._]|%\h\h)',
|
46
|
+
|
45
47
|
:class_name => 'c_u_',
|
46
48
|
:grabs_comma => false
|
47
49
|
},
|
48
50
|
:unreserved_reserved_pct => {
|
49
|
-
:unencoded => /([^A-Za-z0-9\-\._:\/?#\[\]@!\$%'\(\)*+,;=]|%(?!\h\h))
|
50
|
-
:class => '(
|
51
|
+
:unencoded => /([^A-Za-z0-9\-\._:\/?#\[\]@!\$%'\(\)*+,;=]|%(?!\h\h))/u,
|
52
|
+
:class => '(?:[A-Za-z0-9\-\._:\/?#\[\]@!\$%\'\(\)*+,;=]|%\h\h)',
|
51
53
|
:class_name => 'c_urp_',
|
52
54
|
:grabs_comma => true
|
53
55
|
},
|
54
56
|
|
55
57
|
:varname => {
|
56
|
-
:class => '(
|
58
|
+
:class => '(?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*?',
|
57
59
|
:class_name => 'c_vn_'
|
58
60
|
}
|
59
61
|
|
@@ -76,31 +78,21 @@ class URITemplate::Draft7
|
|
76
78
|
DEFAULT_PROCESSING = CONVERT_VALUES + CONVERT_RESULT
|
77
79
|
|
78
80
|
# @private
|
79
|
-
VAR = Regexp.compile(<<'__REGEXP__'.strip,
|
80
|
-
(
|
81
|
-
(?<varchar> [a-zA-Z_]|%[0-9a-fA-F]{2}){0}
|
82
|
-
(?<varname> \g<varchar>(?:\g<varchar>|\.)*){0}
|
83
|
-
(?<varspec> \g<varname>(?<explode>\*?)(?::(?<length>\d+))?){0}
|
84
|
-
\g<varspec>
|
81
|
+
VAR = Regexp.compile(<<'__REGEXP__'.strip, Utils::KCODE_UTF8)
|
82
|
+
((?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*)(\*)?(?::(\d+))?
|
85
83
|
__REGEXP__
|
86
84
|
|
87
85
|
# @private
|
88
|
-
EXPRESSION = Regexp.compile(<<'__REGEXP__'.strip,
|
89
|
-
(
|
90
|
-
(?<varchar> [a-zA-Z_]|%[0-9a-fA-F]{2}){0}
|
91
|
-
(?<varname> \g<varchar>(?:\g<varchar>|\.)*){0}
|
92
|
-
(?<varspec> \g<varname>\*?(?::\d+)?){0}
|
93
|
-
\{\g<operator>(?<vars>\g<varspec>(?:,\g<varspec>)*)\}
|
86
|
+
EXPRESSION = Regexp.compile(<<'__REGEXP__'.strip, Utils::KCODE_UTF8)
|
87
|
+
\{([+#\./;?&]?)((?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*\*?(?::\d+)?(?:,(?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*\*?(?::\d+)?)*)\}
|
94
88
|
__REGEXP__
|
95
89
|
|
96
90
|
# @private
|
97
|
-
URI = Regexp.compile(<<'__REGEXP__'.strip,
|
98
|
-
(
|
99
|
-
(?<varchar> [a-zA-Z_]|%[0-9a-fA-F]{2}){0}
|
100
|
-
(?<varname> \g<varchar>(?:\g<varchar>|\.)*){0}
|
101
|
-
(?<varspec> \g<varname>\*?(?::\d+)?){0}
|
102
|
-
^(([^"'%<>^`{|}\s\p{Cc}]|%\h\h)+|\{\g<operator>(?<vars>\g<varspec>(?:,\g<varspec>)*)\})*$
|
91
|
+
URI = Regexp.compile(<<'__REGEXP__'.strip, Utils::KCODE_UTF8)
|
92
|
+
\A(([^"'%<>^`{|}\s\p{Cntrl}]|%\h\h)+|\{([+#\./;?&]?)((?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*\*?(?::\d+)?(?:,(?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*\*?(?::\d+)?)*)\})*\z
|
103
93
|
__REGEXP__
|
94
|
+
|
95
|
+
SLASH = ?/
|
104
96
|
|
105
97
|
# @private
|
106
98
|
class Token
|
@@ -199,12 +191,12 @@ __REGEXP__
|
|
199
191
|
end
|
200
192
|
|
201
193
|
def to_s
|
202
|
-
'{' + self.class::OPERATOR +
|
194
|
+
return '{' + self.class::OPERATOR + @variable_specs.map{|name,expand,max_length| name + (expand ? '*': '') + (max_length > 0 ? (':' + max_length.to_s) : '') }.join(',') + '}'
|
203
195
|
end
|
204
196
|
|
205
197
|
#TODO: certain things after a slurpy variable will never get matched. therefore, it's pointless to add expressions for them
|
206
198
|
#TODO: variables, which appear twice could be compacted, don't they?
|
207
|
-
def to_r_source
|
199
|
+
def to_r_source
|
208
200
|
source = []
|
209
201
|
first = true
|
210
202
|
vs = @variable_specs.size - 1
|
@@ -212,21 +204,21 @@ __REGEXP__
|
|
212
204
|
if self.class::NAMED
|
213
205
|
@variable_specs.each{| var, expand , max_length |
|
214
206
|
last = (vs == i)
|
215
|
-
value = "(
|
207
|
+
value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{,'+max_length.to_s+'}':'*'}"
|
216
208
|
if expand
|
217
209
|
#if self.class::PAIR_IF_EMPTY
|
218
|
-
pair = "(
|
210
|
+
pair = "(?:#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self.class::PAIR_CONNECTOR)})?#{value}"
|
219
211
|
|
220
212
|
if first
|
221
|
-
source << "(
|
213
|
+
source << "((?:#{pair})(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*)"
|
222
214
|
else
|
223
|
-
source << "(
|
215
|
+
source << "((?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*)"
|
224
216
|
end
|
225
217
|
else
|
226
218
|
if self.class::PAIR_IF_EMPTY
|
227
|
-
pair = "#{Regexp.escape(var)}(
|
219
|
+
pair = "#{Regexp.escape(var)}(#{Regexp.escape(self.class::PAIR_CONNECTOR)}#{value})"
|
228
220
|
else
|
229
|
-
pair = "#{Regexp.escape(var)}(
|
221
|
+
pair = "#{Regexp.escape(var)}(#{Regexp.escape(self.class::PAIR_CONNECTOR)}#{value}|)"
|
230
222
|
end
|
231
223
|
|
232
224
|
if first
|
@@ -244,26 +236,26 @@ __REGEXP__
|
|
244
236
|
last = (vs == i)
|
245
237
|
if expand
|
246
238
|
# could be list or map, too
|
247
|
-
value = "
|
239
|
+
value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{,'+max_length.to_s+'}':'*'}"
|
248
240
|
|
249
|
-
pair = "(
|
241
|
+
pair = "(?:#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self.class::PAIR_CONNECTOR)})?#{value}"
|
250
242
|
|
251
243
|
value = "#{pair}(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*"
|
252
244
|
elsif last
|
253
245
|
# the last will slurp lists
|
254
246
|
if self.class::CHARACTER_CLASS[:grabs_comma]
|
255
|
-
value = "
|
247
|
+
value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
|
256
248
|
else
|
257
|
-
value = "(
|
249
|
+
value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
|
258
250
|
end
|
259
251
|
else
|
260
|
-
value = "
|
252
|
+
value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
|
261
253
|
end
|
262
254
|
if first
|
263
|
-
source << "(
|
255
|
+
source << "(#{value})"
|
264
256
|
first = false
|
265
257
|
else
|
266
|
-
source << "(?:#{Regexp.escape(self.class::SEPARATOR)}(
|
258
|
+
source << "(?:#{Regexp.escape(self.class::SEPARATOR)}(#{value}))?"
|
267
259
|
end
|
268
260
|
i = i+1
|
269
261
|
}
|
@@ -281,21 +273,24 @@ __REGEXP__
|
|
281
273
|
rest = matched
|
282
274
|
splitted = []
|
283
275
|
found_value = false
|
276
|
+
# 1 = name
|
277
|
+
# 2 = value
|
278
|
+
# 3 = rest
|
284
279
|
until rest.size == 0
|
285
280
|
match = ex.match(rest)
|
286
281
|
if match.nil?
|
287
282
|
raise "Couldn't match #{rest.inspect} againts the hash extractor. This is definitly a Bug. Please report this ASAP!"
|
288
283
|
end
|
289
284
|
if match.post_match.size == 0
|
290
|
-
rest = match[
|
285
|
+
rest = match[3].to_s
|
291
286
|
else
|
292
287
|
rest = ''
|
293
288
|
end
|
294
|
-
if match[
|
289
|
+
if match[1]
|
295
290
|
found_value = true
|
296
|
-
splitted << [ match[
|
291
|
+
splitted << [ match[1][0..-2], decode(match[2] + rest , false) ]
|
297
292
|
else
|
298
|
-
splitted << [ match[
|
293
|
+
splitted << [ match[2] + rest, nil ]
|
299
294
|
end
|
300
295
|
rest = match.post_match
|
301
296
|
end
|
@@ -318,10 +313,10 @@ __REGEXP__
|
|
318
313
|
def hash_extractor(max_length)
|
319
314
|
@hash_extractors ||= {}
|
320
315
|
@hash_extractors[max_length] ||= begin
|
321
|
-
value = "
|
322
|
-
pair = "(
|
323
|
-
|
324
|
-
Regexp.new(
|
316
|
+
value = "#{self::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
|
317
|
+
pair = "(#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self::PAIR_CONNECTOR)})?(#{value})"
|
318
|
+
source = "\\A#{Regexp.escape(self::SEPARATOR)}?" + pair + "(\\z|#{Regexp.escape(self::SEPARATOR)}(?!#{Regexp.escape(self::SEPARATOR)}))"
|
319
|
+
Regexp.new( source , Utils::KCODE_UTF8)
|
325
320
|
end
|
326
321
|
end
|
327
322
|
|
@@ -366,7 +361,7 @@ __REGEXP__
|
|
366
361
|
|
367
362
|
def cut(str,chars)
|
368
363
|
if chars > 0
|
369
|
-
md = Regexp.compile("
|
364
|
+
md = Regexp.compile("\\A#{self.class::CHARACTER_CLASS[:class]}{,#{chars.to_s}}", Utils::KCODE_UTF8).match(str)
|
370
365
|
#TODO: handle invalid matches
|
371
366
|
return md[0]
|
372
367
|
else
|
@@ -518,9 +513,12 @@ __REGEXP__
|
|
518
513
|
until scanner.eos?
|
519
514
|
expression = scanner.scan(EXPRESSION)
|
520
515
|
if expression
|
521
|
-
vars = scanner[
|
516
|
+
vars = scanner[2].split(',').map{|name|
|
522
517
|
match = VAR.match(name)
|
523
|
-
|
518
|
+
# 1 = varname
|
519
|
+
# 2 = explode
|
520
|
+
# 3 = length
|
521
|
+
[ match[1], match[2] == '*', match[3].to_i ]
|
524
522
|
}
|
525
523
|
yield OPERATORS[scanner[1]].new(vars)
|
526
524
|
else
|
@@ -643,13 +641,14 @@ __REGEXP__
|
|
643
641
|
#
|
644
642
|
# @return Regexp
|
645
643
|
def to_r
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
644
|
+
@regexp ||= begin
|
645
|
+
#classes = CHARACTER_CLASSES.map{|_,v| v[:class]+"{0}\n" }
|
646
|
+
bc = 0
|
647
|
+
source = tokens.map(&:to_r_source)
|
648
|
+
source.unshift('\A')
|
649
|
+
source.push('\z')
|
650
|
+
Regexp.new( source.join, Utils::KCODE_UTF8)
|
651
|
+
end
|
653
652
|
end
|
654
653
|
|
655
654
|
|
@@ -693,7 +692,7 @@ __REGEXP__
|
|
693
692
|
if uri_or_match.kind_of? String
|
694
693
|
m = self.to_r.match(uri_or_match)
|
695
694
|
elsif uri_or_match.kind_of?(MatchData)
|
696
|
-
if uri_or_match.regexp != self.to_r
|
695
|
+
if uri_or_match.respond_to?(:regexp) and uri_or_match.regexp != self.to_r
|
697
696
|
raise ArgumentError, "Trying to extract variables from MatchData which was not generated by this template."
|
698
697
|
end
|
699
698
|
m = uri_or_match
|
@@ -705,15 +704,7 @@ __REGEXP__
|
|
705
704
|
if m.nil?
|
706
705
|
return nil
|
707
706
|
else
|
708
|
-
result = extract_matchdata(m)
|
709
|
-
if post_processing.include? :convert_values
|
710
|
-
result.map!{|k,v| [k, Utils.pair_array_to_hash(v)] }
|
711
|
-
end
|
712
|
-
|
713
|
-
if post_processing.include? :convert_result
|
714
|
-
result = Utils.pair_array_to_hash(result)
|
715
|
-
end
|
716
|
-
|
707
|
+
result = extract_matchdata(m, post_processing)
|
717
708
|
if block_given?
|
718
709
|
return yield result
|
719
710
|
end
|
@@ -817,10 +808,10 @@ __REGEXP__
|
|
817
808
|
# Merge!
|
818
809
|
# Analyze the last token of this an the first token of the next and try to merge them
|
819
810
|
if self.tokens.last.kind_of?(Literal)
|
820
|
-
if self.tokens.last.string[-1] ==
|
811
|
+
if self.tokens.last.string[-1] == SLASH # the last token ends with an /
|
821
812
|
if other.tokens.first.kind_of? Literal
|
822
813
|
# both seems to be paths, merge them!
|
823
|
-
if other.tokens.first.string[0] ==
|
814
|
+
if other.tokens.first.string[0] == SLASH
|
824
815
|
# strip one '/'
|
825
816
|
return self.class.new( self.tokens[0..-2] + [ Literal.new(self.tokens.last.string + other.tokens.first.string[1..-1]) ] + other.tokens[1..-1] )
|
826
817
|
else
|
@@ -834,7 +825,7 @@ __REGEXP__
|
|
834
825
|
end
|
835
826
|
elsif other.tokens.first.kind_of? Literal
|
836
827
|
# okay, this template does not end with /, but the next starts with a literal => merge them!
|
837
|
-
if other.tokens.first.string[0] ==
|
828
|
+
if other.tokens.first.string[0] == SLASH
|
838
829
|
return self.class.new( self.tokens[0..-2] + [Literal.new(self.tokens.last.string + other.tokens.first.string)] + other.tokens[1..-1] )
|
839
830
|
else
|
840
831
|
return self.class.new( self.tokens[0..-2] + [Literal.new(self.tokens.last.string + '/' + other.tokens.first.string)] + other.tokens[1..-1] )
|
@@ -843,7 +834,7 @@ __REGEXP__
|
|
843
834
|
end
|
844
835
|
|
845
836
|
if other.tokens.first.kind_of?(Literal)
|
846
|
-
if other.tokens.first.string[0] ==
|
837
|
+
if other.tokens.first.string[0] == SLASH
|
847
838
|
return self.class.new( self.tokens + other.tokens )
|
848
839
|
else
|
849
840
|
return self.class.new( self.tokens + [Literal.new('/' + other.tokens.first.string)]+ other.tokens[1..-1] )
|
@@ -866,19 +857,40 @@ protected
|
|
866
857
|
Tokenizer.new(pattern).to_a
|
867
858
|
end
|
868
859
|
|
860
|
+
def arity
|
861
|
+
@arity ||= tokens.inject(0){|a,t| a + t.arity }
|
862
|
+
end
|
863
|
+
|
869
864
|
# @private
|
870
|
-
def extract_matchdata(matchdata)
|
871
|
-
bc =
|
865
|
+
def extract_matchdata(matchdata, post_processing)
|
866
|
+
bc = 1
|
872
867
|
vars = []
|
873
868
|
tokens.each{|part|
|
869
|
+
next if part.literal?
|
874
870
|
i = 0
|
875
|
-
|
876
|
-
|
871
|
+
pa = part.arity
|
872
|
+
while i < pa
|
873
|
+
vars << part.extract(i, matchdata[bc])
|
877
874
|
bc += 1
|
878
875
|
i += 1
|
879
876
|
end
|
880
877
|
}
|
881
|
-
|
878
|
+
if post_processing.include? :convert_result
|
879
|
+
if post_processing.include? :convert_values
|
880
|
+
vars.flatten!(1)
|
881
|
+
return Hash[*vars.map!{|k,v| [k,Utils.pair_array_to_hash(v)] }.flatten(1) ]
|
882
|
+
else
|
883
|
+
vars.flatten!(2)
|
884
|
+
return Hash[*vars]
|
885
|
+
end
|
886
|
+
else
|
887
|
+
if post_processing.include? :convert_value
|
888
|
+
vars.flatten!(1)
|
889
|
+
return vars.collect{|k,v| [k,Utils.pair_array_to_hash(v)] }
|
890
|
+
else
|
891
|
+
return vars.flatten(1)
|
892
|
+
end
|
893
|
+
end
|
882
894
|
end
|
883
895
|
|
884
896
|
end
|
data/lib/uri_template/utils.rb
CHANGED
@@ -67,12 +67,52 @@ module URITemplate
|
|
67
67
|
# A collection of some utility methods
|
68
68
|
module Utils
|
69
69
|
|
70
|
+
KCODE_UTF8 = (Regexp::KCODE_UTF8 rescue 0)
|
71
|
+
|
70
72
|
# @private
|
71
73
|
PCT = /%(\h\h)/.freeze
|
72
74
|
|
73
75
|
# A regexp which match all non-simple characters.
|
74
76
|
NOT_SIMPLE_CHARS = /([^A-Za-z0-9\-\._])/.freeze
|
75
77
|
|
78
|
+
ASCII = 'ASCII'.freeze
|
79
|
+
UTF8 = 'UTF-8'.freeze
|
80
|
+
|
81
|
+
def to_ascii_force_encoding(str)
|
82
|
+
str.force_encoding(ASCII)
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_utf8_force_encoding(str)
|
86
|
+
str.force_encoding(UTF8)
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_ascii_encode(str)
|
90
|
+
str.encode(ASCII)
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_utf8_encode(str)
|
94
|
+
str.encode(UTF8)
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_ascii_fallback(str)
|
98
|
+
str
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_utf8_fallback(str)
|
102
|
+
str
|
103
|
+
end
|
104
|
+
|
105
|
+
if "".respond_to?(:force_encoding)
|
106
|
+
alias_method :to_ascii, :to_ascii_force_encoding
|
107
|
+
alias_method :to_utf8, :to_utf8_force_encoding
|
108
|
+
elsif "".respond_to?(:encode)
|
109
|
+
alias_method :to_ascii, :to_ascii_encode
|
110
|
+
alias_method :to_utf8, :to_utf8_encode
|
111
|
+
else
|
112
|
+
alias_method :to_ascii, :to_ascii_fallback
|
113
|
+
alias_method :to_utf8, :to_utf8_fallback
|
114
|
+
end
|
115
|
+
|
76
116
|
# Encodes the given string into a pct-encoded string.
|
77
117
|
# @param s The string to be encoded.
|
78
118
|
# @param m A regexp matching all characters, which need to be encoded.
|
@@ -81,10 +121,11 @@ module URITemplate
|
|
81
121
|
# URITemplate::Utils.pct("abc") #=> "abc"
|
82
122
|
# URITemplate::Utils.pct("%") #=> "%25"
|
83
123
|
#
|
124
|
+
#TODO: is encoding as utf8/ascii really needed?
|
84
125
|
def pct(s, m=NOT_SIMPLE_CHARS)
|
85
|
-
s.to_s.
|
126
|
+
to_ascii( s.to_s.gsub(m){
|
86
127
|
'%'+$1.unpack('H2'*$1.bytesize).join('%').upcase
|
87
|
-
}
|
128
|
+
} )
|
88
129
|
end
|
89
130
|
|
90
131
|
# Decodes the given pct-encoded string into a utf-8 string.
|
@@ -95,9 +136,9 @@ module URITemplate
|
|
95
136
|
# URITemplate::Utils.dpct("%25") #=> "%"
|
96
137
|
#
|
97
138
|
def dpct(s)
|
98
|
-
s.to_s.
|
139
|
+
to_utf8(s.to_s.gsub(PCT){
|
99
140
|
$1.to_i(16).chr
|
100
|
-
}
|
141
|
+
})
|
101
142
|
end
|
102
143
|
|
103
144
|
# Converts an object to a param value.
|
@@ -139,20 +180,26 @@ module URITemplate
|
|
139
180
|
# Turns the given value into a hash if it is an array of pairs.
|
140
181
|
# Otherwise it returns the value.
|
141
182
|
# You can test whether a value will be converted with {#pair_array?}.
|
183
|
+
#
|
142
184
|
# @example
|
143
185
|
# URITemplate::Utils.pair_array_to_hash( 'x' ) #=> 'x'
|
144
186
|
# URITemplate::Utils.pair_array_to_hash( [ ['a',1],['b',2],['c',3] ] ) #=> {'a'=>1,'b'=>2,'c'=>3}
|
145
187
|
# URITemplate::Utils.pair_array_to_hash( [ ['a',1],['a',2],['a',3] ] ) #=> {'a'=>3}
|
146
|
-
|
147
|
-
|
148
|
-
|
188
|
+
#
|
189
|
+
# @example Carful vs. Ignorant
|
190
|
+
# URITemplate::Utils.pair_array_to_hash( [ ['a',1],'foo','bar'], false ) #=> {'a'=>1,'foo'=>'bar'}
|
191
|
+
# URITemplate::Utils.pair_array_to_hash( [ ['a',1],'foo','bar'], true ) #=> [ ['a',1],'foo','bar']
|
192
|
+
#
|
193
|
+
# @param x the value to convert
|
194
|
+
# @param careful [true,false] wheter to check every array item. Use this when you expect array with subarrays which are not pairs. Setting this to false however improves runtime by ~30% even with comparetivly short arrays.
|
195
|
+
def pair_array_to_hash(x, careful = false )
|
196
|
+
if careful ? pair_array?(x) : (x.kind_of?(Array) and x.first.kind_of?(Array))
|
197
|
+
return Hash[ *x.flatten(1) ]
|
149
198
|
else
|
150
|
-
return
|
199
|
+
return x
|
151
200
|
end
|
152
201
|
end
|
153
202
|
|
154
|
-
|
155
|
-
|
156
203
|
extend self
|
157
204
|
|
158
205
|
end
|
data/uri_template.gemspec
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'uri_template'
|
3
|
-
s.version = '0.1.
|
4
|
-
s.date = '2011-11-
|
3
|
+
s.version = '0.1.4'
|
4
|
+
s.date = '2011-11-19'
|
5
5
|
s.authors = ["HannesG"]
|
6
6
|
s.email = %q{hannes.georg@googlemail.com}
|
7
7
|
s.summary = 'A templating system for URIs.'
|
8
8
|
s.homepage = 'http://github.com/hannesg/uri_template'
|
9
|
-
s.description = 'A templating system for URIs, which implements http://tools.ietf.org/html/draft-gregorio-uritemplate-07 . An implementation of an older version of that spec is known as addressable. This gem however is intended to be extended when newer specs evolve. For now only draft 7 is supported. Downside:
|
9
|
+
s.description = 'A templating system for URIs, which implements http://tools.ietf.org/html/draft-gregorio-uritemplate-07 . An implementation of an older version of that spec is known as addressable. This gem however is intended to be extended when newer specs evolve. For now only draft 7 is supported. Downside: not for 1.8.7.'
|
10
10
|
|
11
11
|
s.require_paths = ['lib']
|
12
12
|
|
@@ -14,4 +14,6 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.add_development_dependency 'rspec'
|
16
16
|
s.add_development_dependency 'yard'
|
17
|
+
s.add_development_dependency 'rake'
|
18
|
+
s.add_development_dependency 'bundler'
|
17
19
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uri_template
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-11-
|
12
|
+
date: 2011-11-19 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &16477840 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *16477840
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: yard
|
27
|
-
requirement: &
|
27
|
+
requirement: &16477400 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,11 +32,33 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *16477400
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rake
|
38
|
+
requirement: &16476980 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *16476980
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bundler
|
49
|
+
requirement: &16476560 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *16476560
|
36
58
|
description: ! 'A templating system for URIs, which implements http://tools.ietf.org/html/draft-gregorio-uritemplate-07
|
37
59
|
. An implementation of an older version of that spec is known as addressable. This
|
38
60
|
gem however is intended to be extended when newer specs evolve. For now only draft
|
39
|
-
7 is supported. Downside:
|
61
|
+
7 is supported. Downside: not for 1.8.7.'
|
40
62
|
email: hannes.georg@googlemail.com
|
41
63
|
executables: []
|
42
64
|
extensions: []
|