uri_template 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/lib/uri_template/colon.rb +153 -0
- data/lib/uri_template/draft7.rb +48 -74
- data/lib/uri_template/utils.rb +35 -0
- data/lib/uri_template.rb +95 -3
- data/uri_template.gemspec +2 -2
- metadata +7 -6
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# 0.1.2 - 10.11.2011
|
2
|
+
+ added a new template-type: Colon
|
3
|
+
this should allow (some day) to rails-like routing tables
|
4
|
+
+ made the tokens-method mandatory and added two interfaces for tokens.
|
5
|
+
this allows cross-type features like variable anaylisis
|
6
|
+
|
1
7
|
# 0.1.1 - 4.11.2011
|
2
8
|
+ added a bunch of useful helper methods
|
3
9
|
|
@@ -0,0 +1,153 @@
|
|
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 by Hannes Georg
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'forwardable'
|
19
|
+
|
20
|
+
require 'uri_template'
|
21
|
+
require 'uri_template/utils'
|
22
|
+
|
23
|
+
# A colon based template denotes variables with a colon.
|
24
|
+
# This template type is realy basic but having just on template type was a bit weird.
|
25
|
+
module URITemplate
|
26
|
+
|
27
|
+
class Colon
|
28
|
+
|
29
|
+
include URITemplate
|
30
|
+
|
31
|
+
VAR = /(?:\{:(?<name>[a-z]+)\}|:(?<name>[a-z]+)(?![a-z]))/
|
32
|
+
|
33
|
+
class Token
|
34
|
+
|
35
|
+
class Variable < self
|
36
|
+
|
37
|
+
include URITemplate::Expression
|
38
|
+
|
39
|
+
attr_reader :name
|
40
|
+
|
41
|
+
def initialize(name)
|
42
|
+
@name = name
|
43
|
+
@variables = [name]
|
44
|
+
end
|
45
|
+
|
46
|
+
def expand(vars)
|
47
|
+
return Utils.pct(vars[@name])
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_r
|
51
|
+
return ['(?<', name, '>[^/]*?)'].join
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class Static < self
|
57
|
+
|
58
|
+
include URITemplate::Literal
|
59
|
+
|
60
|
+
def initialize(str)
|
61
|
+
@string = str
|
62
|
+
end
|
63
|
+
|
64
|
+
def expand(_)
|
65
|
+
return @string
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_r
|
69
|
+
return Regexp.escape(@string)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_reader :pattern
|
77
|
+
|
78
|
+
# Tries to convert the value into a colon-template.
|
79
|
+
# @example
|
80
|
+
# URITemplate::Colon.try_convert('/foo/:bar/').pattern #=> '/foo/:bar/'
|
81
|
+
# URITemplate::Colon.try_convert(URITemplate::Draft7.new('/foo/{bar}/')).pattern #=> '/foo/{:bar}/'
|
82
|
+
def self.try_convert(x)
|
83
|
+
if x.kind_of? String
|
84
|
+
return new(x)
|
85
|
+
elsif x.kind_of? self
|
86
|
+
return x
|
87
|
+
elsif x.kind_of? URITemplate::Draft7 and x.level == 1
|
88
|
+
return new( x.pattern.gsub(/\{(.*?)\}/){ "{:#{$1}}" } )
|
89
|
+
else
|
90
|
+
return nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def initialize(pattern)
|
95
|
+
@pattern = pattern
|
96
|
+
end
|
97
|
+
|
98
|
+
# Extracts variables from an uri.
|
99
|
+
#
|
100
|
+
# @param String uri
|
101
|
+
# @return nil,Hash
|
102
|
+
def extract(uri)
|
103
|
+
return self.to_r.match(uri) do |md|
|
104
|
+
Hash[ *self.variables.map{|v|
|
105
|
+
[v, Utils.dpct(md[v])]
|
106
|
+
}.flatten(1) ]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def type
|
111
|
+
:colon
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_r
|
115
|
+
@regexp ||= Regexp.new('\A' + tokens.map(&:to_r).join + '\z')
|
116
|
+
end
|
117
|
+
|
118
|
+
def tokens
|
119
|
+
@tokens ||= tokenize!
|
120
|
+
end
|
121
|
+
|
122
|
+
# Tries to concatenate two templates, as if they were path segments.
|
123
|
+
# Removes double slashes or inserts one if they are missing.
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
# tpl = URITemplate::Colon.new('/xy/')
|
127
|
+
# (tpl / '/z/' ).pattern #=> '/xy/z/'
|
128
|
+
# (tpl / 'z/' ).pattern #=> '/xy/z/'
|
129
|
+
# (tpl / ':z' ).pattern #=> '/xy/:z'
|
130
|
+
# (tpl / ':a' / 'b' ).pattern #=> '/xy/:a/b'
|
131
|
+
#
|
132
|
+
def /(o)
|
133
|
+
this, other, this_converted, other_converted = URITemplate.coerce( self, o )
|
134
|
+
if this_converted
|
135
|
+
return this / other
|
136
|
+
end
|
137
|
+
return self.class.new( File.join( this.pattern, other.pattern ) )
|
138
|
+
end
|
139
|
+
|
140
|
+
protected
|
141
|
+
|
142
|
+
def tokenize!
|
143
|
+
RegexpEnumerator.new(VAR).each(@pattern).map{|x|
|
144
|
+
if x.kind_of? String
|
145
|
+
Token::Static.new(x)
|
146
|
+
else
|
147
|
+
Token::Variable.new(x['name'])
|
148
|
+
end
|
149
|
+
}.to_a
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
data/lib/uri_template/draft7.rb
CHANGED
@@ -109,22 +109,18 @@ __REGEXP__
|
|
109
109
|
# @private
|
110
110
|
class Literal < Token
|
111
111
|
|
112
|
-
|
113
|
-
|
112
|
+
include URITemplate::Literal
|
113
|
+
|
114
114
|
def initialize(string)
|
115
115
|
@string = string
|
116
116
|
end
|
117
117
|
|
118
|
-
def size
|
119
|
-
0
|
120
|
-
end
|
121
|
-
|
122
118
|
def level
|
123
119
|
1
|
124
120
|
end
|
125
121
|
|
126
|
-
def
|
127
|
-
|
122
|
+
def arity
|
123
|
+
0
|
128
124
|
end
|
129
125
|
|
130
126
|
def to_r_source(*_)
|
@@ -139,11 +135,15 @@ __REGEXP__
|
|
139
135
|
|
140
136
|
# @private
|
141
137
|
class Expression < Token
|
138
|
+
|
139
|
+
include URITemplate::Expression
|
142
140
|
|
143
141
|
attr_reader :variables, :max_length
|
144
142
|
|
145
143
|
def initialize(vars)
|
146
|
-
@
|
144
|
+
@variable_specs = vars
|
145
|
+
@variables = vars.map(&:first)
|
146
|
+
@variables.uniq!
|
147
147
|
end
|
148
148
|
|
149
149
|
PREFIX = ''.freeze
|
@@ -158,13 +158,9 @@ __REGEXP__
|
|
158
158
|
NAMED = false
|
159
159
|
OPERATOR = ''
|
160
160
|
|
161
|
-
def size
|
162
|
-
@variables.size
|
163
|
-
end
|
164
|
-
|
165
161
|
def level
|
166
|
-
if @
|
167
|
-
if @
|
162
|
+
if @variable_specs.none?{|_,expand,ml| expand || (ml > 0) }
|
163
|
+
if @variable_specs.size == 1
|
168
164
|
return self.class::BASE_LEVEL
|
169
165
|
else
|
170
166
|
return 3
|
@@ -174,9 +170,13 @@ __REGEXP__
|
|
174
170
|
end
|
175
171
|
end
|
176
172
|
|
177
|
-
def
|
173
|
+
def arity
|
174
|
+
@variable_specs.size
|
175
|
+
end
|
176
|
+
|
177
|
+
def expand( vars )
|
178
178
|
result = []
|
179
|
-
|
179
|
+
@variable_specs.each{| var, expand , max_length |
|
180
180
|
unless vars[var].nil?
|
181
181
|
if vars[var].kind_of? Hash
|
182
182
|
result.push( *transform_hash(var, vars[var], expand, max_length) )
|
@@ -199,7 +199,7 @@ __REGEXP__
|
|
199
199
|
end
|
200
200
|
|
201
201
|
def to_s
|
202
|
-
'{' + self.class::OPERATOR + @
|
202
|
+
'{' + self.class::OPERATOR + @variable_specs.map{|name,expand,max_length| name +(expand ? '*': '') + (max_length > 0 ? ':'+max_length.to_s : '') }.join(',') + '}'
|
203
203
|
end
|
204
204
|
|
205
205
|
#TODO: certain things after a slurpy variable will never get matched. therefore, it's pointless to add expressions for them
|
@@ -207,10 +207,10 @@ __REGEXP__
|
|
207
207
|
def to_r_source(base_counter = 0)
|
208
208
|
source = []
|
209
209
|
first = true
|
210
|
-
vs =
|
210
|
+
vs = @variable_specs.size - 1
|
211
211
|
i = 0
|
212
212
|
if self.class::NAMED
|
213
|
-
|
213
|
+
@variable_specs.each{| var, expand , max_length |
|
214
214
|
last = (vs == i)
|
215
215
|
value = "(?:\\g<#{self.class::CHARACTER_CLASS[:class_name]}>|,)#{(max_length > 0)?'{,'+max_length.to_s+'}':'*'}"
|
216
216
|
if expand
|
@@ -240,7 +240,7 @@ __REGEXP__
|
|
240
240
|
i = i+1
|
241
241
|
}
|
242
242
|
else
|
243
|
-
|
243
|
+
@variable_specs.each{| var, expand , max_length |
|
244
244
|
last = (vs == i)
|
245
245
|
if expand
|
246
246
|
# could be list or map, too
|
@@ -272,12 +272,12 @@ __REGEXP__
|
|
272
272
|
end
|
273
273
|
|
274
274
|
def extract(position,matched)
|
275
|
-
name, expand, max_length = @
|
275
|
+
name, expand, max_length = @variable_specs[position]
|
276
276
|
if matched.nil?
|
277
277
|
return [[ name , matched ]]
|
278
278
|
end
|
279
279
|
if expand
|
280
|
-
ex = self.hash_extractor(max_length)
|
280
|
+
ex = self.class.hash_extractor(max_length)
|
281
281
|
rest = matched
|
282
282
|
splitted = []
|
283
283
|
found_value = false
|
@@ -310,22 +310,25 @@ __REGEXP__
|
|
310
310
|
|
311
311
|
return [ [ name, decode( matched ) ] ]
|
312
312
|
end
|
313
|
-
|
314
|
-
def variable_names
|
315
|
-
@variables.collect(&:first)
|
316
|
-
end
|
317
313
|
|
318
314
|
protected
|
319
315
|
|
320
|
-
|
321
|
-
value = "\\g<#{self.class::CHARACTER_CLASS[:class_name]}>#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
|
322
|
-
|
323
|
-
pair = "(?<name>\\g<c_vn_>#{Regexp.escape(self.class::PAIR_CONNECTOR)})?(?<value>#{value})"
|
316
|
+
module ClassMethods
|
324
317
|
|
325
|
-
|
318
|
+
def hash_extractor(max_length)
|
319
|
+
@hash_extractors ||= {}
|
320
|
+
@hash_extractors[max_length] ||= begin
|
321
|
+
value = "\\g<#{self::CHARACTER_CLASS[:class_name]}>#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
|
322
|
+
pair = "(?<name>\\g<c_vn_>#{Regexp.escape(self::PAIR_CONNECTOR)})?(?<value>#{value})"
|
323
|
+
|
324
|
+
Regexp.new( CHARACTER_CLASSES[:varname][:class] + "{0}\n" + self::CHARACTER_CLASS[:class] + "{0}\n" + "^#{Regexp.escape(self::SEPARATOR)}?" + pair + "(?<rest>$|#{Regexp.escape(self::SEPARATOR)}(?!#{Regexp.escape(self::SEPARATOR)}))" ,Regexp::EXTENDED)
|
325
|
+
end
|
326
|
+
end
|
326
327
|
|
327
328
|
end
|
328
329
|
|
330
|
+
extend ClassMethods
|
331
|
+
|
329
332
|
def encode(x)
|
330
333
|
Utils.pct(Utils.object_to_param(x), self.class::CHARACTER_CLASS[:unencoded])
|
331
334
|
end
|
@@ -544,6 +547,7 @@ __REGEXP__
|
|
544
547
|
# tpl = URITemplate::Draft7.new('{foo}')
|
545
548
|
# URITemplate::Draft7.try_convert( tpl ) #=> tpl
|
546
549
|
# URITemplate::Draft7.try_convert('{foo}') #=> tpl
|
550
|
+
# URITemplate::Draft7.try_convert(URITemplate.new(:colon, ':foo')) #=> tpl
|
547
551
|
# # This pattern is invalid, so it wont be parsed:
|
548
552
|
# URITemplate::Draft7.try_convert('{foo') #=> nil
|
549
553
|
#
|
@@ -552,6 +556,14 @@ __REGEXP__
|
|
552
556
|
return x
|
553
557
|
elsif x.kind_of? String and valid? x
|
554
558
|
return new(x)
|
559
|
+
elsif x.kind_of? URITemplate::Colon
|
560
|
+
return new( x.tokens.map{|tk|
|
561
|
+
if tk.literal?
|
562
|
+
Literal.new(tk.string)
|
563
|
+
else
|
564
|
+
Expression.new([[tk.variables.first, false, 0]])
|
565
|
+
end
|
566
|
+
})
|
555
567
|
else
|
556
568
|
return nil
|
557
569
|
end
|
@@ -606,6 +618,7 @@ __REGEXP__
|
|
606
618
|
end
|
607
619
|
end
|
608
620
|
|
621
|
+
# @method expand(variables = {})
|
609
622
|
# Expands the template with the given variables.
|
610
623
|
# The expansion should be compatible to uritemplate spec draft 7 ( http://tools.ietf.org/html/draft-gregorio-uritemplate-07 ).
|
611
624
|
# @note
|
@@ -619,31 +632,6 @@ __REGEXP__
|
|
619
632
|
#
|
620
633
|
# @param variables Hash
|
621
634
|
# @return String
|
622
|
-
def expand(variables = {})
|
623
|
-
tokens.map{|part|
|
624
|
-
part.expand(variables, {})
|
625
|
-
}.join
|
626
|
-
end
|
627
|
-
|
628
|
-
# Returns an array containing all variables. Repeated variables are ignored, but the order will be kept intact.
|
629
|
-
# @example
|
630
|
-
# URITemplate::Draft7.new('{foo}{bar}{baz}').variables #=> ['foo','bar','baz']
|
631
|
-
# URITemplate::Draft7.new('{a}{c}{a}{b}').variables #=> ['c','a','b']
|
632
|
-
#
|
633
|
-
# @return Array
|
634
|
-
def variables
|
635
|
-
@variables ||= begin
|
636
|
-
vars = []
|
637
|
-
tokens.each{|token|
|
638
|
-
if token.respond_to? :variable_names
|
639
|
-
vn = token.variable_names.uniq
|
640
|
-
vars -= vn
|
641
|
-
vars.push(*vn)
|
642
|
-
end
|
643
|
-
}
|
644
|
-
vars
|
645
|
-
end
|
646
|
-
end
|
647
635
|
|
648
636
|
# Compiles this template into a regular expression which can be used to test whether a given uri matches this template. This template is also used for {#===}.
|
649
637
|
#
|
@@ -659,7 +647,7 @@ __REGEXP__
|
|
659
647
|
bc = 0
|
660
648
|
@regexp ||= Regexp.new(classes.join + '\A' + tokens.map{|part|
|
661
649
|
r = part.to_r_source(bc)
|
662
|
-
bc += part.
|
650
|
+
bc += part.arity
|
663
651
|
r
|
664
652
|
}.join + '\z' , Regexp::EXTENDED)
|
665
653
|
end
|
@@ -803,7 +791,7 @@ __REGEXP__
|
|
803
791
|
tokens.map(&:level).max
|
804
792
|
end
|
805
793
|
|
806
|
-
# Tries to
|
794
|
+
# Tries to concatenate two templates, as if they were path segments.
|
807
795
|
# Removes double slashes or insert one if they are missing.
|
808
796
|
#
|
809
797
|
# @example
|
@@ -886,20 +874,6 @@ __REGEXP__
|
|
886
874
|
|
887
875
|
return false
|
888
876
|
end
|
889
|
-
|
890
|
-
# Returns the number of static characters in this template.
|
891
|
-
# This method is useful for routing, since it's often pointful to use the url with fewer variable characters.
|
892
|
-
# 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.
|
893
|
-
#
|
894
|
-
# @example
|
895
|
-
# URITemplate::Draft7.new('/xy/').static_characters #=> 4
|
896
|
-
# URITemplate::Draft7.new('{foo}').static_characters #=> 0
|
897
|
-
# URITemplate::Draft7.new('a{foo}b').static_characters #=> 2
|
898
|
-
#
|
899
|
-
# @return Numeric
|
900
|
-
def static_characters
|
901
|
-
@static_characters ||= tokens.select{|t| t.kind_of?(Literal) }.map{|t| t.string.size }.inject(0,:+)
|
902
|
-
end
|
903
877
|
|
904
878
|
protected
|
905
879
|
# @private
|
@@ -917,7 +891,7 @@ protected
|
|
917
891
|
vars = []
|
918
892
|
tokens.each{|part|
|
919
893
|
i = 0
|
920
|
-
while i < part.
|
894
|
+
while i < part.arity
|
921
895
|
vars.push(*part.extract(i, matchdata["v#{bc}"]))
|
922
896
|
bc += 1
|
923
897
|
i += 1
|
data/lib/uri_template/utils.rb
CHANGED
@@ -17,6 +17,41 @@
|
|
17
17
|
|
18
18
|
module URITemplate
|
19
19
|
|
20
|
+
# An awesome little helper which helps iterating over a string.
|
21
|
+
# Initialize with a regexp and pass a string to :each.
|
22
|
+
# It will yield a string or a MatchData
|
23
|
+
class RegexpEnumerator
|
24
|
+
|
25
|
+
include Enumerable
|
26
|
+
|
27
|
+
def initialize(regexp)
|
28
|
+
@regexp = regexp
|
29
|
+
end
|
30
|
+
|
31
|
+
def each(str)
|
32
|
+
return Enumerator.new(self,:each,str) unless block_given?
|
33
|
+
rest = str
|
34
|
+
loop do
|
35
|
+
m = @regexp.match(rest)
|
36
|
+
if m.nil?
|
37
|
+
yield rest
|
38
|
+
break
|
39
|
+
end
|
40
|
+
yield m.pre_match if m.pre_match.size > 0
|
41
|
+
yield m
|
42
|
+
if m[0].size == 0
|
43
|
+
# obviously matches empty string, so post_match will equal rest
|
44
|
+
# terminate or this will loop forever
|
45
|
+
yield m.post_match
|
46
|
+
break
|
47
|
+
end
|
48
|
+
rest = m.post_match
|
49
|
+
end
|
50
|
+
return self
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
20
55
|
# This error will be raised whenever an object could not be converted to a param string.
|
21
56
|
class Unconvertable < StandardError
|
22
57
|
|
data/lib/uri_template.rb
CHANGED
@@ -18,8 +18,65 @@
|
|
18
18
|
# A base module for all implementations of a uri template.
|
19
19
|
module URITemplate
|
20
20
|
|
21
|
+
# This should make it possible to do basic analysis independently from the concrete type.
|
22
|
+
module Token
|
23
|
+
|
24
|
+
def size
|
25
|
+
variables.size
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
# A module which all literal tokens should include.
|
31
|
+
module Literal
|
32
|
+
|
33
|
+
include Token
|
34
|
+
|
35
|
+
attr_reader :string
|
36
|
+
|
37
|
+
def literal?
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def expression?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def variables
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
|
49
|
+
def size
|
50
|
+
0
|
51
|
+
end
|
52
|
+
|
53
|
+
def expand(*_)
|
54
|
+
return string
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# A module which all non-literal tokens should include.
|
61
|
+
module Expression
|
62
|
+
|
63
|
+
include Token
|
64
|
+
|
65
|
+
attr_reader :variables
|
66
|
+
|
67
|
+
def literal?
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
def expression?
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
21
77
|
autoload :Utils, 'uri_template/utils'
|
22
78
|
autoload :Draft7, 'uri_template/draft7'
|
79
|
+
autoload :Colon, 'uri_template/colon'
|
23
80
|
|
24
81
|
# A hash with all available implementations.
|
25
82
|
# Currently the only implementation is :draft7. But there also aliases :default and :latest available. This should make it possible to add newer specs later.
|
@@ -27,6 +84,7 @@ module URITemplate
|
|
27
84
|
VERSIONS = {
|
28
85
|
:draft7 => :Draft7,
|
29
86
|
:default => :Draft7,
|
87
|
+
:colon => :Colon,
|
30
88
|
:latest => :Draft7
|
31
89
|
}
|
32
90
|
|
@@ -95,6 +153,7 @@ module URITemplate
|
|
95
153
|
#
|
96
154
|
# @example
|
97
155
|
# URITemplate.coerce( URITemplate.new(:draft7,'{x}'), '{y}' ) #=> [URITemplate.new(:draft7,'{x}'), URITemplate.new(:draft7,'{y}'), false, true]
|
156
|
+
# URITemplate.coerce( '{y}', URITemplate.new(:draft7,'{x}') ) #=> [URITemplate.new(:draft7,'{y}'), URITemplate.new(:draft7,'{x}'), true, false]
|
98
157
|
def self.coerce(a,b)
|
99
158
|
if a.kind_of? URITemplate
|
100
159
|
if a.class == b.class
|
@@ -140,12 +199,15 @@ module URITemplate
|
|
140
199
|
module Invalid
|
141
200
|
end
|
142
201
|
|
143
|
-
# @abstract
|
144
202
|
# Expands this uri template with the given variables.
|
145
203
|
# The variables should be converted to strings using {Utils#object_to_param}.
|
146
204
|
# @raise {Unconvertable} if a variable could not be converted to a string.
|
147
|
-
|
148
|
-
|
205
|
+
# @param variables Hash
|
206
|
+
# @return String
|
207
|
+
def expand(variables = {})
|
208
|
+
tokens.map{|part|
|
209
|
+
part.expand(variables)
|
210
|
+
}.join
|
149
211
|
end
|
150
212
|
|
151
213
|
# @abstract
|
@@ -153,5 +215,35 @@ module URITemplate
|
|
153
215
|
def type
|
154
216
|
raise "Please implement #type on #{self.class.inspect}."
|
155
217
|
end
|
218
|
+
|
219
|
+
# @abstract
|
220
|
+
# Returns the tokens of this templates. Tokens should include either {Static} or {Expression}.
|
221
|
+
def tokens
|
222
|
+
raise "Please implement #tokens on #{self.class.inspect}."
|
223
|
+
end
|
224
|
+
|
225
|
+
# Returns an array containing all variables. Repeated variables are ignored. The concrete order of the variables may change.
|
226
|
+
# @example
|
227
|
+
# URITemplate.new('{foo}{bar}{baz}').variables #=> ['foo','bar','baz']
|
228
|
+
# URITemplate.new('{a}{c}{a}{b}').variables #=> ['a','c','b']
|
229
|
+
#
|
230
|
+
# @return Array
|
231
|
+
def variables
|
232
|
+
@variables ||= tokens.select(&:expression?).map(&:variables).flatten.uniq
|
233
|
+
end
|
234
|
+
|
235
|
+
# Returns the number of static characters in this template.
|
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\{var\}' both match 'static', but in most cases 'static' should be prefered over 'sta{var}' since it's more specific.
|
238
|
+
#
|
239
|
+
# @example
|
240
|
+
# URITemplate.new('/xy/').static_characters #=> 4
|
241
|
+
# URITemplate.new('{foo}').static_characters #=> 0
|
242
|
+
# URITemplate.new('a{foo}b').static_characters #=> 2
|
243
|
+
#
|
244
|
+
# @return Numeric
|
245
|
+
def static_characters
|
246
|
+
@static_characters ||= tokens.select(&:literal?).map{|t| t.string.size }.inject(0,:+)
|
247
|
+
end
|
156
248
|
|
157
249
|
end
|
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.1.
|
4
|
+
version: 0.1.2
|
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-10 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &14750900 !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: *14750900
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: yard
|
27
|
-
requirement: &
|
27
|
+
requirement: &14750460 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *14750460
|
36
36
|
description: ! 'A templating system for URIs, which implements http://tools.ietf.org/html/draft-gregorio-uritemplate-07
|
37
37
|
. An implementation of an older version of that spec is known as addressable. This
|
38
38
|
gem however is intended to be extended when newer specs evolve. For now only draft
|
@@ -43,6 +43,7 @@ extensions: []
|
|
43
43
|
extra_rdoc_files: []
|
44
44
|
files:
|
45
45
|
- lib/uri_template.rb
|
46
|
+
- lib/uri_template/colon.rb
|
46
47
|
- lib/uri_template/draft7.rb
|
47
48
|
- lib/uri_template/utils.rb
|
48
49
|
- uri_template.gemspec
|