uri_template 0.1.1 → 0.1.2
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 +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
|