addressable 2.3.6 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # encoding:utf-8
2
4
  #--
3
- # Copyright (C) 2006-2013 Bob Aman
5
+ # Copyright (C) Bob Aman
4
6
  #
5
7
  # Licensed under the Apache License, Version 2.0 (the "License");
6
8
  # you may not use this file except in compliance with the License.
@@ -35,7 +37,7 @@ module Addressable
35
37
  Addressable::URI::CharacterClasses::DIGIT + '_'
36
38
 
37
39
  var_char =
38
- "(?:(?:[#{variable_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)"
40
+ "(?>(?:[#{variable_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)"
39
41
  RESERVED =
40
42
  "(?:[#{anything}]|%[a-fA-F0-9][a-fA-F0-9])"
41
43
  UNRESERVED =
@@ -234,7 +236,18 @@ module Addressable
234
236
  if !pattern.respond_to?(:to_str)
235
237
  raise TypeError, "Can't convert #{pattern.class} into String."
236
238
  end
237
- @pattern = pattern.to_str.freeze
239
+ @pattern = pattern.to_str.dup.freeze
240
+ end
241
+
242
+ ##
243
+ # Freeze URI, initializing instance variables.
244
+ #
245
+ # @return [Addressable::URI] The frozen URI object.
246
+ def freeze
247
+ self.variables
248
+ self.variable_defaults
249
+ self.named_captures
250
+ super
238
251
  end
239
252
 
240
253
  ##
@@ -399,7 +412,7 @@ module Addressable
399
412
  # match.captures
400
413
  # #=> ["a", ["b", "c"]]
401
414
  def match(uri, processor=nil)
402
- uri = Addressable::URI.parse(uri)
415
+ uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)
403
416
  mapping = {}
404
417
 
405
418
  # First, we need to process the pattern, and extract the values.
@@ -477,6 +490,8 @@ module Addressable
477
490
  # @param [Hash] mapping The mapping that corresponds to the pattern.
478
491
  # @param [#validate, #transform] processor
479
492
  # An optional processor object may be supplied.
493
+ # @param [Boolean] normalize_values
494
+ # Optional flag to enable/disable unicode normalization. Default: true
480
495
  #
481
496
  # The object should respond to either the <tt>validate</tt> or
482
497
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -507,11 +522,11 @@ module Addressable
507
522
  # "http://example.com/{?one,two,three}/"
508
523
  # ).partial_expand({"one" => "1", "three" => 3}).pattern
509
524
  # #=> "http://example.com/?one=1{&two}&three=3"
510
- def partial_expand(mapping, processor=nil)
525
+ def partial_expand(mapping, processor=nil, normalize_values=true)
511
526
  result = self.pattern.dup
512
527
  mapping = normalize_keys(mapping)
513
528
  result.gsub!( EXPRESSION ) do |capture|
514
- transform_partial_capture(mapping, capture, processor)
529
+ transform_partial_capture(mapping, capture, processor, normalize_values)
515
530
  end
516
531
  return Addressable::Template.new(result)
517
532
  end
@@ -522,6 +537,8 @@ module Addressable
522
537
  # @param [Hash] mapping The mapping that corresponds to the pattern.
523
538
  # @param [#validate, #transform] processor
524
539
  # An optional processor object may be supplied.
540
+ # @param [Boolean] normalize_values
541
+ # Optional flag to enable/disable unicode normalization. Default: true
525
542
  #
526
543
  # The object should respond to either the <tt>validate</tt> or
527
544
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -572,11 +589,11 @@ module Addressable
572
589
  # ExampleProcessor
573
590
  # ).to_str
574
591
  # #=> Addressable::Template::InvalidTemplateValueError
575
- def expand(mapping, processor=nil)
592
+ def expand(mapping, processor=nil, normalize_values=true)
576
593
  result = self.pattern.dup
577
594
  mapping = normalize_keys(mapping)
578
595
  result.gsub!( EXPRESSION ) do |capture|
579
- transform_capture(mapping, capture, processor)
596
+ transform_capture(mapping, capture, processor, normalize_values)
580
597
  end
581
598
  return Addressable::URI.parse(result)
582
599
  end
@@ -592,6 +609,7 @@ module Addressable
592
609
  @variables ||= ordered_variable_defaults.map { |var, val| var }.uniq
