addressable 2.4.0 → 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-2015 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 =
@@ -410,7 +412,7 @@ module Addressable
410
412
  # match.captures
411
413
  # #=> ["a", ["b", "c"]]
412
414
  def match(uri, processor=nil)
413
- uri = Addressable::URI.parse(uri)
415
+ uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)
414
416
  mapping = {}
415
417
 
416
418
  # First, we need to process the pattern, and extract the values.
@@ -488,6 +490,8 @@ module Addressable
488
490
  # @param [Hash] mapping The mapping that corresponds to the pattern.
489
491
  # @param [#validate, #transform] processor
490
492
  # An optional processor object may be supplied.
493
+ # @param [Boolean] normalize_values
494
+ # Optional flag to enable/disable unicode normalization. Default: true
491
495
  #
492
496
  # The object should respond to either the <tt>validate</tt> or
493
497
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -518,11 +522,11 @@ module Addressable
518
522
  # "http://example.com/{?one,two,three}/"
519
523
  # ).partial_expand({"one" => "1", "three" => 3}).pattern
520
524
  # #=> "http://example.com/?one=1{&two}&three=3"
521
- def partial_expand(mapping, processor=nil)
525
+ def partial_expand(mapping, processor=nil, normalize_values=true)
522
526
  result = self.pattern.dup
523
527
  mapping = normalize_keys(mapping)
524
528
  result.gsub!( EXPRESSION ) do |capture|
525
- transform_partial_capture(mapping, capture, processor)
529
+ transform_partial_capture(mapping, capture, processor, normalize_values)
526
530
  end
527
531
  return Addressable::Template.new(result)
528
532
  end
@@ -533,6 +537,8 @@ module Addressable
533
537
  # @param [Hash] mapping The mapping that corresponds to the pattern.
534
538
  # @param [#validate, #transform] processor
535
539
  # An optional processor object may be supplied.
540
+ # @param [Boolean] normalize_values
541
+ # Optional flag to enable/disable unicode normalization. Default: true
536
542
  #
537
543
  # The object should respond to either the <tt>validate</tt> or
538
544
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -583,11 +589,11 @@ module Addressable
583
589
  # ExampleProcessor
584
590
  # ).to_str
585
591
  # #=> Addressable::Template::InvalidTemplateValueError
586
- def expand(mapping, processor=nil)
592
+ def expand(mapping, processor=nil, normalize_values=true)
587
593
  result = self.pattern.dup
588
594
  mapping = normalize_keys(mapping)
589
595
  result.gsub!( EXPRESSION ) do |capture|
590
- transform_capture(mapping, capture, processor)
596
+ transform_capture(mapping, capture, processor, normalize_values)
591
597
  end
592
598
  return Addressable::URI.parse(result)
593
599
  end
@@ -647,40 +653,6 @@ module Addressable
647
653
  self.to_regexp.named_captures
648
654
  end
649
655
 
650
- ##
651
- # Generates a route result for a given set of parameters.
652
- # Should only be used by rack-mount.
653
- #
654
- # @param params [Hash] The set of parameters used to expand the template.
655
- # @param recall [Hash] Default parameters used to expand the template.
656
- # @param options [Hash] Either a `:processor` or a `:parameterize` block.
657
- #
658
- # @api private
659
- def generate(params={}, recall={}, options={})
660
- merged = recall.merge(params)
661
- if options[:processor]
662
- processor = options[:processor]
663
- elsif options[:parameterize]
664
- # TODO: This is sending me into fits trying to shoe-horn this into
665
- # the existing API. I think I've got this backwards and processors
666
- # should be a set of 4 optional blocks named :validate, :transform,
667
- # :match, and :restore. Having to use a singleton here is a huge
668
- # code smell.
669
- processor = Object.new
670
- class <<processor
671
- attr_accessor :block
672
- def transform(name, value)
673
- block.call(name, value)
674
- end
675
- end
676
- processor.block = options[:parameterize]
677
- else
678
- processor = nil
679
- end
680
- result = self.expand(merged, processor)
681
- result.to_s if result
682
- end
683
-
684
656
  private
685
657
  def ordered_variable_defaults
686
658
  @ordered_variable_defaults ||= begin
@@ -704,6 +676,8 @@ module Addressable
704
676
  # The expression to expand
705
677
  # @param [#validate, #transform] processor
706
678
  # An optional processor object may be supplied.
679
+ # @param [Boolean] normalize_values
680
+ # Optional flag to enable/disable unicode normalization. Default: true
707
681
  #
708
682
  # The object should respond to either the <tt>validate</tt> or
