uri_template 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,7 +35,7 @@ class URITemplate::RFC6570
35
35
  # @private
36
36
  Utils = URITemplate::Utils
37
37
 
38
- if SUPPORTS_UNICODE_CHARS
38
+ if Utils.use_unicode?
39
39
  # @private
40
40
  # \/ - unicode ctrl-chars
41
41
  LITERAL = /([^"'%<>\\^`{|}\u0000-\u001F\u007F-\u009F\s]|%[0-9a-fA-F]{2})+/u
@@ -48,16 +48,20 @@ class URITemplate::RFC6570
48
48
  CHARACTER_CLASSES = {
49
49
 
50
50
  :unreserved => {
51
- :class => '(?:[A-Za-z0-9\-\._]|%[0-9a-fA-F]{2})',
51
+ :class => '(?:[A-Za-z0-9\-\._]|%[0-9a-fA-F]{2})',
52
+ :class_with_comma => '(?:[A-Za-z0-9\-\._,]|%[0-9a-fA-F]{2})',
53
+ :class_without_comma => '(?:[A-Za-z0-9\-\._]|%[0-9a-fA-F]{2})',
52
54
  :grabs_comma => false
53
55
  },
54
56
  :unreserved_reserved_pct => {
55
57
  :class => '(?:[A-Za-z0-9\-\._:\/?#\[\]@!\$%\'\(\)*+,;=]|%[0-9a-fA-F]{2})',
58
+ :class_with_comma => '(?:[A-Za-z0-9\-\._:\/?#\[\]@!\$%\'\(\)*+,;=]|%[0-9a-fA-F]{2})',
59
+ :class_without_comma => '(?:[A-Za-z0-9\-\._:\/?#\[\]@!\$%\'\(\)*+;=]|%[0-9a-fA-F]{2})',
56
60
  :grabs_comma => true
57
61
  },
58
62
 
59
63
  :varname => {
60
- :class => '(?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*?',
64
+ :class => '(?:[A-Za-z0-9\-\._]|%[0-9a-fA-F]{2})+',
61
65
  :class_name => 'c_vn_'
62
66
  }
63
67
 
@@ -94,8 +98,6 @@ __REGEXP__
94
98
  \\A(#{LITERAL.source}|#{EXPRESSION.source})*\\z
95
99
  __REGEXP__
96
100
 
97
- SLASH = ?/
98
-
99
101
  # @private
100
102
  class Token
101
103
  end
@@ -127,424 +129,6 @@ __REGEXP__
127
129
 
128
130
  end
129
131
 
130
-
131
- # @private
132
- class Expression < Token
133
-
134
- include URITemplate::Expression
135
-
136
- attr_reader :variables, :max_length
137
-
138
- def initialize(vars)
139
- @variable_specs = vars
140
- @variables = vars.map(&:first)
141
- @variables.uniq!
142
- end
143
-
144
- PREFIX = ''.freeze
145
- SEPARATOR = ','.freeze
146
- PAIR_CONNECTOR = '='.freeze
147
- PAIR_IF_EMPTY = true
148
- LIST_CONNECTOR = ','.freeze
149
- BASE_LEVEL = 1
150
-
151
- CHARACTER_CLASS = CHARACTER_CLASSES[:unreserved]
152
-
153
- NAMED = false
154
- OPERATOR = ''
155
-
156
- def level
157
- if @variable_specs.none?{|_,expand,ml| expand || (ml > 0) }
158
- if @variable_specs.size == 1
159
- return self.class::BASE_LEVEL
160
- else
161
- return 3
162
- end
163
- else
164
- return 4
165
- end
166
- end
167
-
168
- def expands?
169
- @variable_specs.any?{|_,expand,_| expand }
170
- end
171
-
172
- def arity
173
- @variable_specs.size
174
- end
175
-
176
- def expand( vars )
177
- result = []
178
- @variable_specs.each{| var, expand , max_length |
179
- unless vars[var].nil?
180
- if vars[var].kind_of?(Hash) or Utils.pair_array?(vars[var])
181
- if max_length && max_length > 0
182
- raise InvalidValue::LengthLimitInapplicable.new(var,vars[var])
183
- end
184
- result.push( *transform_hash(var, vars[var], expand, max_length) )
185
- elsif vars[var].kind_of? Array
186
- if max_length && max_length > 0
187
- raise InvalidValue::LengthLimitInapplicable.new(var,vars[var])
188
- end
189
- result.push( *transform_array(var, vars[var], expand, max_length) )
190
- else
191
- if self.class::NAMED
192
- result.push( pair(var, vars[var], max_length) )
193
- else
194
- result.push( cut( escape(vars[var]), max_length ) )
195
- end
196
- end
197
- end
198
- }
199
- if result.any?
200
- return (self.class::PREFIX + result.join(self.class::SEPARATOR))
201
- else
202
- return ''
203
- end
204
- end
205
-
206
- def to_s
207
- return '{' + self.class::OPERATOR + @variable_specs.map{|name,expand,max_length| name + (expand ? '*': '') + (max_length > 0 ? (':' + max_length.to_s) : '') }.join(',') + '}'
208
- end
209
-
210
- #TODO: certain things after a slurpy variable will never get matched. therefore, it's pointless to add expressions for them
211
- #TODO: variables, which appear twice could be compacted, don't they?
212
- def to_r_source
213
- source = []
214
- first = true
215
- vs = @variable_specs.size - 1
216
- i = 0
217
- if self.class::NAMED
218
- @variable_specs.each{| var, expand , max_length |
219
- value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*'}"
220
- if expand
221
- #if self.class::PAIR_IF_EMPTY
222
- pair = "#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self.class::PAIR_CONNECTOR)}#{value}"
223
-
224
- if first
225
- source << "((?:#{pair})(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*)"
226
- else
227
- source << "((?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*)"
228
- end
229
- else
230
- if self.class::PAIR_IF_EMPTY
231
- pair = "#{Regexp.escape(var)}(#{Regexp.escape(self.class::PAIR_CONNECTOR)}#{value})"
232
- else
233
- pair = "#{Regexp.escape(var)}(#{Regexp.escape(self.class::PAIR_CONNECTOR)}#{value}|)"
234
- end
235
-
236
- if first
237
- source << "(?:#{pair})"
238
- else
239
- source << "(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})?"
240
- end
241
- end
242
-
243
- first = false
244
- i = i+1
245
- }
246
- else
247
- @variable_specs.each{| var, expand , max_length |
248
- last = (vs == i)
249
- if expand
250
- # could be list or map, too
251
- value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*'}"
252
-
253
- pair = "(?:#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self.class::PAIR_CONNECTOR)})?#{value}"
254
-
255
- value = "#{pair}(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*"
256
- elsif last
257
- # the last will slurp lists
258
- if self.class::CHARACTER_CLASS[:grabs_comma]
259
- value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
260
- else
261
- value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
262
- end
263
- else
264
- value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
265
- end
266
- if first
267
- source << "(#{value})"
268
- first = false
269
- else
270
- source << "(?:#{Regexp.escape(self.class::SEPARATOR)}(#{value}))?"
271
- end
272
- i = i+1
273
- }
274
- end
275
- return '(?:' + Regexp.escape(self.class::PREFIX) + source.join + ')?'
276
- end
277
-
278
- def extract(position,matched)
279
- name, expand, max_length = @variable_specs[position]
280
- if matched.nil?
281
- return [[ name , matched ]]
282
- end
283
- if expand
284
- #TODO: do we really need this? - this could be stolen from rack
285
- ex = self.class.hash_extractor(max_length)
286
- rest = matched
287
- splitted = []
288
- if self.class::NAMED
289
- # 1 = name
290
- # 2 = value
291
- # 3 = rest
292
- until rest.size == 0
293
- match = ex.match(rest)
294
- if match.nil?
295
- raise "Couldn't match #{rest.inspect} againts the hash extractor. This is definitly a Bug. Please report this ASAP!"
296
- end
297
- if match.post_match.size == 0
298
- rest = match[3].to_s
299
- else
300
- rest = ''
301
- end
302
- splitted << [ match[1], decode(match[2] + rest , false) ]
303
- rest = match.post_match
304
- end
305
- result = Utils.pair_array_to_hash2( splitted )
306
- if result.size == 1 && result[0][0] == name
307
- return result
308
- else
309
- return [ [ name , result ] ]
310
- end
311
- else
312
- found_value = false
313
- # 1 = name and seperator
314
- # 2 = value
315
- # 3 = rest
316
- until rest.size == 0
317
- match = ex.match(rest)
318
- if match.nil?
319
- raise "Couldn't match #{rest.inspect} againts the hash extractor. This is definitly a Bug. Please report this ASAP!"
320
- end
321
- if match.post_match.size == 0
322
- rest = match[3].to_s
323
- else
324
- rest = ''
325
- end
326
- if match[1]
327
- found_value = true
328
- splitted << [ match[1][0..-2], decode(match[2] + rest , false) ]
329
- else
330
- splitted << [ match[2] + rest, nil ]
331
- end
332
- rest = match.post_match
333
- end
334
- if !found_value
335
- return [ [ name, splitted.map{|n,v| decode(n , false) } ] ]
336
- else
337
- return [ [ name, splitted ] ]
338
- end
339
- end
340
- elsif self.class::NAMED
341
- return [ [ name, decode( matched[1..-1] ) ] ]
342
- end
343
-
344
- return [ [ name, decode( matched ) ] ]
345
- end
346
-
347
- protected
348
-
349
- module ClassMethods
350
-
351
- def hash_extractor(max_length)
352
- @hash_extractors ||= {}
353
- @hash_extractors[max_length] ||= begin
354
- value = "#{self::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
355
- if self::NAMED
356
- pair = "(#{CHARACTER_CLASSES[:varname][:class]})#{Regexp.escape(self::PAIR_CONNECTOR)}(#{value})"
357
- else
358
- pair = "(#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self::PAIR_CONNECTOR)})?(#{value})"
359
- end
360
- source = "\\A#{Regexp.escape(self::SEPARATOR)}?" + pair + "(\\z|#{Regexp.escape(self::SEPARATOR)}(?!#{Regexp.escape(self::SEPARATOR)}))"
361
- Regexp.new( source , Utils::KCODE_UTF8)
362
- end
363
- end
364
-
365
- end
366
-
367
- extend ClassMethods
368
-
369
- def escape(x)
370
- Utils.escape_url(Utils.object_to_param(x))
371
- end
372
-
373
- def unescape(x)
374
- Utils.unescape_url(x)
375
- end
376
-
377
- SPLITTER = /^(?:,(,*)|([^,]+))/
378
-
379
- def decode(x, split = true)
380
- if x.nil?
381
- if self.class::PAIR_IF_EMPTY
382
- return x
383
- else
384
- return ''
385
- end
386
- elsif split
387
- r = []
388
- v = x
389
- until v.size == 0
390
- m = SPLITTER.match(v)
391
- if m[1] and m[1].size > 0
392
- if m.post_match.size == 0
393
- r << m[1]
394
- else
395
- r << m[1][0..-2]
396
- end
397
- elsif m[2]
398
- r << unescape(m[2])
399
- end
400
- v = m.post_match
401
- end
402
- case(r.size)
403
- when 0 then ''
404
- when 1 then r.first
405
- else r
406
- end
407
- else
408
- unescape(x)
409
- end
410
- end
411
-
412
- def cut(str,chars)
413
- if chars > 0
414
- md = Regexp.compile("\\A#{self.class::CHARACTER_CLASS[:class]}{0,#{chars.to_s}}", Utils::KCODE_UTF8).match(str)
415
- #TODO: handle invalid matches
416
- return md[0]
417
- else
418
- return str
419
- end
420
- end
421
-
422
- def pair(key, value, max_length = 0)
423
- ek = escape(key)
424
- ev = escape(value)
425
- if !self.class::PAIR_IF_EMPTY and ev.size == 0
426
- return ek
427
- else
428
- return ek + self.class::PAIR_CONNECTOR + cut( ev, max_length )
429
- end
430
- end
431
-
432
- def transform_hash(name, hsh, expand , max_length)
433
- if expand
434
- hsh.map{|key,value| pair(key,value) }
435
- elsif hsh.none?
436
- []
437
- else
438
- [ (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) ]
439
- end
440
- end
441
-
442
- def transform_array(name, ary, expand , max_length)
443
- if expand
444
- self.class::NAMED ? ary.map{|value| pair(name,value) } : ary.map{|value| escape(value) }
445
- elsif ary.none?
446
- []
447
- else
448
- [ (self.class::NAMED ? escape(name)+self.class::PAIR_CONNECTOR : '' ) + ary.map{|value| escape(value) }.join(self.class::LIST_CONNECTOR) ]
449
- end
450
- end
451
-
452
- class Reserved < self
453
-
454
- CHARACTER_CLASS = CHARACTER_CLASSES[:unreserved_reserved_pct]
455
- OPERATOR = '+'.freeze
456
- BASE_LEVEL = 2
457
-
458
- def escape(x)
459
- Utils.escape_uri(Utils.object_to_param(x))
460
- end
461
-
462
- def unescape(x)
463
- Utils.unescape_uri(x)
464
- end
465
-
466
- end
467
-
468
- class Fragment < self
469
-
470
- CHARACTER_CLASS = CHARACTER_CLASSES[:unreserved_reserved_pct]
471
- PREFIX = '#'.freeze
472
- OPERATOR = '#'.freeze
473
- BASE_LEVEL = 2
474
-
475
- def escape(x)
476
- Utils.escape_uri(Utils.object_to_param(x))
477
- end
478
-
479
- def unescape(x)
480
- Utils.unescape_uri(x)
481
- end
482
-
483
- end
484
-
485
- class Label < self
486
-
487
- SEPARATOR = '.'.freeze
488
- PREFIX = '.'.freeze
489
- OPERATOR = '.'.freeze
490
- BASE_LEVEL = 3
491
-
492
- end
493
-
494
- class Path < self
495
-
496
- SEPARATOR = '/'.freeze
497
- PREFIX = '/'.freeze
498
- OPERATOR = '/'.freeze
499
- BASE_LEVEL = 3
500
-
501
- end
502
-
503
- class PathParameters < self
504
-
505
- SEPARATOR = ';'.freeze
506
- PREFIX = ';'.freeze
507
- NAMED = true
508
- PAIR_IF_EMPTY = false
509
- OPERATOR = ';'.freeze
510
- BASE_LEVEL = 3
511
-
512
- end
513
-
514
- class FormQuery < self
515
-
516
- SEPARATOR = '&'.freeze
517
- PREFIX = '?'.freeze
518
- NAMED = true
519
- OPERATOR = '?'.freeze
520
- BASE_LEVEL = 3
521
-
522
- end
523
-
524
- class FormQueryContinuation < self
525
-
526
- SEPARATOR = '&'.freeze
527
- PREFIX = '&'.freeze
528
- NAMED = true
529
- OPERATOR = '&'.freeze
530
- BASE_LEVEL = 3
531
-
532
- end
533
-
534
- end
535
-
536
- # @private
537
- OPERATORS = {
538
- '' => Expression,
539
- '+' => Expression::Reserved,
540
- '#' => Expression::Fragment,
541
- '.' => Expression::Label,
542
- '/' => Expression::Path,
543
- ';' => Expression::PathParameters,
544
- '?' => Expression::FormQuery,
545
- '&' => Expression::FormQueryContinuation
546
- }
547
-
548
132
  # This error is raised when an invalid pattern was given.
549
133
  class Invalid < StandardError
550
134
 
@@ -601,9 +185,6 @@ __REGEXP__
601
185
  end
602
186
 
603
187
  def each
604
- if !block_given?
605
- return Enumerator.new(self)
606
- end
607
188
  scanner = StringScanner.new(@source)
608
189
  until scanner.eos?
609
190
  expression = scanner.scan(EXPRESSION)
@@ -641,9 +222,6 @@ __REGEXP__
641
222
  # URITemplate::RFC6570.try_convert( tpl ) #=> tpl
642
223
  # URITemplate::RFC6570.try_convert('{foo}') #=> tpl
643
224
  # URITemplate::RFC6570.try_convert(URITemplate.new(:colon, ':foo')) #=> tpl
644
- # URITemplate::RFC6570.try_convert(URITemplate.new(:draft7, '{foo}')) #=> tpl
645
- # # Draft7 and RFC6570 handle expansion of named variables a bit differently:
646
- # URITemplate::RFC6570.try_convert(URITemplate.new(:draft7, '{?list*}')) #=> nil
647
225
  # # This pattern is invalid, so it wont be parsed:
648
226
  # URITemplate::RFC6570.try_convert('{foo') #=> nil
649
227
  #
@@ -653,6 +231,7 @@ __REGEXP__
653
231
  elsif x.kind_of? String and valid? x
654
232
  return new(x)
655
233
  elsif x.kind_of? URITemplate::Colon
234
+ return nil if x.tokens.any?{|tk| tk.kind_of? URITemplate::Colon::Token::Splat }
656
235
  return new( x.tokens.map{|tk|
657
236
  if tk.literal?
658
237
  Literal.new(tk.string)
@@ -660,27 +239,11 @@ __REGEXP__
660
239
  Expression.new([[tk.variables.first, false, 0]])
661
240
  end
662
241
  })
663
- elsif (x.class == URITemplate::Draft7 and self == URITemplate::RFC6570) or (x.class == URITemplate::RFC6570 and self == URITemplate::Draft7)
664
- if x.tokens.none?{|t| t.class::NAMED and t.expands? }
665
- return self.new(x.to_s)
666
- end
667
242
  else
668
243
  return nil
669
244
  end
670
245
  end
671
246
 
672
- # Like {.try_convert}, but raises an ArgumentError, when the conversion failed.
673
- #
674
- # @raise ArgumentError
675
- def convert(x)
676
- o = self.try_convert(x)
677
- if o.nil?
678
- raise ArgumentError, "Expected to receive something that can be converted to an #{self.class}, but got: #{x.inspect}."
679
- else
680
- return o
681
- end
682
- end
683
-
684
247
  # Tests whether a given pattern is a valid template pattern.
685
248
  # @example
686
249
  # URITemplate::RFC6570.valid? 'foo' #=> true
@@ -717,7 +280,7 @@ __REGEXP__
717
280
 
718
281
  # @method expand(variables = {})
719
282
  # Expands the template with the given variables.
720
- # The expansion should be compatible to uritemplate spec draft 7 ( http://tools.ietf.org/html/draft-gregorio-uritemplate-07 ).
283
+ # The expansion should be compatible to uritemplate spec rfc 6570 ( http://tools.ietf.org/html/rfc6570 ).
721
284
  # @note
722
285
  # All keys of the supplied hash should be strings as anything else won't be recognised.
723
286
  # @note
@@ -776,14 +339,13 @@ __REGEXP__
776
339
  # @example Extraction cruces
777
340
  # two_lists = URITemplate::RFC6570.new('{listA*,listB*}')
778
341
  # uri = two_lists.expand('listA'=>[1,2],'listB'=>[3,4]) #=> "1,2,3,4"
779
- # variables = two_lists.extract( uri ) #=> {'listA'=>["1","2","3","4"],'listB'=>nil}
342
+ # variables = two_lists.extract( uri ) #=> {'listA'=>["1","2","3"],'listB'=>["4"]}
780
343
  # # However, like said in the note:
781
344
  # two_lists.expand( variables ) == uri #=> true
782
345
  #
783
346
  # @note
784
347
  # The current implementation drops duplicated variables instead of checking them.
785
348
  #
786
- #
787
349
  def extract(uri_or_match, post_processing = DEFAULT_PROCESSING )
788
350
  if uri_or_match.kind_of? String
789
351
  m = self.to_r.match(uri_or_match)
@@ -816,22 +378,6 @@ __REGEXP__
816
378
  extract( uri_or_match, NO_PROCESSING )
817
379
  end
818
380
 
819
- # Returns the pattern for this template.
820
- def pattern
821
- @pattern ||= tokens.map(&:to_s).join
822
- end
823
-
824
- alias to_s pattern
825
-
826
- # Compares two template patterns.
827
- def ==(o)
828
- this, other, this_converted, _ = URITemplate.coerce( self, o )
829
- if this_converted
830
- return this == other
831
- end
832
- return this.pattern == other.pattern
833
- end
834
-
835
381
  # @method ===(uri)
836
382
  # Alias for to_r.=== . Tests whether this template matches a given uri.
837
383
  # @return TrueClass, FalseClass
@@ -855,7 +401,7 @@ __REGEXP__
855
401
  self.class::TYPE
856
402
  end
857
403
 
858
- # Returns the level of this template according to the draft ( http://tools.ietf.org/html/draft-gregorio-uritemplate-07#section-1.2 ). Higher level means higher complexity.
404
+ # Returns the level of this template according to the rfc 6570 ( http://tools.ietf.org/html/rfc6570#section-1.2 ). Higher level means higher complexity.
859
405
  # Basically this is defined as:
860
406
  #
861
407
  # * Level 1: no operators, one variable per expansion, no variable modifiers
@@ -878,70 +424,6 @@ __REGEXP__
878
424
  tokens.map(&:level).max
879
425
  end
880
426
 
881
- # Tries to concatenate two templates, as if they were path segments.
882
- # Removes double slashes or insert one if they are missing.
883
- #
884
- # @example
885
- # tpl = URITemplate::RFC6570.new('/xy/')
886
- # (tpl / '/z/' ).pattern #=> '/xy/z/'
887
- # (tpl / 'z/' ).pattern #=> '/xy/z/'
888
- # (tpl / '{/z}' ).pattern #=> '/xy{/z}'
889
- # (tpl / 'a' / 'b' ).pattern #=> '/xy/a/b'
890
- #
891
- def /(o)
892
- this, other, this_converted, _ = URITemplate.coerce( self, o )
893
- if this_converted
894
- return this / other
895
- end
896
- klass = self.class
897
- if other.absolute?
898
- raise ArgumentError, "Expected to receive a relative template but got an absoulte one: #{other.inspect}. If you think this is a bug, please report it."
899
- end
900
-
901
- if other.pattern == ''
902
- return self
903
- end
904
- # Merge!
905
- # Analyze the last token of this an the first token of the next and try to merge them
906
- if self.tokens.last.kind_of?(klass::Literal)
907
- if self.tokens.last.string[-1] == SLASH # the last token ends with an /
908
- if other.tokens.first.kind_of? klass::Literal
909
- # both seems to be paths, merge them!
910
- if other.tokens.first.string[0] == SLASH
911
- # strip one '/'
912
- return self.class.new( self.tokens[0..-2] + [ klass::Literal.new(self.tokens.last.string + other.tokens.first.string[1..-1]) ] + other.tokens[1..-1] )
913
- else
914
- # no problem, but we can merge them
915
- return self.class.new( self.tokens[0..-2] + [ klass::Literal.new(self.tokens.last.string + other.tokens.first.string) ] + other.tokens[1..-1] )
916
- end
917
- elsif other.tokens.first.kind_of? klass::Expression::Path
918
- # this will automatically insert '/'
919
- # so we can strip one '/'
920
- return self.class.new( self.tokens[0..-2] + [ klass::Literal.new(self.tokens.last.string[0..-2]) ] + other.tokens )
921
- end
922
- elsif other.tokens.first.kind_of? klass::Literal
923
- # okay, this template does not end with /, but the next starts with a literal => merge them!
924
- if other.tokens.first.string[0] == SLASH
925
- return self.class.new( self.tokens[0..-2] + [ klass::Literal.new(self.tokens.last.string + other.tokens.first.string)] + other.tokens[1..-1] )
926
- else
927
- return self.class.new( self.tokens[0..-2] + [ klass::Literal.new(self.tokens.last.string + '/' + other.tokens.first.string)] + other.tokens[1..-1] )
928
- end
929
- end
930
- end
931
-
932
- if other.tokens.first.kind_of?(klass::Literal)
933
- if other.tokens.first.string[0] == SLASH
934
- return self.class.new( self.tokens + other.tokens )
935
- else
936
- return self.class.new( self.tokens + [ klass::Literal.new('/' + other.tokens.first.string)]+ other.tokens[1..-1] )
937
- end
938
- elsif other.tokens.first.kind_of?(klass::Expression::Path)
939
- return self.class.new( self.tokens + other.tokens )
940
- else
941
- return self.class.new( self.tokens + [ klass::Literal.new('/')] + other.tokens )
942
- end
943
- end
944
-
945
427
  # Returns an array containing a the template tokens.
946
428
  def tokens
947
429
  @tokens ||= tokenize!
@@ -966,27 +448,27 @@ protected
966
448
  i = 0
967
449
  pa = part.arity
968
450
  while i < pa
969
- vars << part.extract(i, matchdata[bc])
451
+ vars.push( *part.extract(i, matchdata[bc]) )
970
452
  bc += 1
971
453
  i += 1
972
454
  end
973
455
  }
974
456
  if post_processing.include? :convert_result
975
457
  if post_processing.include? :convert_values
976
- vars.flatten!(1)
977
- return Hash[*vars.map!{|k,v| [k,Utils.pair_array_to_hash(v)] }.flatten(1) ]
458
+ return Hash[ vars.map!{|k,v| [k,Utils.pair_array_to_hash(v)] } ]
978
459
  else
979
- vars.flatten!(2)
980
- return Hash[*vars]
460
+ return Hash[vars]
981
461
  end
982
462
  else
983
463
  if post_processing.include? :convert_value
984
- vars.flatten!(1)
985
464
  return vars.collect{|k,v| [k,Utils.pair_array_to_hash(v)] }
986
465
  else
987
- return vars.flatten(1)
466
+ return vars
988
467
  end
989
468
  end
990
469
  end
991
470
 
992
471
  end
472
+
473
+ require 'uri_template/rfc6570/regex_builder.rb'
474
+ require 'uri_template/rfc6570/expression.rb'