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.to_s
	@query = query
	@fragment = fragment
end

Instance Attribute Details

#fragmentObject (readonly)

Returns the value of attribute fragment.



33
34
35
# File 'lib/protocol/url/relative.rb', line 33

def fragment
  @fragment
end

#pathObject (readonly)

Returns the value of attribute path.



27
28
29
# File 'lib/protocol/url/relative.rb', line 27

def path
  @path
end

#queryObject (readonly)

Returns the value of attribute query.



30
31
32
# File 'lib/protocol/url/relative.rb', line 30

def query
  @query
end

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



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

attr :fragment

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



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

attr :path

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



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

attr :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  # => "/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  # => "/documents/summary.pdf"


68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/protocol/url/relative.rb', line 68

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

#<=>(other) ⇒ Object

Compare this URL with another for sorting purposes.



173
174
175
# File 'lib/protocol/url/relative.rb', line 173

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

#==(other) ⇒ Object

Check structural equality by comparing components.



181
182
183
# File 'lib/protocol/url/relative.rb', line 181

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

#===(other) ⇒ Object

Check string equality, useful for case statements.



189
190
191
# File 'lib/protocol/url/relative.rb', line 189

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.



133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/protocol/url/relative.rb', line 133

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

#as_jsonObject

Convert the URL to a JSON-compatible representation.



203
204
205
# File 'lib/protocol/url/relative.rb', line 203

def as_json(...)
	to_s
end

#equal?(other) ⇒ Boolean

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

Returns:

  • (Boolean)


165
166
167
# File 'lib/protocol/url/relative.rb', line 165

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

#fragment?Boolean

Returns:

  • (Boolean)


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

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

#hashObject

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



157
158
159
# File 'lib/protocol/url/relative.rb', line 157

def hash
	to_ary.hash
end

#inspectObject

Generate a human-readable representation for debugging.



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

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

#normalize!Object

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)

Examples:

Basic normalization

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


123
124
125
126
127
128
129
# File 'lib/protocol/url/relative.rb', line 123

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

#query?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/protocol/url/relative.rb', line 43

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

#to_aryObject

Convert the URL to an array representation.



150
151
152
# File 'lib/protocol/url/relative.rb', line 150

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

#to_jsonObject

Convert the URL to JSON.



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

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

#to_local_pathObject

Convert the URL path to a local filesystem path.



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

def to_local_path
	Path.to_local_path(@path)
end

#to_sObject

Convert the URL to its string representation.



196
197
198
# File 'lib/protocol/url/relative.rb', line 196

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"


106
107
108
# File 'lib/protocol/url/relative.rb', line 106

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