@@ -14,12 +14,33 @@ module URL
1414 class Reference < Relative
1515 include Comparable
1616
17- # Generate a reference from a path and user parameters. The path may contain a `#fragment` or `?query=parameters`.
18- def self . parse ( value , parameters = nil )
19- base , fragment = value . split ( "#" , 2 )
20- path , query = base . split ( "?" , 2 )
21-
22- self . new ( path , query , fragment , parameters )
17+ def self . []( value , parameters = nil )
18+ case value
19+ when String
20+ if match = value . match ( PATTERN )
21+ path = match [ :path ]
22+ query = match [ :query ]
23+ fragment = match [ :fragment ]
24+
25+ # Unescape path and fragment for user-friendly internal storage
26+ # Query strings are kept as-is since they contain = and & syntax
27+ path = Encoding . unescape ( path ) if path && !path . empty?
28+ fragment = Encoding . unescape ( fragment ) if fragment
29+
30+ self . new ( path , query , fragment , parameters )
31+ else
32+ raise ArgumentError , "Invalid URL (contains whitespace or control characters): #{ value . inspect } "
33+ end
34+ when Relative
35+ self . new ( value . path , value . query , value . fragment , parameters )
36+ when nil
37+ nil
38+ else
39+ raise ArgumentError , "Cannot coerce #{ value . inspect } to Reference!"
40+ end
41+ end # Generate a reference from a path and user parameters. The path may contain a `#fragment` or `?query=parameters`.
42+ def self . parse ( value = "/" , parameters = nil )
43+ self . []( value , parameters )
2344 end
2445
2546 # Initialize the reference.
@@ -59,18 +80,6 @@ def <=> other
5980 to_ary <=> other . to_ary
6081 end
6182
62- # Type-cast a reference.
63- #
64- # @parameter reference [Reference | String] The reference to type-cast.
65- # @returns [Reference] The type-casted reference.
66- def self . [] reference
67- if reference . is_a? self
68- return reference
69- else
70- return self . parse ( reference )
71- end
72- end
73-
7483 # @returns [Boolean] Whether the reference has parameters.
7584 def parameters?
7685 @parameters and !@parameters . empty?
@@ -108,14 +117,22 @@ def fragment?
108117 end
109118
110119 # Append the reference to the given buffer.
111- private def append_query ( buffer = String . new )
120+ # Encodes the path and fragment which are stored unescaped internally.
121+ # Query strings are passed through as-is (they contain = and & which are valid syntax).
122+ def append ( buffer = String . new )
123+ buffer << Encoding . escape_path ( @path )
124+
112125 if @query and !@query . empty?
113126 buffer << "?" << @query
114127 buffer << "&" << Encoding . encode ( @parameters ) if parameters?
115128 elsif parameters?
116129 buffer << "?" << Encoding . encode ( @parameters )
117130 end
118131
132+ if @fragment and !@fragment . empty?
133+ buffer << "#" << Encoding . escape_fragment ( @fragment )
134+ end
135+
119136 return buffer
120137 end
121138
@@ -143,7 +160,7 @@ def base
143160 # @parameter fragment [String] Set the fragment to this value.
144161 # @parameter pop [Boolean] If the path contains a trailing filename, pop the last component of the path before appending the new path.
145162 # @parameter merge [Boolean] If the parameters are specified, merge them with the existing parameters, otherwise replace them (including query string).
146- def with ( path : nil , parameters : false , fragment : @fragment , pop : false , merge : true )
163+ def with ( path : nil , query : @query , fragment : @fragment , parameters : false , pop : false , merge : true )
147164 if merge
148165 # Merge mode: combine new parameters with existing, keep query:
149166 # parameters = (@parameters || {}).merge(parameters || {})
@@ -156,26 +173,18 @@ def with(path: nil, parameters: false, fragment: @fragment, pop: false, merge: t
156173 elsif !parameters
157174 parameters = @parameters
158175 end
159-
160- query = @query
161176 else
162177 # Replace mode: use new parameters if provided, clear query when replacing:
163178 if parameters == false
164179 # No new parameters provided, keep existing:
165180 parameters = @parameters
166- query = @query
167181 else
168182 # New parameters provided, replace and clear query:
169- # parameters = parameters
170183 query = nil
171184 end
172185 end
173186
174- if path
175- path = Path . expand ( @path , path , pop )
176- else
177- path = @path
178- end
187+ path = Path . expand ( @path , path , pop )
179188
180189 self . class . new ( path , query , fragment , parameters )
181190 end
0 commit comments