pdfmonkey 0.9.0 → 1.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9fe16723c65c8ec16e6436a54fe172f703ac80ca9179c2742fdcf8c0eefedba7
4
- data.tar.gz: 010ea3163095477fe44589a51211ae5cd284a1f88c55bb4186002d4014902f4d
3
+ metadata.gz: b9b2578ff539c12892c1f12d30dfd3ff64bcf57380f9143cb29cfb5d3ab2c39b
4
+ data.tar.gz: 807b5ec97e55da7a6d19adcf1c50e3d7436a4830bfd05148c6529bea7c1b87f0
5
5
  SHA512:
6
- metadata.gz: 5f2c3974bc32769ea21190fb7731d6a0c2bb08d6863f6d377bf4ca5fb0ea55d200235c7ebeba81cd9018e571eebb252c6e73774f821e8991eeb197662096e3cd
7
- data.tar.gz: 30ac1caeb3a3839f5d0e0da4f3cb1605f929055ac900e7232960dd30279dcc3e33bb6da433ea571b775a85b9e9950a6ce52702bc1bdd217191d1a35b49d59db0
6
+ metadata.gz: ddc09a6c959f07fd8d8ec54035d646ae529f0fdf5ff4b5ac14e181bf75992feffa77738d6e6bb0aa234f2a029ba352ab7d21870ab04955e1b8fcee8440d6539c
7
+ data.tar.gz: b7be90236f0a8791c7d09d3189dc177f37cf8229a7b961c793f946664ec8ec98cdc6d4cbfee564e7e5f32fc657a7fd3f9e5f2bfcc1ff5ee34026098bdc07192d
data/CHANGELOG.md CHANGED
@@ -1,6 +1,41 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 1.0.0 - 2026-02-18
4
+
5
+ ### Breaking changes
6
+
7
+ * **Requiring Ruby >= 3.2** — The gem now requires Ruby 3.2 or later.
8
+ * **Exception-based error handling** — API errors now raise `Pdfmonkey::ApiError` (with `errors` and `status_code` attributes) and network errors raise `Pdfmonkey::ConnectionError` instead of returning error hashes. Rescue `Pdfmonkey::Error` for a catch-all.
9
+ * **`Document.generate!` / `Document.generate` signature change** — These methods now use keyword arguments (`document_template_id:`, `payload:`, `meta:`). Positional arguments still work but emit a deprecation warning.
10
+ * **`Document.generate!` raises on failure** — `Document.generate!` now raises `Pdfmonkey::GenerationError` when the document ends with `error` or `failure` status, instead of returning the failed document.
11
+ * **`Document.generate!` polling** — `Document.generate!` now sleeps `poll_interval` seconds (default 0.5s) between status polls instead of busy-looping.
12
+ * **`document_template_id` validation** — `Document.generate!`, `Document.generate`, and `Document.create_draft` now raise `ArgumentError` when `document_template_id` is missing or blank.
13
+ * **Removing `ostruct` dependency** — Attributes are now backed by `Struct` instead of `OpenStruct`. This removes the runtime dependency on the `ostruct` gem.
14
+ * **`Document#attributes` is no longer public** — Access individual attributes through their accessor methods instead.
15
+ * **`User-Agent` is no longer configurable** — The `user_agent` configuration option has been removed. The header is now always `pdfmonkey-ruby/<version>`.
16
+ * **`to_json` omits nil attributes** — `Resource#to_json` now compacts nil values and strips the `errors` attribute from the serialized output.
17
+ * **Resource base class** — All resource classes now inherit from `Pdfmonkey::Resource` which provides shared CRUD operations, attribute management and JSON serialization.
18
+
19
+ ### New features
20
+
21
+ * Adding `Document#generate` and `Document#generate!` instance methods for triggering generation on draft documents
22
+ * Adding `Document#save` as a public method (was private in 0.9.0)
23
+ * Adding `Document.create_draft` for creating draft documents with preview support
24
+ * Adding `Document#update!` for updating document attributes via PUT
25
+ * Adding `Document.list_cards`, `Document.fetch_card`, and `Document.fetch_full` for accessing documents through the `Document` class
26
+ * Adding `output_type` to `Document` attributes
27
+ * Adding resources for:
28
+ * `Engine`
29
+ * `Snippet`
30
+ * `TemplateCard`
31
+ * `TemplateFolder`
32
+ * `Template`
33
+ * `Webhook`
34
+ * `Workspace` (read-only)
35
+ * Adding `CurrentUser.fetch` for retrieving authenticated user info
36
+ * Adding `Pdfmonkey.with_adapter` for per-request adapter scoping (e.g. multi-tenant credentials)
37
+ * Adding persistent HTTP connections with configurable timeouts (`open_timeout`, `read_timeout`, `keep_alive_timeout`)
38
+ * Adding API key validation at request time (raises `Pdfmonkey::Error` if unconfigured)
4
39
 
