addressable 2.3.3 → 2.8.9
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +199 -29
- data/README.md +103 -69
- data/lib/addressable/idna/native.rb +31 -8
- data/lib/addressable/idna/pure.rb +4269 -217
- data/lib/addressable/idna.rb +3 -2
- data/lib/addressable/template.rb +243 -58
- data/lib/addressable/uri.rb +614 -294
- data/lib/addressable/version.rb +5 -4
- data/lib/addressable.rb +4 -0
- metadata +30 -81
- data/Gemfile +0 -18
- data/Rakefile +0 -37
- data/data/unicode.data +0 -0
- data/spec/addressable/idna_spec.rb +0 -231
- data/spec/addressable/net_http_compat_spec.rb +0 -26
- data/spec/addressable/template_spec.rb +0 -1022
- data/spec/addressable/uri_spec.rb +0 -5161
- data/tasks/clobber.rake +0 -2
- data/tasks/gem.rake +0 -86
- data/tasks/git.rake +0 -45
- data/tasks/metrics.rake +0 -22
- data/tasks/rspec.rake +0 -58
- data/tasks/rubyforge.rake +0 -89
- data/tasks/yard.rake +0 -27
- data/website/index.html +0 -110
data/lib/addressable/idna.rb
CHANGED
data/lib/addressable/template.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
#--
|
|
3
|
-
# Copyright (C)
|
|
4
|
+
# Copyright (C) Bob Aman
|
|
4
5
|
#
|
|
5
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
7
|
# you may not use this file except in compliance with the License.
|
|
@@ -35,7 +36,7 @@ module Addressable
|
|
|
35
36
|
Addressable::URI::CharacterClasses::DIGIT + '_'
|
|
36
37
|
|
|
37
38
|
var_char =
|
|
38
|
-
"(
|
|
39
|
+
"(?>(?:[#{variable_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)"
|
|
39
40
|
RESERVED =
|
|
40
41
|
"(?:[#{anything}]|%[a-fA-F0-9][a-fA-F0-9])"
|
|
41
42
|
UNRESERVED =
|
|
@@ -132,6 +133,7 @@ module Addressable
|
|
|
132
133
|
self.template.variables
|
|
133
134
|
end
|
|
134
135
|
alias_method :keys, :variables
|
|
136
|
+
alias_method :names, :variables
|
|
135
137
|
|
|
136
138
|
##
|
|
137
139
|
# @return [Array]
|
|
@@ -146,6 +148,64 @@ module Addressable
|
|
|
146
148
|
end
|
|
147
149
|
alias_method :captures, :values
|
|
148
150
|
|
|
151
|
+
##
|
|
152
|
+
# Accesses captured values by name or by index.
|
|
153
|
+
#
|
|
154
|
+
# @param [String, Symbol, Fixnum] key
|
|
155
|
+
# Capture index or name. Note that when accessing by with index
|
|
156
|
+
# of 0, the full URI will be returned. The intention is to mimic
|
|
157
|
+
# the ::MatchData#[] behavior.
|
|
158
|
+
#
|
|
159
|
+
# @param [#to_int, nil] len
|
|
160
|
+
# If provided, an array of values will be returned with the given
|
|
161
|
+
# parameter used as length.
|
|
162
|
+
#
|
|
163
|
+
# @return [Array, String, nil]
|
|
164
|
+
# The captured value corresponding to the index or name. If the
|
|
165
|
+
# value was not provided or the key is unknown, nil will be
|
|
166
|
+
# returned.
|
|
167
|
+
#
|
|
168
|
+
# If the second parameter is provided, an array of that length will
|
|
169
|
+
# be returned instead.
|
|
170
|
+
def [](key, len = nil)
|
|
171
|
+
if len
|
|
172
|
+
to_a[key, len]
|
|
173
|
+
elsif String === key or Symbol === key
|
|
174
|
+
mapping[key.to_s]
|
|
175
|
+
else
|
|
176
|
+
to_a[key]
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
##
|
|
181
|
+
# @return [Array]
|
|
182
|
+
# Array with the matched URI as first element followed by the captured
|
|
183
|
+
# values.
|
|
184
|
+
def to_a
|
|
185
|
+
[to_s, *values]
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
##
|
|
189
|
+
# @return [String]
|
|
190
|
+
# The matched URI as String.
|
|
191
|
+
def to_s
|
|
192
|
+
uri.to_s
|
|
193
|
+
end
|
|
194
|
+
alias_method :string, :to_s
|
|
195
|
+
|
|
196
|
+
# Returns multiple captured values at once.
|
|
197
|
+
#
|
|
198
|
+
# @param [String, Symbol, Fixnum] *indexes
|
|
199
|
+
# Indices of the captures to be returned
|
|
200
|
+
#
|
|
201
|
+
# @return [Array]
|
|
202
|
+
# Values corresponding to given indices.
|
|
203
|
+
#
|
|
204
|
+
# @see Addressable::Template::MatchData#[]
|
|
205
|
+
def values_at(*indexes)
|
|
206
|
+
indexes.map { |i| self[i] }
|
|
207
|
+
end
|
|
208
|
+
|
|
149
209
|
##
|
|
150
210
|
# Returns a <tt>String</tt> representation of the MatchData's state.
|
|
151
211
|
#
|
|
@@ -154,6 +214,15 @@ module Addressable
|
|
|
154
214
|
sprintf("#<%s:%#0x RESULT:%s>",
|
|
155
215
|
self.class.to_s, self.object_id, self.mapping.inspect)
|
|
156
216
|
end
|
|
217
|
+
|
|
218
|
+
##
|
|
219
|
+
# Dummy method for code expecting a ::MatchData instance
|
|
220
|
+
#
|
|
221
|
+
# @return [String] An empty string.
|
|
222
|
+
def pre_match
|
|
223
|
+
""
|
|
224
|
+
end
|
|
225
|
+
alias_method :post_match, :pre_match
|
|
157
226
|
end
|
|
158
227
|
|
|
159
228
|
##
|
|
@@ -166,7 +235,18 @@ module Addressable
|
|
|
166
235
|
if !pattern.respond_to?(:to_str)
|
|
167
236
|
raise TypeError, "Can't convert #{pattern.class} into String."
|
|
168
237
|
end
|
|
169
|
-
@pattern = pattern.to_str.freeze
|
|
238
|
+
@pattern = pattern.to_str.dup.freeze
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
##
|
|
242
|
+
# Freeze URI, initializing instance variables.
|
|
243
|
+
#
|
|
244
|
+
# @return [Addressable::URI] The frozen URI object.
|
|
245
|
+
def freeze
|
|
246
|
+
self.variables
|
|
247
|
+
self.variable_defaults
|
|
248
|
+
self.named_captures
|
|
249
|
+
super
|
|
170
250
|
end
|
|
171
251
|
|
|
172
252
|
##
|
|
@@ -182,6 +262,26 @@ module Addressable
|
|
|
182
262
|
self.class.to_s, self.object_id, self.pattern)
|
|
183
263
|
end
|
|
184
264
|
|
|
265
|
+
##
|
|
266
|
+
# Returns <code>true</code> if the Template objects are equal. This method
|
|
267
|
+
# does NOT normalize either Template before doing the comparison.
|
|
268
|
+
#
|
|
269
|
+
# @param [Object] template The Template to compare.
|
|
270
|
+
#
|
|
271
|
+
# @return [TrueClass, FalseClass]
|
|
272
|
+
# <code>true</code> if the Templates are equivalent, <code>false</code>
|
|
273
|
+
# otherwise.
|
|
274
|
+
def ==(template)
|
|
275
|
+
return false unless template.kind_of?(Template)
|
|
276
|
+
return self.pattern == template.pattern
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
##
|
|
280
|
+
# Addressable::Template makes no distinction between `==` and `eql?`.
|
|
281
|
+
#
|
|
282
|
+
# @see #==
|
|
283
|
+
alias_method :eql?, :==
|
|
284
|
+
|
|
185
285
|
##
|
|
186
286
|
# Extracts a mapping from the URI using a URI Template pattern.
|
|
187
287
|
#
|
|
@@ -311,7 +411,7 @@ module Addressable
|
|
|
311
411
|
# match.captures
|
|
312
412
|
# #=> ["a", ["b", "c"]]
|
|
313
413
|
def match(uri, processor=nil)
|
|
314
|
-
uri = Addressable::URI.parse(uri)
|
|
414
|
+
uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)
|
|
315
415
|
mapping = {}
|
|
316
416
|
|
|
317
417
|
# First, we need to process the pattern, and extract the values.
|
|
@@ -329,6 +429,7 @@ module Addressable
|
|
|
329
429
|
_, operator, varlist = *expansion.match(EXPRESSION)
|
|
330
430
|
varlist.split(',').each do |varspec|
|
|
331
431
|
_, name, modifier = *varspec.match(VARSPEC)
|
|
432
|
+
mapping[name] ||= nil
|
|
332
433
|
case operator
|
|
333
434
|
when nil, '+', '#', '/', '.'
|
|
334
435
|
unparsed_value = unparsed_values[index]
|
|
@@ -337,12 +438,14 @@ module Addressable
|
|
|
337
438
|
value = value.split(JOINERS[operator]) if value && modifier == '*'
|
|
338
439
|
when ';', '?', '&'
|
|
339
440
|
if modifier == '*'
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
441
|
+
if unparsed_values[index]
|
|
442
|
+
value = unparsed_values[index].split(JOINERS[operator])
|
|
443
|
+
value = value.inject({}) do |acc, v|
|
|
444
|
+
key, val = v.split('=')
|
|
445
|
+
val = "" if val.nil?
|
|
446
|
+
acc[key] = val
|
|
447
|
+
acc
|
|
448
|
+
end
|
|
346
449
|
end
|
|
347
450
|
else
|
|
348
451
|
if (unparsed_values[index])
|
|
@@ -367,10 +470,9 @@ module Addressable
|
|
|
367
470
|
value = Addressable::URI.unencode_component(value)
|
|
368
471
|
end
|
|
369
472
|
end
|
|
370
|
-
if mapping
|
|
473
|
+
if !mapping.has_key?(name) || mapping[name].nil?
|
|
474
|
+
# Doesn't exist, set to value (even if value is nil)
|
|
371
475
|
mapping[name] = value
|
|
372
|
-
else
|
|
373
|
-
return nil
|
|
374
476
|
end
|
|
375
477
|
index = index + 1
|
|
376
478
|
end
|
|
@@ -387,6 +489,8 @@ module Addressable
|
|
|
387
489
|
# @param [Hash] mapping The mapping that corresponds to the pattern.
|
|
388
490
|
# @param [#validate, #transform] processor
|
|
389
491
|
# An optional processor object may be supplied.
|
|
492
|
+
# @param [Boolean] normalize_values
|
|
493
|
+
# Optional flag to enable/disable unicode normalization. Default: true
|
|
390
494
|
#
|
|
391
495
|
# The object should respond to either the <tt>validate</tt> or
|
|
392
496
|
# <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
|
|
@@ -417,10 +521,11 @@ module Addressable
|
|
|
417
521
|
# "http://example.com/{?one,two,three}/"
|
|
418
522
|
# ).partial_expand({"one" => "1", "three" => 3}).pattern
|
|
419
523
|
# #=> "http://example.com/?one=1{&two}&three=3"
|
|
420
|
-
def partial_expand(mapping, processor=nil)
|
|
524
|
+
def partial_expand(mapping, processor=nil, normalize_values=true)
|
|
421
525
|
result = self.pattern.dup
|
|
526
|
+
mapping = normalize_keys(mapping)
|
|
422
527
|
result.gsub!( EXPRESSION ) do |capture|
|
|
423
|
-
transform_partial_capture(mapping, capture, processor)
|
|
528
|
+
transform_partial_capture(mapping, capture, processor, normalize_values)
|
|
424
529
|
end
|
|
425
530
|
return Addressable::Template.new(result)
|
|
426
531
|
end
|
|
@@ -431,6 +536,8 @@ module Addressable
|
|
|
431
536
|
# @param [Hash] mapping The mapping that corresponds to the pattern.
|
|
432
537
|
# @param [#validate, #transform] processor
|
|
433
538
|
# An optional processor object may be supplied.
|
|
539
|
+
# @param [Boolean] normalize_values
|
|
540
|
+
# Optional flag to enable/disable unicode normalization. Default: true
|
|
434
541
|
#
|
|
435
542
|
# The object should respond to either the <tt>validate</tt> or
|
|
436
543
|
# <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
|
|
@@ -481,11 +588,11 @@ module Addressable
|
|
|
481
588
|
# ExampleProcessor
|
|
482
589
|
# ).to_str
|
|
483
590
|
# #=> Addressable::Template::InvalidTemplateValueError
|
|
484
|
-
def expand(mapping, processor=nil)
|
|
591
|
+
def expand(mapping, processor=nil, normalize_values=true)
|
|
485
592
|
result = self.pattern.dup
|
|
486
593
|
mapping = normalize_keys(mapping)
|
|
487
594
|
result.gsub!( EXPRESSION ) do |capture|
|
|
488
|
-
transform_capture(mapping, capture, processor)
|
|
595
|
+
transform_capture(mapping, capture, processor, normalize_values)
|
|
489
596
|
end
|
|
490
597
|
return Addressable::URI.parse(result)
|
|
491
598
|
end
|
|
@@ -501,6 +608,7 @@ module Addressable
|
|
|
501
608
|
@variables ||= ordered_variable_defaults.map { |var, val| var }.uniq
|
|
502
609
|
end
|
|
503
610
|
alias_method :keys, :variables
|
|
611
|
+
alias_method :names, :variables
|
|
504
612
|
|
|
505
613
|
##
|
|
506
614
|
# Returns a mapping of variables to their default values specified
|
|
@@ -512,17 +620,49 @@ module Addressable
|
|
|
512
620
|
Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten]
|
|
513
621
|
end
|
|
514
622
|
|
|
623
|
+
##
|
|
624
|
+
# Coerces a template into a `Regexp` object. This regular expression will
|
|
625
|
+
# behave very similarly to the actual template, and should match the same
|
|
626
|
+
# URI values, but it cannot fully handle, for example, values that would
|
|
627
|
+
# extract to an `Array`.
|
|
628
|
+
#
|
|
629
|
+
# @return [Regexp] A regular expression which should match the template.
|
|
630
|
+
def to_regexp
|
|
631
|
+
_, source = parse_template_pattern(pattern)
|
|
632
|
+
Regexp.new(source)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
##
|
|
636
|
+
# Returns the source of the coerced `Regexp`.
|
|
637
|
+
#
|
|
638
|
+
# @return [String] The source of the `Regexp` given by {#to_regexp}.
|
|
639
|
+
#
|
|
640
|
+
# @api private
|
|
641
|
+
def source
|
|
642
|
+
self.to_regexp.source
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
##
|
|
646
|
+
# Returns the named captures of the coerced `Regexp`.
|
|
647
|
+
#
|
|
648
|
+
# @return [Hash] The named captures of the `Regexp` given by {#to_regexp}.
|
|
649
|
+
#
|
|
650
|
+
# @api private
|
|
651
|
+
def named_captures
|
|
652
|
+
self.to_regexp.named_captures
|
|
653
|
+
end
|
|
654
|
+
|
|
515
655
|
private
|
|
516
656
|
def ordered_variable_defaults
|
|
517
|
-
@ordered_variable_defaults ||=
|
|
657
|
+
@ordered_variable_defaults ||= begin
|
|
518
658
|
expansions, _ = parse_template_pattern(pattern)
|
|
519
|
-
expansions.
|
|
659
|
+
expansions.flat_map do |capture|
|
|
520
660
|
_, _, varlist = *capture.match(EXPRESSION)
|
|
521
661
|
varlist.split(',').map do |varspec|
|
|
522
662
|
varspec[VARSPEC, 1]
|
|
523
663
|
end
|
|
524
|
-
end
|
|
525
|
-
|
|
664
|
+
end
|
|
665
|
+
end
|
|
526
666
|
end
|
|
527
667
|
|
|
528
668
|
|
|
@@ -535,6 +675,8 @@ module Addressable
|
|
|
535
675
|
# The expression to expand
|
|
536
676
|
# @param [#validate, #transform] processor
|
|
537
677
|
# An optional processor object may be supplied.
|
|
678
|
+
# @param [Boolean] normalize_values
|
|
679
|
+
# Optional flag to enable/disable unicode normalization. Default: true
|
|
538
680
|
#
|
|
539
681
|
# The object should respond to either the <tt>validate</tt> or
|
|
540
682
|
# <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
|
|
@@ -549,21 +691,36 @@ module Addressable
|
|
|
549
691
|
# after sending the value to the transform method.
|
|
550
692
|
#
|
|
551
693
|
# @return [String] The expanded expression
|
|
552
|
-
def transform_partial_capture(mapping, capture, processor = nil
|
|
694
|
+
def transform_partial_capture(mapping, capture, processor = nil,
|
|
695
|
+
normalize_values = true)
|
|
553
696
|
_, operator, varlist = *capture.match(EXPRESSION)
|
|
554
|
-
|
|
555
|
-
varlist.split(
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
697
|
+
|
|
698
|
+
vars = varlist.split(",")
|
|
699
|
+
|
|
700
|
+
if operator == "?"
|
|
701
|
+
# partial expansion of form style query variables sometimes requires a
|
|
702
|
+
# slight reordering of the variables to produce a valid url.
|
|
703
|
+
first_to_expand = vars.find { |varspec|
|
|
704
|
+
_, name, _ = *varspec.match(VARSPEC)
|
|
705
|
+
mapping.key?(name) && !mapping[name].nil?
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
vars.
|
|
712
|
+
inject("".dup) do |acc, varspec|
|
|
713
|
+
_, name, _ = *varspec.match(VARSPEC)
|
|
714
|
+
next_val = if mapping.key? name
|
|
715
|
+
transform_capture(mapping, "{#{operator}#{varspec}}",
|
|
716
|
+
processor, normalize_values)
|
|
717
|
+
else
|
|
718
|
+
"{#{operator}#{varspec}}"
|
|
719
|
+
end
|
|
720
|
+
# If we've already expanded at least one '?' operator with non-empty
|
|
721
|
+
# value, change to '&'
|
|
722
|
+
operator = "&" if (operator == "?") && (next_val != "")
|
|
723
|
+
acc << next_val
|
|
567
724
|
end
|
|
568
725
|
end
|
|
569
726
|
|
|
@@ -576,6 +733,9 @@ module Addressable
|
|
|
576
733
|
# The expression to replace
|
|
577
734
|
# @param [#validate, #transform] processor
|
|
578
735
|
# An optional processor object may be supplied.
|
|
736
|
+
# @param [Boolean] normalize_values
|
|
737
|
+
# Optional flag to enable/disable unicode normalization. Default: true
|
|
738
|
+
#
|
|
579
739
|
#
|
|
580
740
|
# The object should respond to either the <tt>validate</tt> or
|
|
581
741
|
# <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
|
|
@@ -590,7 +750,8 @@ module Addressable
|
|
|
590
750
|
# after sending the value to the transform method.
|
|
591
751
|
#
|
|
592
752
|
# @return [String] The expanded expression
|
|
593
|
-
def transform_capture(mapping, capture, processor=nil
|
|
753
|
+
def transform_capture(mapping, capture, processor=nil,
|
|
754
|
+
normalize_values=true)
|
|
594
755
|
_, operator, varlist = *capture.match(EXPRESSION)
|
|
595
756
|
return_value = varlist.split(',').inject([]) do |acc, varspec|
|
|
596
757
|
_, name, modifier = *varspec.match(VARSPEC)
|
|
@@ -610,7 +771,7 @@ module Addressable
|
|
|
610
771
|
"Can't convert #{value.class} into String or Array."
|
|
611
772
|
end
|
|
612
773
|
|
|
613
|
-
value = normalize_value(value)
|
|
774
|
+
value = normalize_value(value) if normalize_values
|
|
614
775
|
|
|
615
776
|
if processor == nil || !processor.respond_to?(:transform)
|
|
616
777
|
# Handle percent escaping
|
|
@@ -673,7 +834,9 @@ module Addressable
|
|
|
673
834
|
end
|
|
674
835
|
if processor.respond_to?(:transform)
|
|
675
836
|
transformed_value = processor.transform(name, value)
|
|
676
|
-
|
|
837
|
+
if normalize_values
|
|
838
|
+
transformed_value = normalize_value(transformed_value)
|
|
839
|
+
end
|
|
677
840
|
end
|
|
678
841
|
end
|
|
679
842
|
acc << [name, transformed_value]
|
|
@@ -729,25 +892,24 @@ module Addressable
|
|
|
729
892
|
# operator.
|
|
730
893
|
#
|
|
731
894
|
# @param [Hash, Array, String] value
|
|
732
|
-
# Normalizes keys and values with
|
|
895
|
+
# Normalizes unicode keys and values with String#unicode_normalize (NFC)
|
|
733
896
|
#
|
|
734
897
|
# @return [Hash, Array, String] The normalized values
|
|
735
898
|
def normalize_value(value)
|
|
736
|
-
unless value.is_a?(Hash)
|
|
737
|
-
value = value.respond_to?(:to_ary) ? value.to_ary : value.to_str
|
|
738
|
-
end
|
|
739
|
-
|
|
740
899
|
# Handle unicode normalization
|
|
741
|
-
if value.
|
|
742
|
-
value.map! { |val|
|
|
900
|
+
if value.respond_to?(:to_ary)
|
|
901
|
+
value.to_ary.map! { |val| normalize_value(val) }
|
|
743
902
|
elsif value.kind_of?(Hash)
|
|
744
903
|
value = value.inject({}) { |acc, (k, v)|
|
|
745
|
-
acc[
|
|
746
|
-
Addressable::IDNA.unicode_normalize_kc(v)
|
|
904
|
+
acc[normalize_value(k)] = normalize_value(v)
|
|
747
905
|
acc
|
|
748
906
|
}
|
|
749
907
|
else
|
|
750
|
-
value =
|
|
908
|
+
value = value.to_s if !value.kind_of?(String)
|
|
909
|
+
if value.encoding != Encoding::UTF_8
|
|
910
|
+
value = value.dup.force_encoding(Encoding::UTF_8)
|
|
911
|
+
end
|
|
912
|
+
value = value.unicode_normalize(:nfc)
|
|
751
913
|
end
|
|
752
914
|
value
|
|
753
915
|
end
|
|
@@ -775,15 +937,35 @@ module Addressable
|
|
|
775
937
|
end
|
|
776
938
|
end
|
|
777
939
|
|
|
940
|
+
##
|
|
941
|
+
# Generates the <tt>Regexp</tt> that parses a template pattern. Memoizes the
|
|
942
|
+
# value if template processor not set (processors may not be deterministic)
|
|
943
|
+
#
|
|
944
|
+
# @param [String] pattern The URI template pattern.
|
|
945
|
+
# @param [#match] processor The template processor to use.
|
|
946
|
+
#
|
|
947
|
+
# @return [Array, Regexp]
|
|
948
|
+
# An array of expansion variables nad a regular expression which may be
|
|
949
|
+
# used to parse a template pattern
|
|
950
|
+
def parse_template_pattern(pattern, processor = nil)
|
|
951
|
+
if processor.nil? && pattern == @pattern
|
|
952
|
+
@cached_template_parse ||=
|
|
953
|
+
parse_new_template_pattern(pattern, processor)
|
|
954
|
+
else
|
|
955
|
+
parse_new_template_pattern(pattern, processor)
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
|
|
778
959
|
##
|
|
779
960
|
# Generates the <tt>Regexp</tt> that parses a template pattern.
|
|
780
961
|
#
|
|
781
962
|
# @param [String] pattern The URI template pattern.
|
|
782
963
|
# @param [#match] processor The template processor to use.
|
|
783
964
|
#
|
|
784
|
-
# @return [Regexp]
|
|
785
|
-
#
|
|
786
|
-
|
|
965
|
+
# @return [Array, Regexp]
|
|
966
|
+
# An array of expansion variables nad a regular expression which may be
|
|
967
|
+
# used to parse a template pattern
|
|
968
|
+
def parse_new_template_pattern(pattern, processor = nil)
|
|
787
969
|
# Escape the pattern. The two gsubs restore the escaped curly braces
|
|
788
970
|
# back to their original form. Basically, escape everything that isn't
|
|
789
971
|
# within an expansion.
|
|
@@ -803,10 +985,12 @@ module Addressable
|
|
|
803
985
|
_, operator, varlist = *expansion.match(EXPRESSION)
|
|
804
986
|
leader = Regexp.escape(LEADERS.fetch(operator, ''))
|
|
805
987
|
joiner = Regexp.escape(JOINERS.fetch(operator, ','))
|
|
806
|
-
|
|
988
|
+
combined = varlist.split(',').map do |varspec|
|
|
807
989
|
_, name, modifier = *varspec.match(VARSPEC)
|
|
808
|
-
|
|
809
|
-
|
|
990
|
+
|
|
991
|
+
result = processor && processor.respond_to?(:match) ? processor.match(name) : nil
|
|
992
|
+
if result
|
|
993
|
+
"(?<#{name}>#{ result })"
|
|
810
994
|
else
|
|
811
995
|
group = case operator
|
|
812
996
|
when '+'
|
|
@@ -827,16 +1011,17 @@ module Addressable
|
|
|
827
1011
|
"#{ UNRESERVED }*?"
|
|
828
1012
|
end
|
|
829
1013
|
if modifier == '*'
|
|
830
|
-
"(
|
|
1014
|
+
"(?<#{name}>#{group}(?:#{joiner}?#{group})*)?"
|
|
831
1015
|
else
|
|
832
|
-
"(
|
|
1016
|
+
"(?<#{name}>#{group})?"
|
|
833
1017
|
end
|
|
834
1018
|
end
|
|
835
1019
|
end.join("#{joiner}?")
|
|
1020
|
+
"(?:|#{leader}#{combined})"
|
|
836
1021
|
end
|
|
837
1022
|
|
|
838
1023
|
# Ensure that the regular expression matches the whole URI.
|
|
839
|
-
regexp_string = "
|
|
1024
|
+
regexp_string = "\\A#{regexp_string}\\z"
|
|
840
1025
|
return expansions, Regexp.new(regexp_string)
|
|
841
1026
|
end
|
|
842
1027
|
|