addressable 2.4.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +54 -0
- data/Gemfile +13 -12
- data/README.md +31 -15
- data/Rakefile +5 -3
- data/addressable.gemspec +23 -20
- data/lib/addressable/idna/native.rb +11 -5
- data/lib/addressable/idna/pure.rb +61 -55
- data/lib/addressable/idna.rb +3 -1
- data/lib/addressable/template.rb +64 -84
- data/lib/addressable/uri.rb +228 -96
- data/lib/addressable/version.rb +4 -2
- data/lib/addressable.rb +2 -0
- data/spec/addressable/idna_spec.rb +35 -3
- data/spec/addressable/net_http_compat_spec.rb +3 -1
- data/spec/addressable/security_spec.rb +3 -1
- data/spec/addressable/template_spec.rb +77 -3
- data/spec/addressable/uri_spec.rb +663 -203
- data/spec/spec_helper.rb +12 -0
- data/tasks/clobber.rake +2 -0
- data/tasks/gem.rake +9 -14
- data/tasks/git.rake +2 -0
- data/tasks/metrics.rake +2 -0
- data/tasks/profile.rake +72 -0
- data/tasks/rspec.rake +3 -1
- data/tasks/yard.rake +2 -0
- metadata +36 -11
- data/spec/addressable/rack_mount_compat_spec.rb +0 -104
data/lib/addressable/template.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# encoding:utf-8
|
2
4
|
#--
|
3
|
-
# Copyright (C)
|
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
|
-
"(
|
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
|
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
|
-
|
739
|
-
.reduce("") do |acc, (varspec, op)|
|
712
|
+
vars.
|
713
|
+
inject("".dup) do |acc, varspec|
|
740
714
|
_, name, _ = *varspec.match(VARSPEC)
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
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
|
-
|
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
|
-
#
|
990
|
-
|
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.
|