709
683
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -718,56 +692,36 @@ module Addressable
718
692
  # after sending the value to the transform method.
719
693
  #
720
694
  # @return [String] The expanded expression
721
- def transform_partial_capture(mapping, capture, processor = nil)
695
+ def transform_partial_capture(mapping, capture, processor = nil,
696
+ normalize_values = true)
722
697
  _, operator, varlist = *capture.match(EXPRESSION)
723
698
 
724
- vars = varlist.split(',')
699
+ vars = varlist.split(",")
725
700
 
726
- if '?' == operator
701
+ if operator == "?"
727
702
  # partial expansion of form style query variables sometimes requires a
728
703
  # slight reordering of the variables to produce a valid url.
729
704
  first_to_expand = vars.find { |varspec|
730
705
  _, name, _ = *varspec.match(VARSPEC)
731
- mapping.key? name
706
+ mapping.key?(name) && !mapping[name].nil?
732
707
  }
733
708
 
734
709
  vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand
735
710
  end
736
711
 
737
- vars
738
- .zip(operator_sequence(operator).take(vars.length))
739
- .reduce("") do |acc, (varspec, op)|
712
+ vars.
713
+ inject("".dup) do |acc, varspec|
740
714
  _, name, _ = *varspec.match(VARSPEC)
741
-
742
- acc << if mapping.key? name
743
- transform_capture(mapping, "{#{op}#{varspec}}", processor)
744
- else
745
- "{#{op}#{varspec}}"
746
- end
747
- end
748
- end
749
-
750
- ##
751
- # Creates a lazy Enumerator of the operators that should be used to expand
752
- # variables in a varlist starting with `operator`. For example, an operator
753
- # `"?"` results in the sequence `"?","&","&"...`
754
- #
755
- # @param [String] operator from which to generate a sequence
756
- #
757
- # @return [Enumerator] sequence of operators
758
- def operator_sequence(operator)
759
- rest_operator = if "?" == operator
760
- "&"
761
- else
762
- operator
763
- end
764
- head_operator = operator
765
-
766
- Enumerator.new do |y|
767
- y << head_operator.to_s
768
- while true
769
- y << rest_operator.to_s
770
- end
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
771
725
  end
772
726
  end
773
727
 
@@ -780,6 +734,9 @@ module Addressable
780
734
  # The expression to replace
781
735
  # @param [#validate, #transform] processor
782
736
  # An optional processor object may be supplied.
737
+ # @param [Boolean] normalize_values
738
+ # Optional flag to enable/disable unicode normalization. Default: true
739
+ #
783
740
  #
784
741
  # The object should respond to either the <tt>validate</tt> or
785
742
  # <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
@@ -794,7 +751,8 @@ module Addressable
794
751
  # after sending the value to the transform method.
795
752
  #
796
753
  # @return [String] The expanded expression
797
- def transform_capture(mapping, capture, processor=nil)
754
+ def transform_capture(mapping, capture, processor=nil,
755
+ normalize_values=true)
798
756
  _, operator, varlist = *capture.match(EXPRESSION)
799
757
  return_value = varlist.split(',').inject([]) do |acc, varspec|
800
758
  _, name, modifier = *varspec.match(VARSPEC)
@@ -814,7 +772,7 @@ module Addressable
814
772
  "Can't convert #{value.class} into String or Array."
815
773
  end
816
774
 
817
- value = normalize_value(value)
775
+ value = normalize_value(value) if normalize_values
818
776
 
819
777
  if processor == nil || !processor.respond_to?(:transform)
820
778
  # Handle percent escaping
@@ -877,7 +835,9 @@ module Addressable
877
835
  end
878
836
  if processor.respond_to?(:transform)
879
837
  transformed_value = processor.transform(name, value)
880
- transformed_value = normalize_value(transformed_value)
838
+ if normalize_values
839
+ transformed_value = normalize_value(transformed_value)
840
+ end
881
841
  end
882
842
  end
883
843
  acc << [name, transformed_value]
@@ -979,15 +939,35 @@ module Addressable
979
939
  end
980
940
  end
981
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
+
982
961
  ##
983
962
  # Generates the <tt>Regexp</tt> that parses a template pattern.
984
963
  #
985
964
  # @param [String] pattern The URI template pattern.
986
965
  # @param [#match] processor The template processor to use.
987
966
  #
988
- # @return [Regexp]
989
- # A regular expression which may be used to parse a template pattern.
990
- 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)
991
971
  # Escape the pattern. The two gsubs restore the escaped curly braces
992
972
  # back to their original form. Basically, escape everything that isn't
993
973
  # within an expansion.