Class: Protocol::URL::Relative

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/protocol/url/relative.rb

Overview

Represents a relative URL, which does not include a scheme or authority.

Direct Known Subclasses

Absolute, Reference

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, query = nil, fragment = nil) ⇒ Relative

Initialize a new relative URL.



20
21
22
23
24
# File 'lib/protocol/url/relative.rb', line 20

def initialize(path, query = nil, fragment = nil)
	@path = Path[path]
	@query = query
	@fragment = fragment
end

Instance Attribute Details

#fragmentObject

Returns the value of attribute fragment.



52
53
54
# File 'lib/protocol/url/relative.rb', line 52

def fragment
  @fragment
end

#pathObject

Returns the value of attribute path.



39
40
41
# File 'lib/protocol/url/relative.rb', line 39

def path
  @path
end

#queryObject

Returns the value of attribute query.



49
50
51
# File 'lib/protocol/url/relative.rb', line 49

def query
  @query
end

#The fragment identifier.(fragmentidentifier.) ⇒ Object (readonly)



52
# File 'lib/protocol/url/relative.rb', line 52

attr_accessor :fragment

#The query string component.(querystringcomponent.) ⇒ Object (readonly)



49
# File 'lib/protocol/url/relative.rb', line 49

attr_accessor :query

Instance Method Details

#+(other) ⇒ Object

Combine this relative URL with another URL or path.

Examples:

Combine two relative paths.

base = Relative.new("/documents/reports/")
other = Relative.new("invoices/2024.pdf")
result = base + other
result.path.to_s  # => "/documents/reports/invoices/2024.pdf"

Navigate to parent directory.

base = Relative.new("/documents/reports/archive/")
other = Relative.new("../../summary.pdf")
result = base + other
result.path.to_s  # => "/documents/summary.pdf"


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/protocol/url/relative.rb', line 91

def +(other)
	case other
	when Absolute
		# Relative + Absolute: the absolute URL takes precedence
		# You can't apply relative navigation to an absolute URL
		other
	when Relative
		# Relative + Relative: merge paths directly
		self.class.new(
			@path.join(other.path),
			other.query,
			other.fragment
		)
	when String
		# Relative + String: parse and combine
		self + URL[other]
	else
		raise ArgumentError, "Cannot combine Relative URL with #{other.class}"
	end
end

#<=>(other) ⇒ Object

Compare this URL with another for sorting purposes.



201
202
203
# File 'lib/protocol/url/relative.rb', line 201

def <=>(other)
	to_ary <=> other.to_ary
end

#==(other) ⇒ Object

Check structural equality by comparing components.



209
210
211
# File 'lib/protocol/url/relative.rb', line 209

def ==(other)
	to_ary == other.to_ary
end

#===(other) ⇒ Object

Check string equality, useful for case statements.



217
218
219
# File 'lib/protocol/url/relative.rb', line 217

def ===(other)
	to_s === other
end

#append(buffer = String.new) ⇒ Object

Append the relative URL to the given buffer. The path, query, and fragment are expected to already be properly encoded.



161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/protocol/url/relative.rb', line 161

def append(buffer = String.new)
	buffer << @path.encoded
	
	if @query and !@query.empty?
		buffer << "?" << @query
	end
	
	if @fragment and !@fragment.empty?
		buffer << "#" << @fragment
	end
	
	return buffer
end

#as_jsonObject

Convert the URL to a JSON-compatible representation.



231
232
233
# File 'lib/protocol/url/relative.rb', line 231

def as_json(...)
	to_s
end

#equal?(other) ⇒ Boolean

Check if this URL is equal to another URL by comparing components.

Returns:

  • (Boolean)


193
194
195
# File 'lib/protocol/url/relative.rb', line 193

def equal?(other)
	to_ary == other.to_ary
end

#fragment?Boolean

Returns:

  • (Boolean)


71
72
73
# File 'lib/protocol/url/relative.rb', line 71

def fragment?
	@fragment and !@fragment.empty?
end

#freezeObject

Freeze the URL and its direct components.



28
29
30
31
32
33
34
35
36
# File 'lib/protocol/url/relative.rb', line 28

def freeze
	return self if frozen?
	
	@path.freeze
	@query.freeze
	@fragment.freeze
	
	return super
end

#hashObject

Compute a hash value for the URL based on its components.



185
186
187
# File 'lib/protocol/url/relative.rb', line 185

def hash
	to_ary.hash
end

#inspectObject

Generate a human-readable representation for debugging.



245
246
247
# File 'lib/protocol/url/relative.rb', line 245

def inspect
	"#<#{self.class} #{to_s}>"
end

#local_path(root) ⇒ Object Also known as: to_local_path

Resolve the URL path beneath a local filesystem root.



59
60
61
# File 'lib/protocol/url/relative.rb', line 59

def local_path(root)
	@path.local_path(root)
end

#normalize!Object

Normalize the encoded path and simplify its structure.

This modifies the URL in-place by normalizing and simplifying the path component:

  • Decodes percent-encoded unreserved characters
  • Uses uppercase hexadecimal digits for retained percent escapes
  • Removes "." segments (current directory)
  • Resolves ".." segments (parent directory)
  • Collapses empty path segments represented by consecutive slashes

Normalization is intentionally lossy. Callers that need to preserve the original path structure should retain the parsed URL and avoid this method.

Examples:

Basic normalization

url = Relative.new("/foo//bar/./baz/../qux")
url.normalize!
url.path.to_s  # => "/foo/bar/qux"


153
154
155
156
157
# File 'lib/protocol/url/relative.rb', line 153

def normalize!
	@path = @path.normalize.simplify
	
	return self
end

#query?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'lib/protocol/url/relative.rb', line 66

def query?
	@query and !@query.empty?
end

#The path component of the URL.=(pathcomponentoftheURL. = (value)) ⇒ Object



39
# File 'lib/protocol/url/relative.rb', line 39

attr :path

#to_aryObject

Convert the URL to an array representation.



178
179
180
# File 'lib/protocol/url/relative.rb', line 178

def to_ary
	[@path, @query, @fragment]
end

#to_jsonObject

Convert the URL to JSON.



238
239
240
# File 'lib/protocol/url/relative.rb', line 238

def to_json(...)
	as_json.to_json(...)
end

#to_sObject

Convert the URL to its string representation.



224
225
226
# File 'lib/protocol/url/relative.rb', line 224

def to_s
	append
end

#with(path: nil, query: @query, fragment: @fragment, pop: true) ⇒ Object

Create a new Relative URL with modified components.

Examples:

Update the query string.

url = Relative.new("/search", "query=ruby")
updated = url.with(query: "query=python")
updated.to_s  # => "/search?query=python"

Append to the path.

url = Relative.new("/documents/")
updated = url.with(path: "report.pdf", pop: false)
updated.to_s  # => "/documents/report.pdf"


129
130
131
132
133
# File 'lib/protocol/url/relative.rb', line 129

def with(path: nil, query: @query, fragment: @fragment, pop: true)
	path = @path.join(path, pop: pop) unless path.nil?
	
	self.class.new(path || @path, query, fragment)
end