593
610
  end
594
611
  alias_method :keys, :variables
612
+ alias_method :names, :variables
595
613
 
596
614
  ##
597
615
  # Returns a mapping of variables to their default values specified
@@ -603,9 +621,41 @@ module Addressable
603
621
  Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten]
604
622
  end
605
623
 
624
+ ##
625
+ # Coerces a template into a `Regexp` object. This regular expression will
626
+ # behave very similarly to the actual template, and should match the same
627
+ # URI values, but it cannot fully handle, for example, values that would
628
+ # extract to an `Array`.
629
+ #
630
+ # @return [Regexp] A regular expression which should match the template.
631
+ def to_regexp
632
+ _, source = parse_template_pattern(pattern)
633
+ Regexp.new(source)
634
+ end
635
+
636
+ ##
637
+ # Returns the source of the coerced `Regexp`.
638
+ #
639
+ # @return [String] The source of the `Regexp` given by {#to_regexp}.
640
+ #
641
+ # @api private
642
+ def source
643
+ self.to_regexp.source
644
+ end
645
+
646
+ ##
647
+ # Returns the named captures of the coerced `Regexp`.
648
+ #
649
+ # @return [Hash] The named captures of the `Regexp` given by {#to_regexp}.
650
+ #
651
+ # @api private
652
+ def named_captures
653
+ self.to_regexp.named_captures
654
+ end
655
+
606
656
  private
607
657
  def ordered_variable_defaults
608
- @ordered_variable_defaults ||= (
658
+ @ordered_variable_defaults ||= begin
609
659
  expansions, _ = parse_template_pattern(pattern)
610
660
  expansions.map do |capture|
611
661
  _, _, varlist = *capture.match(EXPRESSION)
@@ -613,7 +663,7 @@ module Addressable
613
663
  varspec[VARSPEC, 1]
614
664
  end
615
665
  end.flatten
616
- )
666
+ end
617
667
  end
618
668
 
619
669
 
@@ -626,6 +676,8 @@ module Addressable
626
676
  # The expression to expand
627
677
  # @param [#validate, #transform] processor
628
678
  # An optional processor object may be supplied.
679
+ # @param [Boolean] normalize_values
680
+ # Optional flag to enable/disable unicode normalization. Default: true
629
681
  #
630
682
  # The object should respond to either the <tt>validate</tt> or
631
683
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -640,21 +692,36 @@ module Addressable
640
692
  # after sending the value to the transform method.
641
693
  #
642
694
  # @return [String] The expanded expression
643
- def transform_partial_capture(mapping, capture, processor = nil)
695
+ def transform_partial_capture(mapping, capture, processor = nil,
696
+ normalize_values = true)
644
697
  _, operator, varlist = *capture.match(EXPRESSION)
645
- is_first = true
646
- varlist.split(',').inject('') do |acc, varspec|
647
- _, name, _ = *varspec.match(VARSPEC)
648
- value = mapping[name]
649
- if value
650
- operator = '&' if !is_first && operator == '?'
651
- acc << transform_capture(mapping, "{#{operator}#{varspec}}", processor)
652
- else
653
- operator = '&' if !is_first && operator == '?'
654
- acc << "{#{operator}#{varspec}}"
655
- end
656
- is_first = false
657
- acc
698
+
699
+ vars = varlist.split(",")
700
+
701
+ if operator == "?"
702
+ # partial expansion of form style query variables sometimes requires a
703
+ # slight reordering of the variables to produce a valid url.
704
+ first_to_expand = vars.find { |varspec|
705
+ _, name, _ = *varspec.match(VARSPEC)
706
+ mapping.key?(name) && !mapping[name].nil?
707
+ }
708
+
709
+ vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand
710
+ end
711
+
712
+ vars.
713
+ inject("".dup) do |acc, varspec|
714
+ _, name, _ = *varspec.match(VARSPEC)
715
+ next_val = if mapping.key? name
716
+ transform_capture(mapping, "{#{operator}#{varspec}}",
717
+ processor, normalize_values)
718
+ else
719
+ "{#{operator}#{varspec}}"
720
+ end
721
+ # If we've already expanded at least one '?' operator with non-empty
722
+ # value, change to '&'
723
+ operator = "&" if (operator == "?") && (next_val != "")
724
+ acc << next_val
658
725
  end
