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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +76 -0
- data/Gemfile +18 -18
- data/README.md +37 -18
- data/Rakefile +8 -11
- data/addressable.gemspec +37 -0
- data/lib/addressable/idna/native.rb +24 -6
- data/lib/addressable/idna/pure.rb +67 -58
- data/lib/addressable/idna.rb +3 -1
- data/lib/addressable/template.rb +126 -33
- data/lib/addressable/uri.rb +416 -211
- data/lib/addressable/version.rb +5 -3
- data/lib/addressable.rb +4 -0
- data/spec/addressable/idna_spec.rb +110 -46
- data/spec/addressable/net_http_compat_spec.rb +4 -2
- data/spec/addressable/security_spec.rb +59 -0
- data/spec/addressable/template_spec.rb +297 -165
- data/spec/addressable/uri_spec.rb +2054 -1329
- data/spec/spec_helper.rb +28 -1
- data/tasks/clobber.rake +2 -0
- data/tasks/gem.rake +22 -16
- data/tasks/git.rake +3 -1
- data/tasks/metrics.rake +2 -0
- data/tasks/profile.rake +72 -0
- data/tasks/rspec.rake +10 -45
- data/tasks/yard.rake +2 -0
- metadata +44 -45
- data/tasks/rubyforge.rake +0 -73
- data/website/index.html +0 -110
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 =
|
@@ -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
|
-
|
646
|
-
varlist.split(
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
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
|
-
|
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
|
-
#
|
877
|
-
|
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
|
-
"(
|
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
|
-
"(
|
1016
|
+
"(?<#{name}>#{group}(?:#{joiner}?#{group})*)?"
|
924
1017
|
else
|
925
|
-
"(
|
1018
|
+
"(?<#{name}>#{group})?"
|
926
1019
|
end
|
927
1020
|
end
|
928
1021
|
end.join("#{joiner}?")
|