uri_template 0.1.4 → 0.2.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 +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
|