client-api-builder 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -3
- data/README.md +125 -30
- data/lib/client_api_builder/nested_router.rb +1 -1
- data/lib/client_api_builder/router.rb +6 -2
- data/lib/client_api_builder/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 00351abdc1f660fefd19687f75b696db8a1b9b16390ff6427706a8f5db64ee74
|
|
4
|
+
data.tar.gz: 34dac63d4090971accab88566f4c217ccdb5a1d21ba000281ad5e2ea68bd74cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b34e2623702b08c9ac8156e8f0fe9da060636823492ec49bc34832e4e5bff09bb8ab4b3244cbba38bc49ec1a01a80ae30a484c8ec85fb8ae7a032131156278f5
|
|
7
|
+
data.tar.gz: 4e164e2afd778cd2a6c2adb171c0ee6afec71eef925de9b61fe9b8c1b1d94a5faf2bd8fa624f6f691c4b7268d550e2e58dc82ef0c9b40470fe687026a5cef90f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.1](https://github.com/dougyouch/client-api-builder/compare/v0.7.0...v0.7.1) (2026-10-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **router:** detect destroy_ routes as DELETE ([85604c4](https://github.com/dougyouch/client-api-builder/commit/85604c4d974c8483e292e700eb51a2c78e312b54))
|
|
9
|
+
* **router:** url-encode path values ([76d6fb4](https://github.com/dougyouch/client-api-builder/commit/76d6fb4fa246b342e263322d06262cba6760c278))
|
|
10
|
+
|
|
11
|
+
### Upgrade Notes
|
|
12
|
+
|
|
13
|
+
* Path values are now percent-encoded by `escape_path`, including `/`. A value like `'a/b'` that previously expanded into two path segments is now sent as one segment (`a%2Fb`). To keep `/` as a separator, override `escape_path` in your client, e.g. `value.to_s.split('/').map { |part| ERB::Util.url_encode(part) }.join('/')`.
|
|
14
|
+
|
|
3
15
|
## [0.7.0](https://github.com/dougyouch/client-api-builder/compare/v0.6.1...v0.7.0) (2026-10-03)
|
|
4
16
|
|
|
5
17
|
|
|
6
|
-
###
|
|
18
|
+
### ⚠ BREAKING CHANGES
|
|
19
|
+
|
|
20
|
+
* client-api-builder now requires Ruby 3.2 or newer (previously 3.0). Ruby 3.0 and 3.1 are end-of-life. ([7c951e0](https://github.com/dougyouch/client-api-builder/commit/7c951e0))
|
|
21
|
+
|
|
22
|
+
### Packaging
|
|
23
|
+
|
|
24
|
+
* ship only `lib/`, `README.md`, `LICENSE` and `CHANGELOG.md` in the gem instead of every tracked file ([ca141a1](https://github.com/dougyouch/client-api-builder/commit/ca141a1))
|
|
25
|
+
* refresh the gem summary and description ([ca141a1](https://github.com/dougyouch/client-api-builder/commit/ca141a1))
|
|
26
|
+
* add `ClientApiBuilder::VERSION` ([7c951e0](https://github.com/dougyouch/client-api-builder/commit/7c951e0))
|
|
7
27
|
|
|
8
|
-
|
|
28
|
+
### Build
|
|
9
29
|
|
|
10
|
-
|
|
30
|
+
* automate versioning, changelog and RubyGems publishing with release-please ([7c951e0](https://github.com/dougyouch/client-api-builder/commit/7c951e0))
|
|
31
|
+
* replace Codecov with a GitHub-hosted coverage badge ([7c951e0](https://github.com/dougyouch/client-api-builder/commit/7c951e0))
|
|
32
|
+
* adopt the dynamic-active-model RuboCop config with rubocop-rspec ([f36ed6f](https://github.com/dougyouch/client-api-builder/commit/f36ed6f))
|
data/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Client API Builder
|
|
2
2
|
|
|
3
|
-
[](https://rubygems.org/gems/client-api-builder)
|
|
4
4
|
[](https://github.com/dougyouch/client-api-builder/actions/workflows/ci.yml)
|
|
5
5
|
[](https://github.com/dougyouch/client-api-builder/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/dougyouch/client-api-builder/actions/workflows/ci.yml)
|
|
6
7
|
|
|
7
8
|
A Ruby gem for building robust, secure API clients through declarative configuration. Define your API endpoints and their behavior with minimal boilerplate while benefiting from built-in security features, automatic retries, and comprehensive error handling.
|
|
8
9
|
|
|
@@ -93,25 +94,54 @@ route :method_name, '/path/:param', options
|
|
|
93
94
|
|
|
94
95
|
| Option | Description |
|
|
95
96
|
|--------|-------------|
|
|
96
|
-
| `method:` | HTTP method
|
|
97
|
+
| `method:` | HTTP method. Auto-detected from the route name if omitted. Any of `:get`, `:post`, `:put`, `:patch`, `:delete`, `:head`, `:options`, `:trace`, `:copy`, `:lock`, `:unlock`, `:mkcol`, `:move`, `:propfind`, `:proppatch`. |
|
|
97
98
|
| `query:` | Hash defining query parameters. Use symbols for dynamic values. |
|
|
98
|
-
| `body:` |
|
|
99
|
-
| `
|
|
99
|
+
| `body:` | Request body: a Hash or Array (symbols become arguments) or a literal String. |
|
|
100
|
+
| `no_body:` | `true` to send no body, even for POST/PUT/PATCH. |
|
|
101
|
+
| `has_body:` | `true` to add a `body:` argument to any method, e.g. a GET with a body. |
|
|
102
|
+
| `expected_response_code:` | Single expected HTTP status code. Without one, any 2xx response is accepted. |
|
|
100
103
|
| `expected_response_codes:` | Array of expected HTTP status codes |
|
|
101
104
|
| `stream:` | Enable streaming (`:file`, `:io`, `:block`, or `true`) |
|
|
102
105
|
| `return:` | Return type (`:response`, `:body`, or parsed JSON by default) |
|
|
103
106
|
|
|
107
|
+
POST, PUT and PATCH routes without a `body:` option take the request body as a `body:` argument:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
route :create_user, '/users'
|
|
111
|
+
|
|
112
|
+
client.create_user(body: { name: 'Ann' })
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Per-Request Options
|
|
116
|
+
|
|
117
|
+
Every generated method also accepts options that apply to that call only:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
client.get_user(
|
|
121
|
+
id: 1,
|
|
122
|
+
headers: { 'X-Trace-Id' => 'abc' }, # merged over the class headers
|
|
123
|
+
query: { expand: 'teams' }, # merged over the route's query params
|
|
124
|
+
body: { name: 'Ann' }, # replaces the route's body
|
|
125
|
+
connection_options: { read_timeout: 5 },
|
|
126
|
+
retries: 3, # attempts for this call
|
|
127
|
+
sleep: 0.5, # seconds between attempts
|
|
128
|
+
return: :body # :body or :response instead of parsed JSON
|
|
129
|
+
)
|
|
130
|
+
```
|
|
131
|
+
|
|
104
132
|
### Automatic HTTP Method Detection
|
|
105
133
|
|
|
106
|
-
The Router
|
|
134
|
+
The Router detects the HTTP method from how the route name starts:
|
|
107
135
|
|
|
108
|
-
|
|
|
109
|
-
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
114
|
-
|
|
|
136
|
+
| Name starts with | HTTP Method |
|
|
137
|
+
|------------------|-------------|
|
|
138
|
+
| `post`, `create`, `add`, `insert` | POST |
|
|
139
|
+
| `put`, `update`, `modify`, `change` | PUT |
|
|
140
|
+
| `patch` | PATCH |
|
|
141
|
+
| `delete`, `remove`, `destroy` | DELETE |
|
|
142
|
+
| anything else | GET |
|
|
143
|
+
|
|
144
|
+
The match is on the start of the name only, so `address_lookup` is a POST. Pass `method:` when the name doesn't say it.
|
|
115
145
|
|
|
116
146
|
```ruby
|
|
117
147
|
class MyApiClient
|
|
@@ -132,11 +162,31 @@ end
|
|
|
132
162
|
|
|
133
163
|
Parameters can be defined in three ways:
|
|
134
164
|
|
|
135
|
-
**1. Path Parameters** (using `:param`
|
|
165
|
+
**1. Path Parameters** (using `:param` syntax):
|
|
136
166
|
|
|
137
167
|
```ruby
|
|
138
168
|
route :get_user, '/users/:id'
|
|
139
|
-
|
|
169
|
+
# client.get_user(id: 1)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`{name}` is filled from the client's own `name` method rather than an argument, which suits values like account IDs that are set once:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
attr_accessor :account_id
|
|
176
|
+
|
|
177
|
+
route :get_invoices, '/accounts/{account_id}/invoices'
|
|
178
|
+
# client.get_invoices
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The same `'{name}'` form works as a value inside `query:` and `body:`.
|
|
182
|
+
|
|
183
|
+
Path values, from arguments and `{name}` alike, are percent-encoded so each stays a single segment: `get_file(name: 'a/b c')` requests `/files/a%2Fb%20c`. Only RFC 3986 unreserved characters (`A-Z a-z 0-9 - . _ ~`) are left as is. To change this, override `escape_path`:
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
# Allow '/' in values, e.g. for nested object keys
|
|
187
|
+
def escape_path(value)
|
|
188
|
+
value.to_s.split('/').map { |part| ERB::Util.url_encode(part) }.join('/')
|
|
189
|
+
end
|
|
140
190
|
```
|
|
141
191
|
|
|
142
192
|
**2. Query Parameters:**
|
|
@@ -193,7 +243,7 @@ class MyApiClient
|
|
|
193
243
|
# Default: JSON (using to_json)
|
|
194
244
|
body_builder :to_json
|
|
195
245
|
|
|
196
|
-
# URL-encoded form data (using to_query)
|
|
246
|
+
# URL-encoded form data (using to_query, requires ActiveSupport)
|
|
197
247
|
body_builder :to_query
|
|
198
248
|
|
|
199
249
|
# Custom query params builder (no ActiveSupport dependency)
|
|
@@ -213,6 +263,8 @@ class MyApiClient
|
|
|
213
263
|
end
|
|
214
264
|
```
|
|
215
265
|
|
|
266
|
+
String bodies are sent as is. Query strings are built the same way with `query_builder`, which accepts `:to_query`, `:query_params`, a method name or a block. It defaults to `:to_query` when ActiveSupport is loaded and to `:query_params` otherwise.
|
|
267
|
+
|
|
216
268
|
### Nested Routing (Sections)
|
|
217
269
|
|
|
218
270
|
Organize complex APIs with nested routes:
|
|
@@ -222,12 +274,17 @@ class MyApiClient
|
|
|
222
274
|
include ClientApiBuilder::Router
|
|
223
275
|
|
|
224
276
|
base_url 'https://api.example.com'
|
|
225
|
-
header 'Authorization', :
|
|
277
|
+
header 'Authorization', :authorization
|
|
226
278
|
|
|
227
279
|
attr_accessor :auth_token
|
|
228
280
|
|
|
281
|
+
def authorization
|
|
282
|
+
"Bearer #{auth_token}"
|
|
283
|
+
end
|
|
284
|
+
|
|
229
285
|
section :users do
|
|
230
286
|
base_url 'https://api.example.com/v2' # Override base URL
|
|
287
|
+
header 'Authorization', :authorization
|
|
231
288
|
|
|
232
289
|
route :list, '/users'
|
|
233
290
|
route :get, '/users/:id'
|
|
@@ -235,6 +292,8 @@ class MyApiClient
|
|
|
235
292
|
end
|
|
236
293
|
|
|
237
294
|
section :posts do
|
|
295
|
+
header 'Authorization', :authorization
|
|
296
|
+
|
|
238
297
|
route :list, '/posts'
|
|
239
298
|
route :get, '/posts/:id'
|
|
240
299
|
end
|
|
@@ -249,6 +308,8 @@ user = client.users.get(id: 123)
|
|
|
249
308
|
posts = client.posts.list
|
|
250
309
|
```
|
|
251
310
|
|
|
311
|
+
A section is its own router class. It uses the parent's `base_url` unless it sets one, but headers, query params, connection options, retries and builders are not inherited, so declare the ones it needs inside the section. Symbol and block values, `{name}` path values, and response blocks are evaluated on the root client, so they can use its methods and state.
|
|
312
|
+
|
|
252
313
|
### Connection Options
|
|
253
314
|
|
|
254
315
|
Configure connection settings:
|
|
@@ -268,6 +329,8 @@ class MyApiClient
|
|
|
268
329
|
end
|
|
269
330
|
```
|
|
270
331
|
|
|
332
|
+
Any `Net::HTTP.start` option can be set this way. Your options are applied over the secure HTTPS defaults, so setting `verify_mode` yourself replaces `VERIFY_PEER`.
|
|
333
|
+
|
|
271
334
|
### Retry Configuration
|
|
272
335
|
|
|
273
336
|
Configure automatic retries for transient failures:
|
|
@@ -278,12 +341,14 @@ class MyApiClient
|
|
|
278
341
|
|
|
279
342
|
base_url 'https://api.example.com'
|
|
280
343
|
|
|
281
|
-
#
|
|
344
|
+
# Make up to 3 attempts in total, waiting 0.5 seconds between them
|
|
282
345
|
configure_retries 3, 0.5
|
|
283
346
|
end
|
|
284
347
|
```
|
|
285
348
|
|
|
286
|
-
|
|
349
|
+
The first argument is the total number of attempts, not extra retries. The default is 1, so requests are not retried unless you configure it. `retries:` and `sleep:` can also be passed per request.
|
|
350
|
+
|
|
351
|
+
Only these network errors are retried by default:
|
|
287
352
|
- `Net::OpenTimeout`, `Net::ReadTimeout`
|
|
288
353
|
- `Errno::ECONNRESET`, `Errno::ECONNREFUSED`, `Errno::ETIMEDOUT`
|
|
289
354
|
- `SocketError`, `EOFError`
|
|
@@ -345,6 +410,8 @@ client.process_stream do |response, chunk|
|
|
|
345
410
|
end
|
|
346
411
|
```
|
|
347
412
|
|
|
413
|
+
Streaming routes return the `Net::HTTPResponse`. Files are written in `wb` mode by default; pass `connection_options: { file_mode: 'ab' }` to append.
|
|
414
|
+
|
|
348
415
|
### Response Handling
|
|
349
416
|
|
|
350
417
|
Customize how responses are processed:
|
|
@@ -370,11 +437,16 @@ class MyApiClient
|
|
|
370
437
|
data
|
|
371
438
|
end
|
|
372
439
|
end
|
|
440
|
+
|
|
441
|
+
# A block passed to the call replaces the route's block
|
|
442
|
+
client.get_user(id: 1) { |data| data['name'] }
|
|
373
443
|
```
|
|
374
444
|
|
|
445
|
+
Blocks run on the client, so they can call its methods and set its state. Empty response bodies return `nil`.
|
|
446
|
+
|
|
375
447
|
### Error Handling
|
|
376
448
|
|
|
377
|
-
|
|
449
|
+
`ClientApiBuilder::UnexpectedResponse` is raised when the status code isn't expected (any non-2xx by default, or anything outside `expected_response_code(s)`) and when a response body isn't valid JSON. It carries the response:
|
|
378
450
|
|
|
379
451
|
```ruby
|
|
380
452
|
begin
|
|
@@ -412,12 +484,9 @@ puts client.request_attempts # Number of attempts (including retries)
|
|
|
412
484
|
|
|
413
485
|
### ActiveSupport Integration
|
|
414
486
|
|
|
415
|
-
When ActiveSupport is
|
|
487
|
+
When ActiveSupport is loaded before your client class includes `ClientApiBuilder::Router`, every request is instrumented as a `client_api_builder.request` event:
|
|
416
488
|
|
|
417
489
|
```ruby
|
|
418
|
-
# Set up logging
|
|
419
|
-
ClientApiBuilder.logger = Logger.new(STDOUT)
|
|
420
|
-
|
|
421
490
|
# Subscribe to request events
|
|
422
491
|
ActiveSupport::Notifications.subscribe('client_api_builder.request') do |*args|
|
|
423
492
|
event = ActiveSupport::Notifications::Event.new(*args)
|
|
@@ -433,9 +502,15 @@ subscriber = ClientApiBuilder::ActiveSupportLogSubscriber.new(Rails.logger)
|
|
|
433
502
|
subscriber.subscribe!
|
|
434
503
|
```
|
|
435
504
|
|
|
505
|
+
Separately, `ClientApiBuilder.logger` receives every exception raised during a request attempt, including ones that are retried:
|
|
506
|
+
|
|
507
|
+
```ruby
|
|
508
|
+
ClientApiBuilder.logger = Logger.new($stdout)
|
|
509
|
+
```
|
|
510
|
+
|
|
436
511
|
#### Production Logging
|
|
437
512
|
|
|
438
|
-
|
|
513
|
+
The built-in log subscriber already leaves out query strings, which may hold credentials. To customize the format, subscribe directly:
|
|
439
514
|
|
|
440
515
|
```ruby
|
|
441
516
|
ActiveSupport::Notifications.subscribe('client_api_builder.request') do |_, start_time, end_time, _, payload|
|
|
@@ -461,7 +536,7 @@ Client API Builder includes several security features enabled by default:
|
|
|
461
536
|
|
|
462
537
|
### SSL/TLS Verification
|
|
463
538
|
|
|
464
|
-
|
|
539
|
+
HTTPS connections verify SSL certificates using `OpenSSL::SSL::VERIFY_PEER` and default to a 30 second open timeout and 60 second read timeout. Plain HTTP connections use Net::HTTP's own defaults.
|
|
465
540
|
|
|
466
541
|
### SSRF Protection
|
|
467
542
|
|
|
@@ -478,9 +553,13 @@ class MyApiClient
|
|
|
478
553
|
end
|
|
479
554
|
```
|
|
480
555
|
|
|
556
|
+
### Path Value Encoding
|
|
557
|
+
|
|
558
|
+
Values inserted into a route's path are percent-encoded, so input such as `../admin` or `a/b?x=1` can't add path segments or a query string to the request.
|
|
559
|
+
|
|
481
560
|
### Path Traversal Protection
|
|
482
561
|
|
|
483
|
-
File streaming
|
|
562
|
+
File streaming rejects any path containing `..` or a null byte:
|
|
484
563
|
|
|
485
564
|
```ruby
|
|
486
565
|
# These will raise ArgumentError
|
|
@@ -522,12 +601,12 @@ end
|
|
|
522
601
|
| Method | Description |
|
|
523
602
|
|--------|-------------|
|
|
524
603
|
| `base_url(url)` | Set the base URL for all requests |
|
|
525
|
-
| `header(name, value)` | Add a header to all requests |
|
|
604
|
+
| `header(name, value = nil, &block)` | Add a header to all requests (value, method name symbol, or block) |
|
|
526
605
|
| `body_builder(builder)` | Configure request body serialization |
|
|
527
606
|
| `query_builder(builder)` | Configure query string serialization |
|
|
528
|
-
| `query_param(name, value)` | Add a query parameter to all requests |
|
|
607
|
+
| `query_param(name, value = nil, &block)` | Add a query parameter to all requests (value, method name symbol, or block) |
|
|
529
608
|
| `connection_option(name, value)` | Set Net::HTTP connection options |
|
|
530
|
-
| `configure_retries(
|
|
609
|
+
| `configure_retries(max_attempts, sleep = 0.05)` | Configure retry behavior |
|
|
531
610
|
| `route(name, path, options)` | Define an API endpoint |
|
|
532
611
|
| `section(name, options, &block)` | Define nested routes |
|
|
533
612
|
| `namespace(path, &block)` | Add path prefix to routes in block |
|
|
@@ -541,11 +620,27 @@ end
|
|
|
541
620
|
| `total_request_time` | Duration of last request in seconds |
|
|
542
621
|
| `request_attempts` | Number of attempts for last request |
|
|
543
622
|
| `root_router` | Returns the root router (for nested routers) |
|
|
623
|
+
| `base_url` | Base URL used for requests |
|
|
624
|
+
|
|
625
|
+
### Overridable Hooks
|
|
626
|
+
|
|
627
|
+
Define these in your client to change default behavior:
|
|
628
|
+
|
|
629
|
+
| Method | Default |
|
|
630
|
+
|--------|---------|
|
|
631
|
+
| `retry_request?(exception, options)` | `true` for the network errors listed under Retry Configuration |
|
|
632
|
+
| `escape_path(value)` | Percent-encodes path values (`ERB::Util.url_encode`) |
|
|
633
|
+
| `parse_response(response, options)` | Parses the body as JSON, `nil` when empty |
|
|
634
|
+
| `handle_response(response, options, &block)` | Applies `return:`, parsing and the response block |
|
|
635
|
+
| `expected_response_code!(response, codes, options)` | Raises `UnexpectedResponse` for unexpected codes |
|
|
636
|
+
| `get_retry_request_max_retries(options)` | `retries:` option, then `configure_retries`, then 1 |
|
|
637
|
+
| `get_retry_request_sleep_time(exception, options)` | `sleep:` option, then `configure_retries`, then 0.05 |
|
|
544
638
|
|
|
545
639
|
## Requirements
|
|
546
640
|
|
|
547
641
|
- Ruby 3.2+
|
|
548
642
|
- `inheritance-helper` gem (>= 0.2.5)
|
|
643
|
+
- `activesupport` (optional) for `to_query` builders and instrumentation
|
|
549
644
|
|
|
550
645
|
## Contributing
|
|
551
646
|
|
|
@@ -554,7 +649,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/dougyo
|
|
|
554
649
|
1. Fork the repository
|
|
555
650
|
2. Create your feature branch (`git checkout -b feature/my-feature`)
|
|
556
651
|
3. Write tests for your changes
|
|
557
|
-
4. Ensure all tests pass (`bundle exec rspec`)
|
|
652
|
+
4. Ensure all tests pass with full line and branch coverage (`CI=true bundle exec rspec`)
|
|
558
653
|
5. Ensure code style compliance (`bundle exec rubocop`)
|
|
559
654
|
6. Commit your changes using [conventional commits](https://www.conventionalcommits.org/) (`git commit -am 'feat(router): add my feature'`); release notes and version bumps are generated from them
|
|
560
655
|
7. Push to the branch (`git push origin feature/my-feature`)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'erb'
|
|
3
4
|
require 'inheritance-helper'
|
|
4
5
|
require 'json'
|
|
5
6
|
|
|
@@ -177,7 +178,7 @@ module ClientApiBuilder
|
|
|
177
178
|
:put
|
|
178
179
|
when /^(?:patch)/i
|
|
179
180
|
:patch
|
|
180
|
-
when /^(?:delete|remove)/i
|
|
181
|
+
when /^(?:delete|remove|destroy)/i
|
|
181
182
|
:delete
|
|
182
183
|
else
|
|
183
184
|
:get
|
|
@@ -552,8 +553,11 @@ module ClientApiBuilder
|
|
|
552
553
|
self
|
|
553
554
|
end
|
|
554
555
|
|
|
556
|
+
# Percent-encodes everything but RFC 3986 unreserved characters (A-Z a-z 0-9 - . _ ~),
|
|
557
|
+
# so a value inserted into the path, including any '/', stays one segment.
|
|
558
|
+
# Override to change how path values are encoded.
|
|
555
559
|
def escape_path(path)
|
|
556
|
-
path
|
|
560
|
+
ERB::Util.url_encode(path.to_s)
|
|
557
561
|
end
|
|
558
562
|
|
|
559
563
|
def instrument_request
|