From 5f337e524e61028488336875d7e1d844ce5ae11f Mon Sep 17 00:00:00 2001 From: ingemar Date: Fri, 10 Jan 2025 16:07:23 +0100 Subject: [PATCH] Fix double slashes bug (#15) **Background**: Parsing a URL with a trailing slash and then joining paths onto it resulted in double slashes. Example: ```rb URL.parse("https://example.com/").join("path").to_s # => "https://example.com//path ``` Parsing `nil` would throw a `NoMethodError` for `to_str`. Example: ```rb URL.parse(nil) # qasa-url/lib/url.rb:63:in `parse': undefined method `to_str' for nil (NoMethodError) ``` **Notable changes:** - Refactor `#join` so that double slashes don't occur when parsing a URL with a trailing slash - Refactor `::parse` to return `nil` when given `nil` --- CHANGELOG.md | 4 ++++ lib/qasa/url/version.rb | 2 +- lib/url.rb | 28 +++++++++++++++++++++++----- test/url_test.rb | 30 ++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f901e3..92b5c90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [0.1.1] +- Fixed a bug where a parsed URL ending with a slash would result in double slashes after the hostname when joined with additional paths. +- Fixed bug when `URL.parse` with `nil` was rasing an arrer. Now it returns `nil`. + ## [0.1.0] - Initial release diff --git a/lib/qasa/url/version.rb b/lib/qasa/url/version.rb index 15f67cc..057ac11 100644 --- a/lib/qasa/url/version.rb +++ b/lib/qasa/url/version.rb @@ -2,6 +2,6 @@ module Qasa module Url - VERSION = "0.1.0" + VERSION = "0.1.1" end end diff --git a/lib/url.rb b/lib/url.rb index f1ef559..62a0d85 100644 --- a/lib/url.rb +++ b/lib/url.rb @@ -59,6 +59,8 @@ class << self # URL.parse("https://www.example.com:3000/path?query=string") # # => # def parse(string) + return nil if string.nil? + string .to_str .dup @@ -141,12 +143,21 @@ def path=(path) # url = URL.parse("https://www.example.com") # url.join("path").path("to", "nowhere") # url.to_s # => "https://www.example.com/path/to/nowhere" + # @example + # url = URL.parse("https://www.example.com/") + # url.join("/path", "/to/", "nowhere/") + # url.to_s # => "https://www.example.com/path/to/nowhere/" def join(*paths) - paths.map do |path| - path.start_with?(SLASH) ? path.sub(SLASH, NOTHING) : path.dup - end.then do |paths| - self.path = Array(path).concat(paths).join(SLASH) - end + parts = Array(path).concat(paths) + size = parts.size + + parts + .map + .with_index(1) { |part, index| sanitize_path(part, last: index == size) } + .compact + .then do |parts| + self.path = Array(NOTHING).concat(parts).join(SLASH) + end self end @@ -293,4 +304,11 @@ def deep_transform_keys(hash) def domain_parts host.split(DOT) end + + def sanitize_path(path, last:) + path = path.start_with?(SLASH) ? path[1..] : path.dup + path = path.end_with?(SLASH) ? path[..-2] : path unless last + + path.empty? ? nil : path + end end diff --git a/test/url_test.rb b/test/url_test.rb index bc6186a..7127736 100644 --- a/test/url_test.rb +++ b/test/url_test.rb @@ -30,6 +30,12 @@ class URLTest < Minitest::Test assert_nil result end + + it "returns nil if given nil" do + result = URL.parse(nil) + + assert_nil result + end end describe "#join" do @@ -65,6 +71,30 @@ class URLTest < Minitest::Test assert_equal "/path/to/nowhere", url.path end + it "does not double slash the path if the parsed URL ends with a slash" do + url = URL.parse("http://www.example.com/") + + url.join("/path") + + assert_equal "http://www.example.com/path", url.to_s + end + + it "does not double slash the path if the parsed URL ends with a slash" do + url = URL.parse("http://www.example.com") + + url.join("/path", "/to/", "nowhere") + + assert_equal "http://www.example.com/path/to/nowhere", url.to_s + end + + it "doesn not remove a trailing slash" do + url = URL.parse("http://www.example.com/") + + url.join("path", "to", "nowhere/") + + assert_equal "http://www.example.com/path/to/nowhere/", url.to_s + end + it "returns self" do url = URL.parse("http://www.example.com")