uri_template 0.4.0 → 0.5.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.
@@ -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'