diff --git a/lib/protocol/url/absolute.rb b/lib/protocol/url/absolute.rb index e76f420..a5d992c 100644 --- a/lib/protocol/url/absolute.rb +++ b/lib/protocol/url/absolute.rb @@ -15,8 +15,8 @@ class Absolute < Relative # @parameter scheme [String] The URL scheme (e.g., "https", "http"). # @parameter authority [String] The authority component (e.g., "example.com", "user@host:port"). # @parameter path [String | Path] The encoded path component (defaults to "/"). - # @parameter query [String, nil] The query string. - # @parameter fragment [String, nil] The fragment identifier. + # @parameter query [String | Nil] The query string. + # @parameter fragment [String | Nil] The fragment identifier. def initialize(scheme, authority, path = "/", query = nil, fragment = nil) @scheme = scheme @authority = authority @@ -36,11 +36,11 @@ def freeze return super end - # @attribute [String] The URL scheme. - attr :scheme + # @attribute [String | Nil] The URL scheme. + attr_accessor :scheme - # @attribute [String] The authority component. - attr :authority + # @attribute [String | Nil] The authority component. + attr_accessor :authority # Check if the URL has a non-empty scheme. # @@ -122,11 +122,11 @@ def append(buffer = String.new) # Create a new Absolute URL with modified components. # - # @parameter scheme [String, nil] The scheme to use (nil to remove scheme). - # @parameter authority [String, nil] The authority to use (nil to remove authority). - # @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 scheme [String | Nil] The scheme to use (nil to remove scheme). + # @parameter authority [String | Nil] The authority to use (nil to remove authority). + # @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 [Absolute] A new Absolute URL with the modified components. # diff --git a/lib/protocol/url/reference.rb b/lib/protocol/url/reference.rb index ff435ea..c36b70f 100644 --- a/lib/protocol/url/reference.rb +++ b/lib/protocol/url/reference.rb @@ -102,8 +102,8 @@ def initialize(path = "/", query = nil, fragment = nil, parameters = nil) @parameters = parameters end - # @attribute [Hash] User supplied parameters that will be appended to the query part. - attr :parameters + # @attribute [Hash | Nil] User supplied parameters that will be appended to the query part. + attr_accessor :parameters # Freeze the reference. # diff --git a/lib/protocol/url/relative.rb b/lib/protocol/url/relative.rb index c77408b..c671aab 100644 --- a/lib/protocol/url/relative.rb +++ b/lib/protocol/url/relative.rb @@ -15,8 +15,8 @@ class Relative # Initialize a new relative URL. # # @parameter path [String | Path] The encoded path component. - # @parameter query [String, nil] The query string. - # @parameter fragment [String, nil] The fragment identifier. + # @parameter query [String | Nil] The query string. + # @parameter fragment [String | Nil] The fragment identifier. def initialize(path, query = nil, fragment = nil) @path = Path[path] @query = query @@ -45,11 +45,11 @@ def path=(path) @path = Path[path] end - # @attribute [String, nil] The query string component. - attr :query + # @attribute [String | Nil] The query string component. + attr_accessor :query - # @attribute [String, nil] The fragment identifier. - attr :fragment + # @attribute [String | Nil] The fragment identifier. + attr_accessor :fragment # Resolve the URL path beneath a local filesystem root. # @@ -111,9 +111,9 @@ def +(other) # Create a new Relative URL with modified components. # - # @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 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. # diff --git a/releases.md b/releases.md index b958c67..ee0857c 100644 --- a/releases.md +++ b/releases.md @@ -2,7 +2,7 @@ ## Unreleased - - Allow unfrozen relative and absolute URLs to replace their path component. + - Allow unfrozen relative and absolute URLs to replace their components. ## v0.10.0 diff --git a/test/protocol/url/absolute.rb b/test/protocol/url/absolute.rb index e72790b..015edb5 100644 --- a/test/protocol/url/absolute.rb +++ b/test/protocol/url/absolute.rb @@ -35,6 +35,19 @@ expect(url.path).to be(:frozen?) expect(url.freeze).to be_equal(url) end + + it "prevents scheme and authority assignment" do + url = Protocol::URL::Absolute.new("https", "example.com", "/") + url.freeze + + expect do + url.scheme = "http" + end.to raise_exception(FrozenError) + + expect do + url.authority = "other.example.com" + end.to raise_exception(FrozenError) + end end with "#path=" do @@ -46,6 +59,28 @@ end end + with "component assignment" do + it "replaces and clears the scheme" do + url = Protocol::URL::Absolute.new("http", "example.com", "/path") + url.scheme = "https" + + expect(url.to_s).to be == "https://example.com/path" + + url.scheme = nil + expect(url.to_s).to be == "//example.com/path" + end + + it "replaces and clears the authority" do + url = Protocol::URL::Absolute.new("https", "example.com", "/path") + url.authority = "cdn.example.com" + + expect(url.to_s).to be == "https://cdn.example.com/path" + + url.authority = nil + expect(url.to_s).to be == "https:/path" + end + end + describe "fragment handling" do it "preserves encoded fragments" do url = Protocol::URL["http://example.com/path#hello%20world"] diff --git a/test/protocol/url/reference.rb b/test/protocol/url/reference.rb index 466013e..841f7ff 100644 --- a/test/protocol/url/reference.rb +++ b/test/protocol/url/reference.rb @@ -60,6 +60,34 @@ expect(reference.path).to be(:frozen?) expect(reference.parameters).to be(:frozen?) end + + it "prevents parameters assignment" do + reference.freeze + + expect do + reference.parameters = {"page" => 2} + end.to raise_exception(FrozenError) + end + end + + with "#parameters=" do + it "replaces parameters while preserving the query" do + reference = subject.new("/search", "q=ruby", nil, {"page" => 1}) + reference.parameters = {"page" => 2} + + expect(reference.query).to be == "q=ruby" + expect(reference.parameters).to be == {"page" => 2} + expect(reference.to_s).to be == "/search?q=ruby&page=2" + end + + it "clears parameters while preserving the query" do + reference = subject.new("/search", "q=ruby", nil, {"page" => 1}) + reference.parameters = nil + + expect(reference.query).to be == "q=ruby" + expect(reference.parameters).to be_nil + expect(reference.to_s).to be == "/search?q=ruby" + end end with ".[]" do diff --git a/test/protocol/url/relative.rb b/test/protocol/url/relative.rb index 9e51055..0c652bd 100644 --- a/test/protocol/url/relative.rb +++ b/test/protocol/url/relative.rb @@ -54,6 +54,19 @@ url.path = "/updated" end.to raise_exception(FrozenError) end + + it "prevents query and fragment assignment" do + url = Protocol::URL::Relative.new("/original") + url.freeze + + expect do + url.query = "q=test" + end.to raise_exception(FrozenError) + + expect do + url.fragment = "section" + end.to raise_exception(FrozenError) + end end with "#path=" do @@ -76,6 +89,28 @@ end end + with "component assignment" do + it "replaces and clears the query" do + url = Protocol::URL::Relative.new("/search", "q=ruby", "results") + url.query = "q=python" + + expect(url.to_s).to be == "/search?q=python#results" + + url.query = nil + expect(url.to_s).to be == "/search#results" + end + + it "replaces and clears the fragment" do + url = Protocol::URL::Relative.new("/search", "q=ruby", "old") + url.fragment = "new" + + expect(url.to_s).to be == "/search?q=ruby#new" + + url.fragment = nil + expect(url.to_s).to be == "/search?q=ruby" + end + end + with "#+" do it "returns Absolute when adding Absolute to Relative" do relative = Protocol::URL::Relative.new("/path")