Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/protocol/url/absolute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ def with(scheme: @scheme, authority: @authority, path: nil, query: @query, fragm
self.class.new(scheme, authority, path || @path, query, fragment)
end

# Absolute URLs cannot be made relative without comparing their origins.
# @parameter base [Object] The ignored base URL or path.
# @returns [self] This absolute URL.
def relative_to(base)
return self
end

# Convert the URL to an array representation.
#
# @returns [Array] An array of `[scheme, authority, path, query, fragment]`.
Expand Down
17 changes: 17 additions & 0 deletions lib/protocol/url/relative.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,23 @@ def with(path: nil, query: @query, fragment: @fragment, pop: true)
self.class.new(path || @path, query, fragment)
end

# Express this URL relative to the given base path.
#
# Already-relative paths are returned unchanged. Query and fragment components
# are preserved when converting a root-relative path.
#
# @parameter base [Relative | Path | String] The base URL or path.
# @returns [Relative] The relative URL.
def relative_to(base)
return self unless @path.absolute?

if base.is_a?(Relative)
base = base.path
end

return self.class.new(@path.relative(base), @query, @fragment)
end

# Normalize the encoded path and simplify its structure.
#
# This modifies the URL in-place by normalizing and simplifying the path component:
Expand Down
4 changes: 4 additions & 0 deletions releases.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Releases

## Unreleased

- Add `Protocol::URL::Relative#relative_to` for expressing root-relative URLs relative to a base path.

## v0.14.0

- Allow paths to compare with their encoded string representation.
Expand Down
8 changes: 8 additions & 0 deletions test/protocol/url/absolute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,14 @@
end
end

with "#relative_to" do
it "returns absolute URLs unchanged" do
url = Protocol::URL::Absolute.new("https", "example.com", "/docs/guide", "q=ruby", "examples")

expect(url.relative_to("/docs/index")).to be_equal(url)
end
end

with "#to_ary" do
it "returns array representation" do
url = Protocol::URL::Absolute.new("https", "example.com", "/path", "q=test", "section")
Expand Down
31 changes: 31 additions & 0 deletions test/protocol/url/relative.rb
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,37 @@
end
end

with "#relative_to" do
it "makes root-relative URLs relative to a path" do
url = Protocol::URL::Relative.new("/docs/guide", "q=ruby", "examples")
result = url.relative_to("/docs/index")

expect(result).to be_a(Protocol::URL::Relative)
expect(result.path).to be == Protocol::URL::Path["guide"]
expect(result.query).to be == "q=ruby"
expect(result.fragment).to be == "examples"
end

it "accepts a URL as the base" do
url = Protocol::URL::Relative.new("/docs/guide")
base = Protocol::URL::Relative.new("/docs/index")

expect(url.relative_to(base).path).to be == Protocol::URL::Path["guide"]
end

it "preserves encoded path segments" do
url = Protocol::URL::Relative.new("/files/a%2Fb")

expect(url.relative_to("/index").path).to be == Protocol::URL::Path["files/a%2Fb"]
end

it "returns already-relative URLs unchanged" do
url = Protocol::URL::Relative.new("../guide", "q=ruby", "examples")

expect(url.relative_to("/docs/index")).to be_equal(url)
end
end

with "#to_ary" do
it "returns array representation" do
url = Protocol::URL::Relative.new("/path", "q=test", "section")
Expand Down