5
40
  ## 0.9.0
6
41
 
data/README.md CHANGED
@@ -6,7 +6,7 @@ This gem is the quickest way to use the [PDFMonkey](https://www.pdfmonkey.io) AP
6
6
 
7
7
  ## Installation
8
8
 
9
- Add this line to your applications Gemfile:
9
+ Add this line to your application's Gemfile:
10
10
 
11
11
  ```ruby
12
12
  gem 'pdfmonkey'
@@ -32,7 +32,7 @@ PDFMonkey will look for the `PDFMONKEY_PRIVATE_KEY` environment variable. This v
32
32
 
33
33
  #### Setting credentials manually
34
34
 
35
- You can choose to set up your credentials explicitely in your application:
35
+ You can choose to set up your credentials explicitly in your application:
36
36
 
37
37
  ```ruby
38
38
  Pdfmonkey.configure do |config|
@@ -40,25 +40,41 @@ Pdfmonkey.configure do |config|
40
40
  end
41
41
  ```
42
42
 
43
- ### Generating a document
43
+ **Note:** Configuration is global. If you need per-request credentials (e.g. multi-tenant), use `with_adapter`:
44
44
 
45
- Once your App is created in the [PDFMonkey Dashboard](https://dashboard.pdfmonkey.io), create a Document Template and add the content you want to use. Every Document Template has a unique identifier (UUID), get your template’s identifier. Your ready to generate a Document.
45
+ ```ruby
46
+ config = Pdfmonkey::Configuration.new
47
+ config.private_key = 'tenant-specific-key'
48
+
49
+ adapter = Pdfmonkey::Adapter.new(config: config)
50
+
51
+ Pdfmonkey.with_adapter(adapter) do
52
+ Pdfmonkey::Document.generate!(
53
+ document_template_id: 'b13ebd75-…',
54
+ payload: { name: 'John Doe' }
55
+ )
56
+ end
57
+ ```
58
+
59
+ All operations inside the block use the given adapter. Calls outside the block continue using the global configuration.
60
+
61
+ ### Documents
46
62
 
47
63
  #### Synchronous generation
48
64
 
49
- If your want to wait for a Documents generation before continuing with your workflow, use the `generate!` method, it will request a document generation and wait for it to succeed or fail before giving you any answer.
65
+ If you want to wait for a Document's generation before continuing with your workflow, use the `generate!` method, it will request a document generation and wait for it to succeed or fail before giving you any answer.
50
66
 
51
67
  ```ruby
52
- template_id = 'b13ebd75-d290-409b-9cac-8f597ae3e785'
53
- data = { name: 'John Doe' }
54
-
55
- document = Pdfmonkey::Document.generate!(template_id, data)
68
+ document = Pdfmonkey::Document.generate!(
69
+ document_template_id: 'b13ebd75-d290-409b-9cac-8f597ae3e785',
70
+ payload: { name: 'John Doe' }
71
+ )
56
72
 
57
73
  document.status # => 'success'
58
74
  document.download_url # => 'https://…'
59
75
  ```
60
76
 
61
- :warning: The download URL of a document is only valid **for 1 hour**. Passed this delay, reload the document to obtain a new one:
77
+ **Warning:** The download URL of a document is only valid **for 1 hour**. Past this delay, reload the document to obtain a new one:
62
78
 
63
79
  ```ruby
64
80
  document.reload!
@@ -66,49 +82,79 @@ document.reload!
66
82
 
67
83
  #### Asynchronous generation
68
84
 
69
- PDFMonkey was created with an asynchronous workflow in mind. It provides webhooks to inform you of a Documents generation success or failure.
85
+ PDFMonkey was created with an asynchronous workflow in mind. It provides webhooks to inform you of a Document's generation success or failure.
70
86
 
71
- To leverage this behavior and continue working while your Document is generated, use the `generate` method:
87
+ To leverage this behavior and continue working while your Document is being generated, use the `generate` method:
72
88
 
73
89
  ```ruby
74
- template_id = 'b13ebd75-d290-409b-9cac-8f597ae3e785'
75
- data = { name: 'John Doe' }
76
-
77
- document = Pdfmonkey::Document.generate(template_id, data)
90
+ document = Pdfmonkey::Document.generate(
91
+ document_template_id: 'b13ebd75-d290-409b-9cac-8f597ae3e785',
92
+ payload: { name: 'John Doe' }
93
+ )
78
94
 
79
95
  document.status # => 'pending'
80
96
  document.download_url # => nil
81
97
  ```
82
98
 
83
- If you have a webhook URL set up it will be called with you document once the generation is complete. You can simulate it for testing with the following cURL command:
99
+ If you have a webhook URL set up it will be called with your document once the generation is complete. You can simulate it for testing with the following cURL command:
84
100
 
85
- ```ruby
101
+ ```bash
86
102
  curl <url of your app> \
87
103
  -X POST \
88
104
  -H 'Content-Type: application/json' \
89
105
  -d '{
90
106
  "document": {
107
+ "id": "76bebeb9-9eb1-481a-bc3c-faf43dc3ac81",
91
108
  "app_id": "d9ec8249-65ae-4d50-8aee-7c12c1f9683a",
92
- "checksum": "ac0c2b6bcc77e2b01dc6ca6a9f656b2d",
93
109
  "created_at": "2020-01-02T03:04:05.000+01:00",
94
110
  "document_template_id": "f7fbe2b4-a57c-46ee-8422-5ae8cc37daac",
95
- "download_url": "https://example.com/76bebeb9-9eb1-481a-bc3c-faf43dc3ac81.pdf",
96
- "filename": "76bebeb9-9eb1-481a-bc3c-faf43dc3ac81.pdf",
97
- "id": "76bebeb9-9eb1-481a-bc3c-faf43dc3ac81",
98
- "meta": null,
99
- "payload": "{\"name\": \"John Doe\"}",
100
- "preview_url": null,
111
+ "meta": "{\"_filename\":\"my-doc.pdf\"}",
112
+ "output_type": "pdf",
101
113
  "status": "success",
102
- "updated_at": "2020-01-02T03:04:15.000+01:00"
114
+ "updated_at": "2020-01-02T03:04:15.000+01:00",
115
+ "xml_data": null,
116
+ "payload": "{\"name\": \"John Doe\"}",
117
+ "download_url": "https://example.com/76bebeb9-9eb1-481a-bc3c-faf43dc3ac81.pdf",
118
+ "checksum": "ac0c2b6bcc77e2b01dc6ca6a9f656b2d",
119
+ "failure_cause": null,
120
+ "filename": "my-doc.pdf",
121
+ "generation_logs": [],
122
+ "preview_url": "https://preview.pdfmonkey.io/pdf/web/viewer.html?file=https%3A%2F%2Fpreview.pdfmonkey.io%2Fdocument-render%2F76bebeb9-9eb1-481a-bc3c-faf43dc3ac81%2Fac0c2b6bcc77e2b01dc6ca6a9f656b2d",
123
+ "public_share_link": "https://example.com/76bebeb9-9eb1-481a-bc3c-faf43dc3ac81.pdf"
103
124
  }
104
125
  }'
