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
3 changes: 2 additions & 1 deletion lib/protocol/url/absolute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ def with(scheme: @scheme, authority: @authority, path: nil, query: @query, fragm

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

Expand Down
11 changes: 8 additions & 3 deletions lib/protocol/url/path.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def self.for(components, encoding: Encoding)
#
# @parameter target [String] The destination path (where you want to go).
# @parameter from [String] The source path (where you are starting from).
# @parameter explicit [Boolean] Whether same-directory paths should start with `./`.
# @returns [String] The relative path from `from` to `target`.
#
# @example Calculate relative path between pages.
Expand All @@ -76,8 +77,8 @@ def self.for(components, encoding: Encoding)
# @example Calculate relative path in same directory.
# Path.relative("/docs/guide.html", "/docs/index.html")
# # => "guide.html"
def self.relative(target, from)
return Path[target].relative(from).to_s
def self.relative(target, from, explicit: false)
return Path[target].relative(from, explicit: explicit).to_s
end

# Initialize a path from either its complete encoded representation or encoded segments.
Expand Down Expand Up @@ -357,8 +358,9 @@ def join(other, pop: true, simplify: true)
# Calculate this path relative to another path.
#
# @parameter from [String | Array(String) | Path] The source path.
# @parameter explicit [Boolean] Whether same-directory paths should start with `./`.
# @returns [Path] The relative path from `from` to this path.
def relative(from)
def relative(from, explicit: false)
target_segments = self.segments
from_segments = Path[from].segments

Expand Down Expand Up @@ -386,6 +388,9 @@ def relative(from)
# An empty reference identifies the current document, so identify the current directory explicitly:
if relative_segments == [""]
relative_segments = [".", ""]
elsif explicit && relative_segments.first != ".."
# Identify same-directory references explicitly:
relative_segments.unshift(".")
elsif relative_segments.first&.include?(":")
# A colon in the first segment would be interpreted as a URI scheme:
relative_segments.unshift(".")
Expand Down
5 changes: 3 additions & 2 deletions lib/protocol/url/relative.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,16 @@ def with(path: nil, query: @query, fragment: @fragment, pop: true)
# are preserved when converting a root-relative path.
#
# @parameter base [Relative | Path | String] The base URL or path.
# @parameter explicit [Boolean] Whether same-directory paths should start with `./`.
# @returns [Relative] The relative URL.
def relative_to(base)
def relative_to(base, explicit: false)
return self unless @path.absolute?

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

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

# Normalize the encoded path and simplify its structure.
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 optional explicit `./` prefixes when generating same-directory relative URLs.

## v0.16.0

- Preserve directory and file semantics when generating relative URL paths.
Expand Down
1 change: 1 addition & 0 deletions test/protocol/url/absolute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
url = Protocol::URL::Absolute.new("https", "example.com", "/docs/guide", "q=ruby", "examples")

expect(url.relative_to("/docs/index")).to be_equal(url)
expect(url.relative_to("/docs/index", explicit: true)).to be_equal(url)
end
end

Expand Down
8 changes: 8 additions & 0 deletions test/protocol/url/path.rb
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,14 @@
expect(Protocol::URL::Path.relative("/docs/guide.html", "/docs/index.html")).to be == "guide.html"
end

it "can identify a same-directory path explicitly" do
expect(Protocol::URL::Path.relative("/docs/guide.html", "/docs/index.html", explicit: true)).to be == "./guide.html"
end

it "does not prefix a parent-directory path" do
expect(Protocol::URL::Path.relative("/assets/app.js", "/docs/index.html", explicit: true)).to be == "../assets/app.js"
end

it "calculates relative path from root to subdirectory" do
expect(Protocol::URL::Path.relative("/foo/bar/", "/")).to be == "foo/bar/"
end
Expand Down
14 changes: 14 additions & 0 deletions test/protocol/url/relative.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,20 @@
expect(result.fragment).to be == "examples"
end

it "can identify same-directory URLs explicitly" do
url = Protocol::URL::Relative.new("/docs/guide", "q=ruby", "examples")
result = url.relative_to("/docs/index", explicit: true)

expect(result.to_s).to be == "./guide?q=ruby#examples"
end

it "does not prefix parent-directory URLs" do
url = Protocol::URL::Relative.new("/assets/app.js")
result = url.relative_to("/docs/index", explicit: true)

expect(result.to_s).to be == "../assets/app.js"
end

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