Protocol::URLSourceProtocolURLRelative

class Relative

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

Definitions

def initialize(path, query = nil, fragment = nil)

Initialize a new relative URL.

Signature

parameter path String

The path component.

parameter query String, nil

The query string.

parameter fragment String, nil

The fragment identifier.

Implementation

def initialize(path, query = nil, fragment = nil)
	@path = path.to_s
	@query = query
	@fragment = fragment
end

attr :path

Signature

attribute String

The path component of the URL.

attr :query

Signature

attribute String, nil

The query string component.

attr :fragment

Signature

attribute String, nil

The fragment identifier.

def to_local_path

Convert the URL path to a local filesystem path.

Signature

returns String

The local filesystem path.

Implementation

def to_local_path
	Path.to_local_path(@path)
end

def query?

Signature

returns Boolean

If there is a query string.

Implementation

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

def fragment?

Signature

returns Boolean

If there is a fragment.

Implementation

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

def +(other)

Combine this relative URL with another URL or path.

Example: Combine two relative paths.

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

Example: Navigate to parent directory.

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

Signature

parameter other String, Absolute, Relative

The URL or path to combine.

returns Absolute, Relative

The combined URL.

Implementation

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.expand(self.path, other.path, true),
			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

def with(path: nil, query: @query, fragment: @fragment, pop: true)

Create a new Relative URL with modified components.

Example: Update the query string.

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

Example: Append to the path.

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

Signature

parameter path String, nil

The path to merge with the current path.

parameter query String, nil

The query string to use.

parameter fragment String, nil

The fragment to use.

parameter pop Boolean

Whether to pop the last path component before merging.

returns Relative

A new Relative URL with the modified components.

Implementation

def with(path: nil, query: @query, fragment: @fragment, pop: true)
	self.class.new(Path.expand(@path, path, pop), query, fragment)
end

def normalize!

Normalize the path by resolving "." and ".." segments and removing duplicate slashes.

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

  • Removes "." segments (current directory)
  • Resolves ".." segments (parent directory)
  • Collapses multiple consecutive slashes to single slashes (except at start)

Example: Basic normalization

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

Signature

returns self

The normalized URL.

Implementation

def normalize!
	components = Path.split(@path)
	normalized = Path.simplify(components)
	@path = Path.join(normalized)
	
	return self
end

def append(buffer = String.new)

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

Implementation

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

def to_ary

Convert the URL to an array representation.

Signature

returns Array

An array of [path, query, fragment].

Implementation

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

def hash

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

Signature

returns Integer

The hash value.

Implementation

def hash
	to_ary.hash
end

def equal?(other)

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

Signature

parameter other Relative

The URL to compare with.

returns Boolean

True if the URLs have identical components.

Implementation

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

def <=>(other)

Compare this URL with another for sorting purposes.

Signature

parameter other Relative

The URL to compare with.

returns Integer

-1, 0, or 1 based on component-wise comparison.

Implementation

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

def ==(other)

Check structural equality by comparing components.

Signature

parameter other Relative

The URL to compare with.

returns Boolean

True if the URLs have identical components.

Implementation

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

def ===(other)

Check string equality, useful for case statements.

Signature

parameter other String, Relative

The value to compare with.

returns Boolean

True if the string representations match.

Implementation

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

def to_s

Convert the URL to its string representation.

Signature

returns String

The formatted URL string.

Implementation

def to_s
	append
end

def as_json(...)

Convert the URL to a JSON-compatible representation.

Signature

returns String

The URL as a string.

Implementation

def as_json(...)
	to_s
end

def to_json(...)

Convert the URL to JSON.

Signature

returns String

The JSON-encoded URL.

Implementation

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

def inspect

Generate a human-readable representation for debugging.

Signature

returns String

A string like #<Protocol::URL::Relative /path?query#fragment>.

Implementation

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