105
126
  ```
106
127
 
128
+ #### Draft documents
129
+
130
+ You can create a draft document that won't be queued for generation.
131
+
132
+ > [!TIP]
133
+ > This is useful for embedding a preview via `preview_url` before triggering generation. That what we do in the PDFMonkey dashboard to show you a preview of your document before generating it, using an `iframe`.
134
+
135
+ ```ruby
136
+ draft = Pdfmonkey::Document.create_draft(
137
+ document_template_id: 'b13ebd75-d290-409b-9cac-8f597ae3e785',
138
+ payload: { name: 'John Doe' }
139
+ )
140
+ draft.status # => 'draft'
141
+ draft.preview_url # => 'https://…'
142
+
143
+ # When ready, trigger generation and wait for completion:
144
+ draft.generate!
145
+ draft.status # => 'success'
146
+ draft.download_url # => 'https://…'
147
+
148
+ # Or trigger generation without waiting:
149
+ draft.generate
150
+ draft.status # => 'pending'
151
+ ```
152
+
107
153
  #### Attaching meta data to the document
108
154
 
109
- In addition to the Documents payload you can add meta data when generating a Document.
155
+ In addition to the Document's payload you can add meta data when generating a Document.
110
156
 
111
- This can be done by passing a third argument to the `generate!` and `generate` methods:
157
+ This can be done by passing the `meta:` keyword argument to the `generate!` and `generate` methods:
112
158
 
113
159
  ```ruby
