Skip to content

Commit 8daf0ae

Browse files
committed
Bump minor version.
1 parent f25c610 commit 8daf0ae

File tree

6 files changed

+185
-124
lines changed

6 files changed

+185
-124
lines changed

context/getting-started.md

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ This guide explains how to get started with `protocol-url` for parsing, manipula
66

77
Add the gem to your project:
88

9-
~~~ bash
9+
``` shell
1010
$ bundle add protocol-url
11-
~~~
11+
```
1212

1313
## Core Concepts
1414

@@ -20,14 +20,12 @@ $ bundle add protocol-url
2020

2121
Additionally, the {ruby Protocol::URL::Path} module provides low-level utilities for path manipulation including splitting, joining, simplifying, and expanding paths according to RFC 3986 rules.
2222

23-
## Basic Usage
24-
25-
### Parsing URLs
23+
## Usage
2624

2725
Parse complete URLs with scheme and authority:
2826

29-
~~~ ruby
30-
require 'protocol/url'
27+
``` ruby
28+
require "protocol/url"
3129

3230
# Parse an absolute URL:
3331
url = Protocol::URL["https://api.example.com:8080/v1/users?page=2#results"]
@@ -36,11 +34,11 @@ url.authority # => "api.example.com:8080"
3634
url.path # => "/v1/users"
3735
url.query # => "page=2"
3836
url.fragment # => "results"
39-
~~~
37+
```
4038

4139
Parse relative URLs and references:
4240

43-
~~~ ruby
41+
``` ruby
4442
# Parse a relative URL:
4543
relative = Protocol::URL["/api/v1/users"]
4644
relative.path # => "/api/v1/users"
@@ -50,13 +48,13 @@ reference = Protocol::URL["/search?q=ruby#top"]
5048
reference.path # => "/search"
5149
reference.query # => "q=ruby"
5250
reference.fragment # => "top"
53-
~~~
51+
```
5452

5553
### Constructing URLs
5654

5755
Build URLs programmatically:
5856

59-
~~~ ruby
57+
``` ruby
6058
# Create an absolute URL:
6159
url = Protocol::URL::Absolute.new("https", "example.com", "/api/users")
6260
url.to_s # => "https://example.com/api/users"
@@ -68,13 +66,13 @@ url.to_s # => "https://user:[email protected]:8080/v1"
6866
# Create a reference with components:
6967
reference = Protocol::URL::Reference.new("/api/search", "q=ruby&limit=10", "results")
7068
reference.to_s # => "/api/search?q=ruby&limit=10#results"
71-
~~~
69+
```
7270

7371
### Combining URLs
7472

7573
URLs can be combined following RFC 3986 resolution rules:
7674

77-
~~~ ruby
75+
``` ruby
7876
# Combine absolute URL with relative path:
7977
base = Protocol::URL["https://example.com/docs/guide/"]
8078
relative = Protocol::URL::Relative.new("../api/reference.html")
@@ -86,15 +84,15 @@ result.to_s # => "https://example.com/docs/api/reference.html"
8684
absolute_path = Protocol::URL::Relative.new("/completely/different/path")
8785
result = base + absolute_path
8886
result.to_s # => "https://example.com/completely/different/path"
89-
~~~
87+
```
9088

9189
## Path Manipulation
9290

9391
The {ruby Protocol::URL::Path} module provides powerful utilities for working with URL paths:
9492

9593
### Splitting and Joining Paths
9694

97-
~~~ ruby
95+
``` ruby
9896
# Split paths into components:
9997
Protocol::URL::Path.split("/a/b/c") # => ["", "a", "b", "c"]
10098
Protocol::URL::Path.split("a/b/c") # => ["a", "b", "c"]
@@ -103,13 +101,13 @@ Protocol::URL::Path.split("a/b/c/") # => ["a", "b", "c", ""]
103101
# Join components back into paths:
104102
Protocol::URL::Path.join(["", "a", "b", "c"]) # => "/a/b/c"
105103
Protocol::URL::Path.join(["a", "b", "c"]) # => "a/b/c"
106-
~~~
104+
```
107105

108106
### Simplifying Paths
109107

