uri_template 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/README +13 -0
- data/lib/uri_template.rb +3 -3
- data/lib/uri_template/colon.rb +2 -2
- data/lib/uri_template/draft7.rb +33 -17
- data/lib/uri_template/utils.rb +134 -59
- data/uri_template.gemspec +2 -2
- metadata +10 -10
data/CHANGELOG
CHANGED
data/README
CHANGED
@@ -6,6 +6,19 @@ It's currently planed to add newer versions of that spec when they emerge. There
|
|
6
6
|
|
7
7
|
Some implementations might be able to extract variables, too ( Draft 7 is! ).
|
8
8
|
|
9
|
+
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:
|
10
|
+
|
11
|
+
````ruby
|
12
|
+
# escape_utils has to be loaded when uri_templates is loaded
|
13
|
+
gem 'escape_utils'
|
14
|
+
require 'escape_utils'
|
15
|
+
|
16
|
+
gem 'uri_template'
|
17
|
+
require 'uri_template'
|
18
|
+
|
19
|
+
UriTemplate::Utils.using_escape_utils? #=> true
|
20
|
+
|
21
|
+
|
9
22
|
== Examples
|
10
23
|
|
11
24
|
require 'uri_template'
|
data/lib/uri_template.rb
CHANGED
@@ -149,7 +149,7 @@ module URITemplate
|
|
149
149
|
end
|
150
150
|
|
151
151
|
# Tries to coerce two URITemplates into a common representation.
|
152
|
-
# Returns an array with two {URITemplate}s and two booleans indicating which of the two were converted or raises an
|
152
|
+
# Returns an array with two {URITemplate}s and two booleans indicating which of the two were converted or raises an ArgumentError.
|
153
153
|
#
|
154
154
|
# @example
|
155
155
|
# URITemplate.coerce( URITemplate.new(:draft7,'{x}'), '{y}' ) #=> [URITemplate.new(:draft7,'{x}'), URITemplate.new(:draft7,'{y}'), false, true]
|
@@ -217,7 +217,7 @@ module URITemplate
|
|
217
217
|
end
|
218
218
|
|
219
219
|
# @abstract
|
220
|
-
# Returns the tokens of this templates. Tokens should include either {
|
220
|
+
# Returns the tokens of this templates. Tokens should include either {Literal} or {Expression}.
|
221
221
|
def tokens
|
222
222
|
raise "Please implement #tokens on #{self.class.inspect}."
|
223
223
|
end
|
@@ -234,7 +234,7 @@ module URITemplate
|
|
234
234
|
|
235
235
|
# Returns the number of static characters in this template.
|
236
236
|
# This method is useful for routing, since it's often pointful to use the url with fewer variable characters.
|
237
|
-
# For example 'static' and 'sta
|
237
|
+
# For example 'static' and 'sta\\{var\\}' both match 'static', but in most cases 'static' should be prefered over 'sta\\{var\\}' since it's more specific.
|
238
238
|
#
|
239
239
|
# @example
|
240
240
|
# URITemplate.new('/xy/').static_characters #=> 4
|
data/lib/uri_template/colon.rb
CHANGED
@@ -44,7 +44,7 @@ class Colon
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def expand(vars)
|
47
|
-
return Utils.
|
47
|
+
return Utils.escape_url(Utils.object_to_param(vars[@name]))
|
48
48
|
end
|
49
49
|
|
50
50
|
def to_r
|
@@ -103,7 +103,7 @@ class Colon
|
|
103
103
|
md = self.to_r.match(uri)
|
104
104
|
return nil unless md
|
105
105
|
return Hash[ *self.variables.each_with_index.map{|v,i|
|
106
|
-
[v, Utils.
|
106
|
+
[v, Utils.unescape_url(md[i+1])]
|
107
107
|
}.flatten(1) ]
|
108
108
|
end
|
109
109
|
|
data/lib/uri_template/draft7.rb
CHANGED
@@ -178,7 +178,7 @@ __REGEXP__
|
|
178
178
|
if self.class::NAMED
|
179
179
|
result.push( pair(var, vars[var], max_length) )
|
180
180
|
else
|
181
|
-
result.push( cut(
|
181
|
+
result.push( cut( escape(vars[var]), max_length ) )
|
182
182
|
end
|
183
183
|
end
|
184
184
|
end
|
@@ -203,7 +203,6 @@ __REGEXP__
|
|
203
203
|
i = 0
|
204
204
|
if self.class::NAMED
|
205
205
|
@variable_specs.each{| var, expand , max_length |
|
206
|
-
last = (vs == i)
|
207
206
|
value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{,'+max_length.to_s+'}':'*'}"
|
208
207
|
if expand
|
209
208
|
#if self.class::PAIR_IF_EMPTY
|
@@ -269,6 +268,7 @@ __REGEXP__
|
|
269
268
|
return [[ name , matched ]]
|
270
269
|
end
|
271
270
|
if expand
|
271
|
+
#TODO: do we really need this? - this could be stolen from rack
|
272
272
|
ex = self.class.hash_extractor(max_length)
|
273
273
|
rest = matched
|
274
274
|
splitted = []
|
@@ -324,8 +324,12 @@ __REGEXP__
|
|
324
324
|
|
325
325
|
extend ClassMethods
|
326
326
|
|
327
|
-
def
|
328
|
-
Utils.
|
327
|
+
def escape(x)
|
328
|
+
Utils.escape_url(Utils.object_to_param(x))
|
329
|
+
end
|
330
|
+
|
331
|
+
def unescape(x)
|
332
|
+
Utils.unescape_url(x)
|
329
333
|
end
|
330
334
|
|
331
335
|
SPLITTER = /^(?:,(,*)|([^,]+))/
|
@@ -345,7 +349,7 @@ __REGEXP__
|
|
345
349
|
if m[1] and m[1].size > 0
|
346
350
|
r << m[1]
|
347
351
|
elsif m[2]
|
348
|
-
r <<
|
352
|
+
r << unescape(m[2])
|
349
353
|
end
|
350
354
|
v = m.post_match
|
351
355
|
end
|
@@ -355,7 +359,7 @@ __REGEXP__
|
|
355
359
|
else r
|
356
360
|
end
|
357
361
|
else
|
358
|
-
|
362
|
+
unescape(x)
|
359
363
|
end
|
360
364
|
end
|
361
365
|
|
@@ -370,8 +374,8 @@ __REGEXP__
|
|
370
374
|
end
|
371
375
|
|
372
376
|
def pair(key, value, max_length = 0)
|
373
|
-
ek =
|
374
|
-
ev =
|
377
|
+
ek = escape(key)
|
378
|
+
ev = escape(value)
|
375
379
|
if !self.class::PAIR_IF_EMPTY and ev.size == 0
|
376
380
|
return ek
|
377
381
|
else
|
@@ -385,17 +389,17 @@ __REGEXP__
|
|
385
389
|
elsif hsh.none?
|
386
390
|
[]
|
387
391
|
else
|
388
|
-
[ (self.class::NAMED ?
|
392
|
+
[ (self.class::NAMED ? escape(name)+self.class::PAIR_CONNECTOR : '' ) + hsh.map{|key,value| escape(key)+self.class::LIST_CONNECTOR+escape(value) }.join(self.class::LIST_CONNECTOR) ]
|
389
393
|
end
|
390
394
|
end
|
391
395
|
|
392
396
|
def transform_array(name, ary, expand , max_length)
|
393
397
|
if expand
|
394
|
-
ary.map{|value|
|
398
|
+
ary.map{|value| escape(value) }
|
395
399
|
elsif ary.none?
|
396
400
|
[]
|
397
401
|
else
|
398
|
-
[ (self.class::NAMED ?
|
402
|
+
[ (self.class::NAMED ? escape(name)+self.class::PAIR_CONNECTOR : '' ) + ary.map{|value| escape(value) }.join(self.class::LIST_CONNECTOR) ]
|
399
403
|
end
|
400
404
|
end
|
401
405
|
|
@@ -404,6 +408,14 @@ __REGEXP__
|
|
404
408
|
CHARACTER_CLASS = CHARACTER_CLASSES[:unreserved_reserved_pct]
|
405
409
|
OPERATOR = '+'.freeze
|
406
410
|
BASE_LEVEL = 2
|
411
|
+
|
412
|
+
def escape(x)
|
413
|
+
Utils.escape_uri(Utils.object_to_param(x))
|
414
|
+
end
|
415
|
+
|
416
|
+
def unescape(x)
|
417
|
+
Utils.unescape_uri(x)
|
418
|
+
end
|
407
419
|
|
408
420
|
end
|
409
421
|
|
@@ -413,6 +425,14 @@ __REGEXP__
|
|
413
425
|
PREFIX = '#'.freeze
|
414
426
|
OPERATOR = '#'.freeze
|
415
427
|
BASE_LEVEL = 2
|
428
|
+
|
429
|
+
def escape(x)
|
430
|
+
Utils.escape_uri(Utils.object_to_param(x))
|
431
|
+
end
|
432
|
+
|
433
|
+
def unescape(x)
|
434
|
+
Utils.unescape_uri(x)
|
435
|
+
end
|
416
436
|
|
417
437
|
end
|
418
438
|
|
@@ -593,8 +613,6 @@ __REGEXP__
|
|
593
613
|
|
594
614
|
extend ClassMethods
|
595
615
|
|
596
|
-
attr_reader :pattern
|
597
|
-
|
598
616
|
attr_reader :options
|
599
617
|
|
600
618
|
# @param String,Array either a pattern as String or an Array of tokens
|
@@ -642,8 +660,6 @@ __REGEXP__
|
|
642
660
|
# @return Regexp
|
643
661
|
def to_r
|
644
662
|
@regexp ||= begin
|
645
|
-
#classes = CHARACTER_CLASSES.map{|_,v| v[:class]+"{0}\n" }
|
646
|
-
bc = 0
|
647
663
|
source = tokens.map(&:to_r_source)
|
648
664
|
source.unshift('\A')
|
649
665
|
source.push('\z')
|
@@ -729,7 +745,7 @@ __REGEXP__
|
|
729
745
|
|
730
746
|
# Compares two template patterns.
|
731
747
|
def ==(o)
|
732
|
-
this, other, this_converted,
|
748
|
+
this, other, this_converted, _ = URITemplate.coerce( self, o )
|
733
749
|
if this_converted
|
734
750
|
return this == other
|
735
751
|
end
|
@@ -793,7 +809,7 @@ __REGEXP__
|
|
793
809
|
# (tpl / 'a' / 'b' ).pattern #=> '/xy/a/b'
|
794
810
|
#
|
795
811
|
def /(o)
|
796
|
-
this, other, this_converted,
|
812
|
+
this, other, this_converted, _ = URITemplate.coerce( self, o )
|
797
813
|
if this_converted
|
798
814
|
return this / other
|
799
815
|
end
|
data/lib/uri_template/utils.rb
CHANGED
@@ -64,81 +64,156 @@ module URITemplate
|
|
64
64
|
|
65
65
|
end
|
66
66
|
|
67
|
-
# A collection of some utility methods
|
67
|
+
# A collection of some utility methods.
|
68
|
+
# The most methods are used to parse or generate uri-parameters.
|
69
|
+
# I will use the escape_utils library if available, but runs happily without.
|
70
|
+
#
|
68
71
|
module Utils
|
69
72
|
|
70
73
|
KCODE_UTF8 = (Regexp::KCODE_UTF8 rescue 0)
|
71
|
-
|
72
|
-
# @private
|
73
|
-
PCT = /%(\h\h)/.freeze
|
74
74
|
|
75
|
-
#
|
76
|
-
|
75
|
+
# Bundles some string encoding methods.
|
76
|
+
module StringEncoding
|
77
|
+
|
78
|
+
# @method to_ascii(string)
|
79
|
+
# converts a string to ascii
|
80
|
+
#
|
81
|
+
# This method checks which encoding method is available.
|
82
|
+
# @param String
|
83
|
+
# @return String
|
84
|
+
# @visibility public
|
85
|
+
def to_ascii_encode(str)
|
86
|
+
str.encode(Encoding::ASCII)
|
87
|
+
end
|
88
|
+
|
89
|
+
# @method to_utf8(string)
|
90
|
+
# converts a string to utf8
|
91
|
+
#
|
92
|
+
# This method checks which encoding method is available.
|
93
|
+
# @param String
|
94
|
+
# @return String
|
95
|
+
# @visibility public
|
96
|
+
def to_utf8_encode(str)
|
97
|
+
str.encode(Encoding::UTF_8)
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_ascii_fallback(str)
|
101
|
+
str
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_utf8_fallback(str)
|
105
|
+
str
|
106
|
+
end
|
107
|
+
|
108
|
+
if "".respond_to?(:encode)
|
109
|
+
# @private
|
110
|
+
alias_method :to_ascii, :to_ascii_encode
|
111
|
+
# @private
|
112
|
+
alias_method :to_utf8, :to_utf8_encode
|
113
|
+
else
|
114
|
+
# @private
|
115
|
+
alias_method :to_ascii, :to_ascii_fallback
|
116
|
+
# @private
|
117
|
+
alias_method :to_utf8, :to_utf8_fallback
|
118
|
+
end
|
119
|
+
|
120
|
+
public :to_ascii
|
121
|
+
public :to_utf8
|
122
|
+
|
123
|
+
end
|
77
124
|
|
78
|
-
|
79
|
-
UTF8 = 'UTF-8'.freeze
|
125
|
+
module Escaping
|
80
126
|
|
81
|
-
|
82
|
-
|
83
|
-
|
127
|
+
# A pure escaping module, which implements escaping methods in pure ruby.
|
128
|
+
# The performance is acceptable, but could be better with escape_utils.
|
129
|
+
module Pure
|
84
130
|
|
85
|
-
|
86
|
-
str.force_encoding(UTF8)
|
87
|
-
end
|
131
|
+
include StringEncoding
|
88
132
|
|
89
|
-
|
90
|
-
|
91
|
-
|
133
|
+
# @private
|
134
|
+
URL_ESCAPED = /([^A-Za-z0-9\-\._])/.freeze
|
135
|
+
|
136
|
+
# @private
|
137
|
+
URI_ESCAPED = /([^A-Za-z0-9!$&'()*+,.\/:;=?@\[\]_~])/.freeze
|
138
|
+
|
139
|
+
# @private
|
140
|
+
PCT = /%(\h\h)/.freeze
|
141
|
+
|
142
|
+
def escape_url(s)
|
143
|
+
to_utf8(s.to_s).gsub(URL_ESCAPED){
|
144
|
+
'%'+$1.unpack('H2'*$1.bytesize).join('%').upcase
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
def escape_uri(s)
|
149
|
+
to_utf8(s.to_s).gsub(URI_ESCAPED){
|
150
|
+
'%'+$1.unpack('H2'*$1.bytesize).join('%').upcase
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
def unescape_url(s)
|
155
|
+
to_utf8( s.to_s.gsub('+',' ').gsub(PCT){
|
156
|
+
$1.to_i(16).chr
|
157
|
+
} )
|
158
|
+
end
|
159
|
+
|
160
|
+
def unescape_uri(s)
|
161
|
+
to_utf8( s.to_s.gsub(PCT){
|
162
|
+
$1.to_i(16).chr
|
163
|
+
})
|
164
|
+
end
|
165
|
+
|
166
|
+
def using_escape_utils?
|
167
|
+
false
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
if defined? EscapeUtils
|
173
|
+
|
174
|
+
# A escaping module, which is backed by escape_utils.
|
175
|
+
# The performance is good, espacially for strings with many escaped characters.
|
176
|
+
module EscapeUtils
|
177
|
+
|
178
|
+
include StringEncoding
|
179
|
+
|
180
|
+
include ::EscapeUtils
|
92
181
|
|
93
|
-
|
94
|
-
|
95
|
-
|
182
|
+
def using_escape_utils?
|
183
|
+
true
|
184
|
+
end
|
185
|
+
|
186
|
+
def escape_url(s)
|
187
|
+
super(to_utf8(s)).gsub('+','%20')
|
188
|
+
end
|
189
|
+
|
190
|
+
def escape_uri(s)
|
191
|
+
super(to_utf8(s))
|
192
|
+
end
|
193
|
+
|
194
|
+
def unescape_url(s)
|
195
|
+
super(to_ascii(s))
|
196
|
+
end
|
197
|
+
|
198
|
+
def unescape_uri(s)
|
199
|
+
super(to_ascii(s))
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
96
203
|
|
97
|
-
def to_ascii_fallback(str)
|
98
|
-
str
|
99
204
|
end
|
100
205
|
|
101
|
-
def to_utf8_fallback(str)
|
102
|
-
str
|
103
|
-
end
|
104
206
|
|
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
207
|
end
|
115
208
|
|
116
|
-
|
117
|
-
# @param s The string to be encoded.
|
118
|
-
# @param m A regexp matching all characters, which need to be encoded.
|
119
|
-
#
|
120
|
-
# @example
|
121
|
-
# URITemplate::Utils.pct("abc") #=> "abc"
|
122
|
-
# URITemplate::Utils.pct("%") #=> "%25"
|
123
|
-
#
|
124
|
-
#TODO: is encoding as utf8/ascii really needed?
|
125
|
-
def pct(s, m=NOT_SIMPLE_CHARS)
|
126
|
-
to_ascii( s.to_s.gsub(m){
|
127
|
-
'%'+$1.unpack('H2'*$1.bytesize).join('%').upcase
|
128
|
-
} )
|
129
|
-
end
|
209
|
+
include StringEncoding
|
130
210
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
#
|
138
|
-
def dpct(s)
|
139
|
-
to_utf8(s.to_s.gsub(PCT){
|
140
|
-
$1.to_i(16).chr
|
141
|
-
})
|
211
|
+
if Escaping.const_defined? :EscapeUtils
|
212
|
+
include Escaping::EscapeUtils
|
213
|
+
puts "Using escape_utils." if $VERBOSE
|
214
|
+
else
|
215
|
+
include Escaping::Pure
|
216
|
+
puts "Not using escape_utils." if $VERBOSE
|
142
217
|
end
|
143
218
|
|
144
219
|
# Converts an object to a param value.
|
data/uri_template.gemspec
CHANGED
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.
|
4
|
+
version: 0.2.0
|
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-
|
12
|
+
date: 2011-12-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &17186480 !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: *17186480
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: yard
|
27
|
-
requirement: &
|
27
|
+
requirement: &17185740 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *17185740
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
38
|
-
requirement: &
|
38
|
+
requirement: &17184760 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *17184760
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: bundler
|
49
|
-
requirement: &
|
49
|
+
requirement: &17195720 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *17195720
|
58
58
|
description: ! 'A templating system for URIs, which implements http://tools.ietf.org/html/draft-gregorio-uritemplate-07
|
59
59
|
. An implementation of an older version of that spec is known as addressable. This
|
60
60
|
gem however is intended to be extended when newer specs evolve. For now only draft
|