addressable 2.4.0 → 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 +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.
|