110108
Remove dot segments (`.` and `..`) from paths:
111109

112-
~~~ ruby
110+
``` ruby
113111
# Simplify a path:
114112
components = ["a", "b", "..", "c", ".", "d"]
115113
simplified = Protocol::URL::Path.simplify(components)
@@ -119,13 +117,13 @@ simplified = Protocol::URL::Path.simplify(components)
119117
components = ["", "a", "b", "..", "..", "c"]
120118
simplified = Protocol::URL::Path.simplify(components)
121119
# => ["", "c"]
122-
~~~
120+
```
123121

124122
### Expanding Paths
125123

126124
Merge two paths according to RFC 3986 rules:
127125

128-
~~~ ruby
126+
``` ruby
129127
# Expand a relative path against a base:
130128
result = Protocol::URL::Path.expand("/a/b/c", "../d")
131129
# => "/a/b/d"
@@ -137,42 +135,63 @@ result = Protocol::URL::Path.expand("/a/b/c/d", "../../e/f")
137135
# Absolute relative paths replace the base:
138136
result = Protocol::URL::Path.expand("/a/b/c", "/x/y/z")
139137
# => "/x/y/z"
140-
~~~
138+
```
141139

142140
The `expand` method has an optional `pop` parameter (default: `true`) that controls whether the last component of the base path is removed before merging:
143141

144-
~~~ ruby
142+
``` ruby
145143
# With pop=true (default), behaves like URI resolution:
146144
Protocol::URL::Path.expand("/a/b/file.html", "other.html")
147145
# => "/a/b/other.html"
148146

149147
# With pop=false, treats base as a directory:
150148
Protocol::URL::Path.expand("/a/b/file.html", "other.html", false)
151149
# => "/a/b/file.html/other.html"
152-
~~~
150+
```
151+
152+
### Converting to Local File System Paths
153+
154+
Convert URL paths to local file system paths safely:
155+
156+
``` ruby
157+
# Convert URL path to local file system path:
158+
Protocol::URL::Path.to_local_path("/documents/report.pdf")
159+
# => "/documents/report.pdf"
160+
161+
# Handles percent-encoded characters:
162+
Protocol::URL::Path.to_local_path("/files/My%20Document.txt")
163+
# => "/files/My Document.txt"
164+
165+
# Security: Preserves percent-encoded path separators
166+
# This prevents directory traversal attacks:
167+
Protocol::URL::Path.to_local_path("/folder/safe%2Fname/file.txt")
168+
# => "/folder/safe%2Fname/file.txt"
169+
# %2F (/) and %5C (\) are NOT decoded, preventing them from creating
170+
# additional path components in the file system
171+
```
153172

154173
## Working with References
155174

156-
{ruby Protocol::URL::Reference} extends relative URLs with query parameters and fragments. For detailed information on working with references, see the [Working with References](working-with-references.md) guide.
175+
{ruby Protocol::URL::Reference} extends relative URLs with query parameters and fragments. For detailed information on working with references, see the [Working with References](../working-with-references/) guide.
157176

158177
Quick example:
159178

160-
~~~ ruby
179+
``` ruby
161180
# Create a reference with query and fragment:
162181
reference = Protocol::URL::Reference.new("/api/users", "status=active", "results")
163182
reference.to_s # => "/api/users?status=active#results"
164183

165184
# Update components immutably:
166185
updated = reference.with(query: "status=inactive")
167186
updated.to_s # => "/api/users?status=inactive#results"
168-
~~~
187+
```
169188

170189
## URL Encoding
171190

172191
The library handles URL encoding automatically for path components:
173192

174-
~~~ ruby
175-
require 'protocol/url/encoding'
193+
``` ruby
194+
require "protocol/url/encoding"
176195

177196
# Escape path components (preserves slashes):
178197
escaped = Protocol::URL::Encoding.escape_path("/path/with spaces/file.html")
@@ -185,13 +204,13 @@ escaped = Protocol::URL::Encoding.escape("hello world!")
185204
# Unescape percent-encoded strings:
186205
unescaped = Protocol::URL::Encoding.unescape("hello%20world%21")
187206
# => "hello world!"
188-
~~~
207+
```
189208