659
726
  end
660
727
 
@@ -667,6 +734,9 @@ module Addressable
667
734
  # The expression to replace
668
735
  # @param [#validate, #transform] processor
669
736
  # An optional processor object may be supplied.
737
+ # @param [Boolean] normalize_values
738
+ # Optional flag to enable/disable unicode normalization. Default: true
739
+ #
670
740
  #
671
741
  # The object should respond to either the <tt>validate</tt> or
672
742
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -681,7 +751,8 @@ module Addressable
681
751
  # after sending the value to the transform method.
682
752
  #
683
753
  # @return [String] The expanded expression
684
- def transform_capture(mapping, capture, processor=nil)
754
+ def transform_capture(mapping, capture, processor=nil,
755
+ normalize_values=true)
685
756
  _, operator, varlist = *capture.match(EXPRESSION)
686
757
  return_value = varlist.split(',').inject([]) do |acc, varspec|
687
758
  _, name, modifier = *varspec.match(VARSPEC)
@@ -701,7 +772,7 @@ module Addressable
701
772
  "Can't convert #{value.class} into String or Array."
702
773
  end
703
774
 
704
- value = normalize_value(value)
775
+ value = normalize_value(value) if normalize_values
705
776
 
706
777
  if processor == nil || !processor.respond_to?(:transform)
707
778
  # Handle percent escaping
@@ -764,7 +835,9 @@ module Addressable
764
835
  end
765
836
  if processor.respond_to?(:transform)
766
837
  transformed_value = processor.transform(name, value)
767
- transformed_value = normalize_value(transformed_value)
838
+ if normalize_values
839
+ transformed_value = normalize_value(transformed_value)
840
+ end
768
841
  end
769
842
  end
770
843
  acc << [name, transformed_value]
@@ -866,15 +939,35 @@ module Addressable
866
939
  end
867
940
  end
868
941
 
942
+ ##
943
+ # Generates the <tt>Regexp</tt> that parses a template pattern. Memoizes the
944
+ # value if template processor not set (processors may not be deterministic)
945
+ #
946
+ # @param [String] pattern The URI template pattern.
947
+ # @param [#match] processor The template processor to use.
948
+ #
949
+ # @return [Array, Regexp]
950
+ # An array of expansion variables nad a regular expression which may be
951
+ # used to parse a template pattern
952
+ def parse_template_pattern(pattern, processor = nil)
953
+ if processor.nil? && pattern == @pattern
954
+ @cached_template_parse ||=
955
+ parse_new_template_pattern(pattern, processor)
956
+ else
957
+ parse_new_template_pattern(pattern, processor)
958
+ end
959
+ end
960
+
869
961
  ##
870
962
  # Generates the <tt>Regexp</tt> that parses a template pattern.
871
963
  #
872
964
  # @param [String] pattern The URI template pattern.
873
965
  # @param [#match] processor The template processor to use.
874
966
  #
875
- # @return [Regexp]
876
- # A regular expression which may be used to parse a template pattern.
877
- def parse_template_pattern(pattern, processor=nil)
967
+ # @return [Array, Regexp]
968
+ # An array of expansion variables nad a regular expression which may be
969
+ # used to parse a template pattern
970
+ def parse_new_template_pattern(pattern, processor = nil)
878
971
  # Escape the pattern. The two gsubs restore the escaped curly braces
879
972
  # back to their original form. Basically, escape everything that isn't
880
973
  # within an expansion.
@@ -899,7 +992,7 @@ module Addressable
899
992
 
900
993
  result = processor && processor.respond_to?(:match) ? processor.match(name) : nil
901
994
  if result
902
- "(#{ result })"
995
+ "(?<#{name}>#{ result })"
903
996
  else
904
997
  group = case operator
905
998
  when '+'
@@ -920,9 +1013,9 @@ module Addressable
920
1013
  "#{ UNRESERVED }*?"
921
1014
  end
922
1015
  if modifier == '*'
923
- "(#{group}(?:#{joiner}?#{group})*)?"
1016
+ "(?<#{name}>#{group}(?:#{joiner}?#{group})*)?"
924
1017
  else
925
- "(#{group})?"
1018
+ "(?<#{name}>#{group})?"
926
1019
  end
927
1020
  end
928
1021
  end.join("#{joiner}?")