addressable 2.3.6 → 2.8.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.
@@ -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}?")