190209
## Practical Examples
191210

192211
### Building API URLs
193212

194-
~~~ ruby
213+
``` ruby
195214
# Build a base API URL:
196215
base = Protocol::URL::Absolute.new("https", "api.example.com", "/v2")
197216

@@ -202,13 +221,13 @@ users_endpoint.to_s # => "https://api.example.com/v2/users"
202221
# Add specific resource ID:
203222
user_detail = users_endpoint + Protocol::URL::Relative.new("123")
204223
user_detail.to_s # => "https://api.example.com/v2/users/123"
205-
~~~
224+
```
206225

207226
### Resolving Relative Links
208227

209228
When parsing HTML or processing links, you often need to resolve relative URLs:
210229

211-
~~~ ruby
230+
``` ruby
212231
# Page URL:
213232
page = Protocol::URL["https://example.com/docs/guide/intro.html"]
214233

@@ -221,20 +240,20 @@ resolved.to_s # => "https://example.com/docs/api/reference.html"
221240
link = Protocol::URL::Relative.new("getting-started.html")
222241
resolved = page + link
223242
resolved.to_s # => "https://example.com/docs/guide/getting-started.html"
224-
~~~
243+
```
225244

226245
### URL Normalization
227246

228247
Clean up URLs by simplifying paths:
229248

230-
~~~ ruby
249+
``` ruby
231250
# URL with redundant path segments:
232251
messy = Protocol::URL["https://example.com/a/b/../c/./d"]
233252

234253
# The path is automatically simplified:
235254
messy.path # => "/a/c/d"
236255
messy.to_s # => "https://example.com/a/c/d"
237-
~~~
256+
```
238257

239258
## Best Practices
240259

@@ -263,36 +282,36 @@ When manipulating paths:
263282

264283
The `expand` method pops the last path component by default to match RFC 3986 URI resolution:
265284

266-
~~~ ruby
285+
``` ruby
267286
# This might be surprising:
268287
Protocol::URL::Path.expand("/api/users", "groups")
269288
# => "/api/groups" (not "/api/users/groups")
270289

271290
# To prevent popping, use pop=false:
272291
Protocol::URL::Path.expand("/api/users", "groups", false)
273292
# => "/api/users/groups"
274-
~~~
293+
```
275294

276295
### Empty Paths
277296

278297
Empty relative paths return the base unchanged:
279298

280-
~~~ ruby
299+
``` ruby
281300
base = Protocol::URL::Reference.new("/api/users")
282301
same = base.with(path: "")
283302
same.to_s # => "/api/users" (unchanged)
284-
~~~
303+
```
285304

286305
### Trailing Slashes
287306

288307
Trailing slashes are preserved and have semantic meaning:
289308

290-
~~~ ruby
309+
``` ruby
291310
# Directory (trailing slash):
292311
Protocol::URL::Path.expand("/docs/", "page.html")
293312
# => "/docs/page.html"
294313

295314
# File (no trailing slash):
296315
Protocol::URL::Path.expand("/docs", "page.html")
297316
# => "/page.html" (pops "docs")
298-
~~~
317+
```

context/index.yaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# Automatically generated context index for Utopia::Project guides.
2-
# Do not edit the files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
2+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
33
---
4-
description: Provides abstractions for parsing, manipulating, and constructing URLs.
4+
description: Provides abstractions for working with URLs.
55
metadata:
66
source_code_uri: https://github.com/socketry/protocol-url.git
77
files:
88
- path: getting-started.md
99
title: Getting Started
10-
description: This guide explains how to get started with `protocol-url` for parsing, manipulating, and constructing URLs in Ruby.
10+
description: This guide explains how to get started with `protocol-url` for parsing,
11+
manipulating, and constructing URLs in Ruby.
1112
- path: working-with-references.md
1213
title: Working with References
13-
description: This guide explains how to use `Protocol::URL::Reference` for managing URLs with query parameters and fragments.
14+
description: This guide explains how to use <code class="language-ruby">Protocol::URL::Reference</code>
15+
for managing URLs with query parameters and fragments.

0 commit comments

Comments
 (0)