Skip to content

Commit 9c43930

Browse files
Allow URL component assignment. (#13)
1 parent 442c50a commit 9c43930

7 files changed

Lines changed: 121 additions & 23 deletions

File tree

lib/protocol/url/absolute.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class Absolute < Relative
1515
# @parameter scheme [String] The URL scheme (e.g., "https", "http").
1616
# @parameter authority [String] The authority component (e.g., "example.com", "user@host:port").
1717
# @parameter path [String | Path] The encoded path component (defaults to "/").
18-
# @parameter query [String, nil] The query string.
19-
# @parameter fragment [String, nil] The fragment identifier.
18+
# @parameter query [String | Nil] The query string.
19+
# @parameter fragment [String | Nil] The fragment identifier.
2020
def initialize(scheme, authority, path = "/", query = nil, fragment = nil)
2121
@scheme = scheme
2222
@authority = authority
@@ -36,11 +36,11 @@ def freeze
3636
return super
3737
end
3838

39-
# @attribute [String] The URL scheme.
40-
attr :scheme
39+
# @attribute [String | Nil] The URL scheme.
40+
attr_accessor :scheme
4141

42-
# @attribute [String] The authority component.
43-
attr :authority
42+
# @attribute [String | Nil] The authority component.
43+
attr_accessor :authority
4444

4545
# Check if the URL has a non-empty scheme.
4646
#
@@ -122,11 +122,11 @@ def append(buffer = String.new)
122122

123123
# Create a new Absolute URL with modified components.
124124
#
125-
# @parameter scheme [String, nil] The scheme to use (nil to remove scheme).
126-
# @parameter authority [String, nil] The authority to use (nil to remove authority).
127-
# @parameter path [String, nil] The path to merge with the current path.
128-
# @parameter query [String, nil] The query string to use.
129-
# @parameter fragment [String, nil] The fragment to use.
125+
# @parameter scheme [String | Nil] The scheme to use (nil to remove scheme).
126+
# @parameter authority [String | Nil] The authority to use (nil to remove authority).
127+
# @parameter path [String | Nil] The path to merge with the current path.
128+
# @parameter query [String | Nil] The query string to use.
129+
# @parameter fragment [String | Nil] The fragment to use.
130130
# @parameter pop [Boolean] Whether to pop the last path component before merging.
131131
# @returns [Absolute] A new Absolute URL with the modified components.
132132
#

lib/protocol/url/reference.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ def initialize(path = "/", query = nil, fragment = nil, parameters = nil)
102102
@parameters = parameters
103103
end
104104

105-
# @attribute [Hash] User supplied parameters that will be appended to the query part.
106-
attr :parameters
105+
# @attribute [Hash | Nil] User supplied parameters that will be appended to the query part.
106+
attr_accessor :parameters
107107

108108
# Freeze the reference.
109109
#

lib/protocol/url/relative.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class Relative
1515
# Initialize a new relative URL.
1616
#
1717
# @parameter path [String | Path] The encoded path component.
18-
# @parameter query [String, nil] The query string.
19-
# @parameter fragment [String, nil] The fragment identifier.
18+
# @parameter query [String | Nil] The query string.
19+
# @parameter fragment [String | Nil] The fragment identifier.
2020
def initialize(path, query = nil, fragment = nil)
2121
@path = Path[path]
2222
@query = query
@@ -45,11 +45,11 @@ def path=(path)
4545
@path = Path[path]
4646
end
4747

48-
# @attribute [String, nil] The query string component.
49-
attr :query
48+
# @attribute [String | Nil] The query string component.
49+
attr_accessor :query
5050

51-
# @attribute [String, nil] The fragment identifier.
52-
attr :fragment
51+
# @attribute [String | Nil] The fragment identifier.
52+
attr_accessor :fragment
5353

5454
# Resolve the URL path beneath a local filesystem root.
5555
#
@@ -111,9 +111,9 @@ def +(other)
111111

112112
# Create a new Relative URL with modified components.
113113
#
114-
# @parameter path [String, nil] The path to merge with the current path.
115-
# @parameter query [String, nil] The query string to use.
116-
# @parameter fragment [String, nil] The fragment to use.
114+
# @parameter path [String | Nil] The path to merge with the current path.
115+
# @parameter query [String | Nil] The query string to use.
116+
# @parameter fragment [String | Nil] The fragment to use.
117117
# @parameter pop [Boolean] Whether to pop the last path component before merging.
118118
# @returns [Relative] A new Relative URL with the modified components.
119119
#

releases.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Unreleased
44

5-
- Allow unfrozen relative and absolute URLs to replace their path component.
5+
- Allow unfrozen relative and absolute URLs to replace their components.
66

77
## v0.10.0
88

test/protocol/url/absolute.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@
3535
expect(url.path).to be(:frozen?)
3636
expect(url.freeze).to be_equal(url)
3737
end
38+
39+
it "prevents scheme and authority assignment" do
40+
url = Protocol::URL::Absolute.new("https", "example.com", "/")
41+
url.freeze
42+
43+
expect do
44+
url.scheme = "http"
45+
end.to raise_exception(FrozenError)
46+
47+
expect do
48+
url.authority = "other.example.com"
49+
end.to raise_exception(FrozenError)
50+
end
3851
end
3952

4053
with "#path=" do
@@ -46,6 +59,28 @@
4659
end
4760
end
4861

62+
with "component assignment" do
63+
it "replaces and clears the scheme" do
64+
url = Protocol::URL::Absolute.new("http", "example.com", "/path")
65+
url.scheme = "https"
66+
67+
expect(url.to_s).to be == "https://example.com/path"
68+
69+
url.scheme = nil
70+
expect(url.to_s).to be == "//example.com/path"
71+
end
72+
73+
it "replaces and clears the authority" do
74+
url = Protocol::URL::Absolute.new("https", "example.com", "/path")
75+
url.authority = "cdn.example.com"
76+
77+
expect(url.to_s).to be == "https://cdn.example.com/path"
78+
79+
url.authority = nil
80+
expect(url.to_s).to be == "https:/path"
81+
end
82+
end
83+
4984
describe "fragment handling" do
5085
it "preserves encoded fragments" do
5186
url = Protocol::URL["http://example.com/path#hello%20world"]

test/protocol/url/reference.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,34 @@
6060
expect(reference.path).to be(:frozen?)
6161
expect(reference.parameters).to be(:frozen?)
6262
end
63+
64+
it "prevents parameters assignment" do
65+
reference.freeze
66+
67+
expect do
68+
reference.parameters = {"page" => 2}
69+
end.to raise_exception(FrozenError)
70+
end
71+
end
72+
73+
with "#parameters=" do
74+
it "replaces parameters while preserving the query" do
75+
reference = subject.new("/search", "q=ruby", nil, {"page" => 1})
76+
reference.parameters = {"page" => 2}
77+
78+
expect(reference.query).to be == "q=ruby"
79+
expect(reference.parameters).to be == {"page" => 2}
80+
expect(reference.to_s).to be == "/search?q=ruby&page=2"
81+
end
82+
83+
it "clears parameters while preserving the query" do
84+
reference = subject.new("/search", "q=ruby", nil, {"page" => 1})
85+
reference.parameters = nil
86+
87+
expect(reference.query).to be == "q=ruby"
88+
expect(reference.parameters).to be_nil
89+
expect(reference.to_s).to be == "/search?q=ruby"
90+
end
6391
end
6492

6593
with ".[]" do

test/protocol/url/relative.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@
5454
url.path = "/updated"
5555
end.to raise_exception(FrozenError)
5656
end
57+
58+
it "prevents query and fragment assignment" do
59+
url = Protocol::URL::Relative.new("/original")
60+
url.freeze
61+
62+
expect do
63+
url.query = "q=test"
64+
end.to raise_exception(FrozenError)
65+
66+
expect do
67+
url.fragment = "section"
68+
end.to raise_exception(FrozenError)
69+
end
5770
end
5871

5972
with "#path=" do
@@ -76,6 +89,28 @@
7689
end
7790
end
7891

92+
with "component assignment" do
93+
it "replaces and clears the query" do
94+
url = Protocol::URL::Relative.new("/search", "q=ruby", "results")
95+
url.query = "q=python"
96+
97+
expect(url.to_s).to be == "/search?q=python#results"
98+
99+
url.query = nil
100+
expect(url.to_s).to be == "/search#results"
101+
end
102+
103+
it "replaces and clears the fragment" do
104+
url = Protocol::URL::Relative.new("/search", "q=ruby", "old")
105+
url.fragment = "new"
106+
107+
expect(url.to_s).to be == "/search?q=ruby#new"
108+
109+
url.fragment = nil
110+
expect(url.to_s).to be == "/search?q=ruby"
111+
end
112+
end
113+
79114
with "#+" do
80115
it "returns Absolute when adding Absolute to Relative" do
81116
relative = Protocol::URL::Relative.new("/path")

0 commit comments

Comments
 (0)