114
160
  meta = {
@@ -116,69 +162,85 @@ meta = {
116
162
  client_id: '123xxx123'
117
163
  }
118
164
 
119
- document = Pdfmonkey::Document.generate!(template_id, payload, meta)
165
+ document = Pdfmonkey::Document.generate!(
166
+ document_template_id: template_id,
167
+ payload: payload,
168
+ meta: meta
169
+ )
120
170
  document.meta
121
171
  # => '{"_filename":"john-doe-contract.pdf","client_id":"123xxx123"}'
122
172
 
123
- document = Pdfmonkey::Document.generate(template_id, payload, meta)
173
+ document = Pdfmonkey::Document.generate(
174
+ document_template_id: template_id,
175
+ payload: payload,
176
+ meta: meta
177
+ )
124
178
  document.meta
125
179
  # => '{"_filename":"john-doe-contract.pdf","client_id":"123xxx123"}'
126
180
  ```
127
181
 
128
- #### Error handling
182
+ #### Image generation
129
183
 
130
- In case of error, be it an HTTP layer error or an API error, `document.status` will be set to `'error'` and `document.error` will contain the error message.
184
+ Image generation uses the same API flow as PDF generation. The template's `output_type` attribute indicates whether it produces `'pdf'` or `'image'` output. Image-specific options are passed through the `meta` parameter:
131
185
 
132
186
  ```ruby
133
- # Using an unknown template
187
+ doc = Pdfmonkey::Document.generate!(
188
+ document_template_id: template_id,
189
+ payload: payload,
190
+ meta: {
191
+ _type: 'png', # webp (default), png, or jpg
192
+ _width: 800, # pixels
193
+ _height: 600, # pixels
194
+ _quality: 80 # webp only, default 100
195
+ }
196
+ )
197
+ doc.download_url # => URL to the generated image
198
+ ```
134
199
 
135
- template_id = 'unknown'
136
- data = { name: 'John Doe' }
200
+ #### Updating a document
137
201
 
138
- document = Pdfmonkey::Document.generate(template_id, data)
202
+ ```ruby
203
+ document.update!(status: 'pending')
204
+ ```
139
205
 
140
- document.status # => 'error'
141
- document.errors # => ["Document template must exist"]
206
+ #### Listing documents
142
207
 
143
- # If your quota is depleted
144
- document = Pdfmonkey::Document.generate(template_id, data)
208
+ ```ruby
209
+ cards = Pdfmonkey::Document.list_cards(page: 1, status: 'success')
145
210
 
146
- document.status # => 'error'
147
- document.errors # => { "status" => ["You’ve reached your quota for th..."] }
211
+ cards.each do |card|
212
+ puts card.id
213
+ puts card.status
214
+ end
148
215
 
149
- # If the network is down
150
- document = Pdfmonkey::Document.generate(template_id, data)
216
+ cards.current_page # => 1
217
+ cards.total_pages # => 5
151
218
 
152
- document.status # => 'error'
153
- document.errors # => ["Failed to open TCP connection to api.pdfmonkey.io:443 (getaddrinfo: nodename nor servname provided, or not known)"]
219
+ # Navigate pages
220
+ next_cards = cards.next_page
221
+ prev_cards = cards.prev_page
154
222
  ```
155
223
 
156
- ### Fetching a document
224
+ You can filter by `document_template_id:`, `status:`, `workspace_id:`, and `updated_since:`.
225
+
226
+ #### Fetching a document
227
+
228
+ > [!CAUTION]
229
+ > Fetching a full document includes its payload, meaning it could be large depending on the data you provided. We **strongly** recommend using only `fetch_card` unless you have a specific reason to fetch the full document.
157
230
 
158
- You can fetch an existing document using the `.fetch` method:
231
+ You can fetch an existing document using `.fetch` (or its explicit alias `.fetch_full`):
159
232
 
160
233
  ```ruby
161
234
  document = Pdfmonkey::Document.fetch('76bebeb9-9eb1-481a-bc3c-faf43dc3ac81')
162
235
  ```
163
236
 
164
- #### Error handling
165
-
166
- In case of error, be it an HTTP layer error or an API error, `document.status` will be set to `'error'` and `document.error` will contain the error message.
237
+ To fetch just the lightweight card representation (recommended):
167
238
 
168
239
  ```ruby
169
- document = Pdfmonkey::Document.fetch('unknown')
170
-
171
- document.status # => 'error'
172
- document.errors # => ["We couldn't find any Document with ID \"unknown\"..."]
173
-
174
- # If the network is down
175
- document = Pdfmonkey::Document.fetch('95eb0b6e-090b-4195-9b7c-cc3d50099867')
176
-
177
- document.status # => 'error'
178
- document.errors # => ["Failed to open TCP connection to api.pdfmonkey.io:443 (getaddrinfo: nodename nor servname provided, or not known)"]
240
+ card = Pdfmonkey::Document.fetch_card('76bebeb9-9eb1-481a-bc3c-faf43dc3ac81')
179
241
  ```
180
242
 
181
- ### Deleting a document
243
+ #### Deleting a document
182
244
 
183
245
  You can delete an existing document using the `.delete` method:
184
246
 
@@ -196,26 +258,306 @@ document.delete!
196
258
 
197
259
  #### Error handling
198
260
 
199
- In case of error, be it an HTTP layer error or an API error, an error Hash will be returned.
261
+ API errors and network errors raise exceptions:
200
262
 
201
263
  ```ruby
202
- document.delete!
203
- #=> true
264
+ begin
265
+ document = Pdfmonkey::Document.generate(
266
+ document_template_id: template_id,
267
+ payload: data
268
+ )
269
+ rescue Pdfmonkey::ApiError => e
270
+ e.message # => "Document template must exist"
271
+ e.errors # => ["Document template must exist"]
272
+ e.status_code # => 422
273
+ rescue Pdfmonkey::ConnectionError => e
274
+ e.message # => "Failed to open TCP connection to api.pdfmonkey.io:443 ..."
275
+ end
276
+ ```
204
277
 
205
- document.delete!
206
- # {
207
- # errors: ["We couldn't find any Document with ID \"11111111-2222-3333-4444-555555555555\". If ..."],
208
- # status: "error"
209
- # }
278
+ When using `generate!`, an additional exception may be raised if the document's status is `'error'` or `'failure'`:
210
279
 
211
- # If the network is down
212
- document.delete!
213
- # {
214
- # errors: ["Failed to open TCP connection to api.pdfmonkey.io:443 (getaddrinfo: nodename nor servname provided, or not known)"],
215
- # status: "error"
216
- # }
280
+ ```ruby
281
+ begin
282
+ document = Pdfmonkey::Document.generate!(
283
+ document_template_id: template_id,
284
+ payload: data
285
+ )
286
+ rescue Pdfmonkey::GenerationError => e
287
+ e.message # => "Document generation failed: Template error"
288
+ e.document # => #<Pdfmonkey::Document …> (the failed document)
289
+ end
290
+ ```
291
+
292
+ All exception classes inherit from `Pdfmonkey::Error`, so you can rescue broadly:
293
+
294
+ ```ruby
295
+ begin
296
+ document = Pdfmonkey::Document.generate!(
297
+ document_template_id: template_id,
298
+ payload: data
299
+ )
300
+ rescue Pdfmonkey::Error => e
301
+ puts "Something went wrong: #{e.message}"
302
+ end
303
+ ```
304
+
305
+ ### Templates
306
+
307
+ #### Fetching a template
308
+
309
+ > [!CAUTION]
310
+ > Fetching a full template includes its body and settings, which can be large. Use `list_cards` when you only need metadata.
311
+
312
+ ```ruby
313
+ template = Pdfmonkey::Template.fetch('b13ebd75-d290-409b-9cac-8f597ae3e785')
314
+ template.identifier # => 'my-invoice'
315
+ template.body # => '<h1>Invoice</h1>…' (published version)
316
+ template.body_draft # => '<h1>Invoice v2</h1>…' (draft version)
317
+ ```
318
+
319
+ You can also use the explicit alias `.fetch_full`:
320
+
321
+ ```ruby
322
+ template = Pdfmonkey::Template.fetch_full('b13ebd75-d290-409b-9cac-8f597ae3e785')
323
+ ```
324
+
325
+ #### Creating a template
326
+
327
+ When creating a template, attributes like `body`, `scss_style`, `settings`, `sample_data`, and `pdf_engine_id` are automatically written to their draft counterparts (`body_draft`, `scss_style_draft`, etc.):
328
+
329
+ ```ruby
330
+ template = Pdfmonkey::Template.create(
331
+ identifier: 'my-invoice',
332
+ body: '<h1>Invoice</h1>'
333
+ )
334
+ template.body_draft # => '<h1>Invoice</h1>'
335
+ ```
336
+
337
+ #### Updating a template
338
+
339
+ Like `create`, `update!` writes to the draft fields:
340
+
341
+ ```ruby
342
+ template.update!(body: '<h1>Updated Invoice</h1>')
343
+ template.body_draft # => '<h1>Updated Invoice</h1>'
344
+ ```
345
+
346
+ #### Publishing a template
347
+
348
+ Once you're happy with the draft, publish it to copy all draft fields to their published counterparts:
349
+
350
+ ```ruby
351
+ template.publish!
352
+ template.body # => '<h1>Updated Invoice</h1>'
217
353
  ```
218
354
 
355
+ #### Listing templates
356
+
357
+ ```ruby
358
+ cards = Pdfmonkey::Template.list_cards(workspace_id: 'f4ab650c-…')
359
+ ```
360
+
361
+ #### Deleting a template
362
+
363
+ ```ruby
364
+ Pdfmonkey::Template.delete('b13ebd75-…')
365
+ # or
366
+ template.delete!
367
+ ```
368
+
369
+ ### Template Folders
370
+
371
+ ```ruby
372
+ # List folders
373
+ folders = Pdfmonkey::TemplateFolder.list
374
+
375
+ # Create a folder
376
+ folder = Pdfmonkey::TemplateFolder.create(identifier: 'invoices')
377
+
378
+ # Fetch a folder
379
+ folder = Pdfmonkey::TemplateFolder.fetch('folder-id')
380
+
381
+ # Update a folder
382
+ folder.update!(identifier: 'receipts')
383
+
384
+ # Delete a folder
385
+ Pdfmonkey::TemplateFolder.delete('folder-id')
386
+ # or
387
+ folder.delete!
388
+ ```
389
+
390
+ To create a template inside a specific folder, pass the `template_folder_id`:
391
+
392
+ ```ruby
393
+ folder = Pdfmonkey::TemplateFolder.create(identifier: 'invoices')
394
+
395
+ template = Pdfmonkey::Template.create(
396
+ identifier: 'monthly-invoice',
397
+ body: '<h1>Invoice</h1>',
398
+ template_folder_id: folder.id
399
+ )
400
+ ```
401
+
402
+ ### Snippets
403
+
404
+ Snippets are reusable HTML components that can be included in templates.
405
+
406
+ ```ruby
407
+ # List snippets
408
+ snippets = Pdfmonkey::Snippet.list
409
+
410
+ # Create a snippet
411
+ snippet = Pdfmonkey::Snippet.create(
412
+ identifier: 'header',
413
+ code: '<div class="header">…</div>',
414
+ workspace_id: 'f4ab650c-…'
415
+ )
416
+
417
+ # Fetch a snippet
418
+ snippet = Pdfmonkey::Snippet.fetch('snippet-id')
419
+
420
+ # Update a snippet
421
+ snippet.update!(code: '<div class="header">Updated</div>')
422
+
423
+ # Delete a snippet
424
+ Pdfmonkey::Snippet.delete('snippet-id')
425
+ # or
426
+ snippet.delete!
427
+ ```
428
+
429
+ ### Workspaces
430
+
431
+ Workspaces are read-only resources. They can be listed and fetched but not created, updated, or deleted through the API.
432
+
433
+ ```ruby
434
+ # List workspaces
435
+ workspaces = Pdfmonkey::Workspace.list_cards
436
+
437
+ workspaces.each do |workspace|
438
+ puts workspace.identifier
439
+ end
440
+
441
+ # Fetch a workspace
442
+ workspace = Pdfmonkey::Workspace.fetch('workspace-id')
443
+ workspace.identifier # => 'my-app'
444
+ ```
445
+
446
+ ### Webhooks
447
+
448
+ Webhooks allow you to receive notifications when documents are generated.
449
+
450
+ ```ruby
451
+ # Create a webhook for all templates in a workspace
452
+ webhook = Pdfmonkey::Webhook.create(
453
+ url: 'https://example.com/webhooks/pdfmonkey',
454
+ event: 'document.generation.completed',
455
+ workspace_id: 'f4ab650c-…'
456
+ )
457
+
458
+ # Optionally restrict to specific templates
459
+ webhook = Pdfmonkey::Webhook.create(
460
+ url: 'https://example.com/webhooks/pdfmonkey',
461
+ event: 'document.generation.completed',
462
+ workspace_id: 'f4ab650c-…',
463
+ document_template_ids: ['tpl-1', 'tpl-2']
464
+ )
465
+
466
+ # You can also specify a custom channel for routing
467
+ webhook = Pdfmonkey::Webhook.create(
468
+ url: 'https://example.com/webhooks/pdfmonkey',
469
+ event: 'document.generation.completed',
470
+ workspace_id: 'f4ab650c-…',
471
+ custom_channel: 'invoices'
472
+ )
473
+
474
+ # Delete a webhook
475
+ Pdfmonkey::Webhook.delete('webhook-id')
476
+ # or
477
+ webhook.delete!
478
+ ```
479
+
480
+ ### Engines
481
+
482
+ List available PDF rendering engines:
483
+
484
+ ```ruby
485
+ engines = Pdfmonkey::Engine.list
486
+
487
+ engines.each do |engine|
488
+ puts "#{engine.name} (deprecated: #{engine.deprecated_on || 'no'})"
489
+ end
490
+ ```
491
+
492
+ You can use an engine when creating a template:
493
+
494
+ ```ruby
495
+ engines = Pdfmonkey::Engine.list
496
+ v4 = engines.find { |e| e.name == 'v4' }
497
+
498
+ template = Pdfmonkey::Template.create(
499
+ identifier: 'my-template',
500
+ body: '<h1>Hello</h1>',
501
+ pdf_engine_id: v4.id
502
+ )
503
+ ```
504
+
505
+ ### Current User
506
+
507
+ Retrieve information about the authenticated user:
508
+
509
+ ```ruby
510
+ user = Pdfmonkey::CurrentUser.fetch
511
+
512
+ user.email # => 'user@example.com'
513
+ user.current_plan # => 'pro'
514
+ user.available_documents # => 1000
515
+ ```
516
+
517
+ ### Pagination
518
+
519
+ All list methods return `Pdfmonkey::Collection` objects that support pagination:
520
+
521
+ ```ruby
522
+ collection = Pdfmonkey::Document.list_cards(page: 1)
523
+
524
+ collection.current_page # => 1
525
+ collection.total_pages # => 5
526
+ collection.next_page_number # => 2
527
+ collection.prev_page_number # => nil
528
+
529
+ # Navigate to next/previous pages
530
+ next_page = collection.next_page # => Collection or nil
531
+ prev_page = collection.prev_page # => Collection or nil
532
+ ```
533
+
534
+ Collections are `Enumerable`. Methods like `.each`, `.map`, and `.select` operate on the items **of the current page only** — they do not automatically fetch subsequent pages:
535
+
536
+ ```ruby
537
+ # These all act on the current page's items
538
+ collection.each { |item| puts item.id }
539
+ collection.map(&:status)
540
+ collection.select { |item| item.status == 'success' }
541
+
542
+ # To process all pages, navigate manually
543
+ page = Pdfmonkey::Template.list_cards(page: 1)
544
+ while page
545
+ page.each { |item| process(item) }
546
+ page = page.next_page
547
+ end
548
+ ```
549
+
550
+ ### Serialization
551
+
552
+ All resources support `to_json` and `to_h`:
553
+
554
+ ```ruby
555
+ document.to_json # => '{"document":{"id":"…","status":"success",…}}'
556
+ document.to_h # => {id: "…", status: "success", errors: nil, …}
557
+ ```
558
+
559
+ `to_json` wraps attributes under the resource's API member key, omits `nil` values and strips the `errors` attribute (it is intended for API requests). Use `to_h` when you need the full attribute hash for logging or caching.
560
+
219
561
  ## Development
220
562
 
221
563
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -224,7 +566,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
224
566
 
225
567
  ## Contributing
226
568
 
227
- Bug reports and pull requests are welcome on GitHub at https://github.com/simonc/pdfmonkey. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
569
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pdfmonkeyio/pdfmonkey-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
228
570
 
229
571
  ## License
230
572
 
@@ -232,4 +574,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
232
574
 
233
575
  ## Code of Conduct
234
576
 
235
- Everyone interacting in the Pdfmonkey projects codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/simonc/pdfmonkey/blob/master/CODE_OF_CONDUCT.md).
577
+ Everyone interacting in the Pdfmonkey project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/pdfmonkeyio/pdfmonkey-ruby/blob/master/CODE_OF_CONDUCT.md).