printavo-ruby 0.17.0 → 0.18.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 59696533b085e116847901e6b03e9d3687e1a7485b513e350452351d990d9ed2
4
- data.tar.gz: b435b6411596af3cb65310f2172f83d25d9ee1e0f317d05d0ad1a25f975c27ad
3
+ metadata.gz: d2291442eb2c04c3ead1543773bcd188723de181093941cdc8519681b5b43e4b
4
+ data.tar.gz: 00274b8c115044888d4099465521acf974c0356261130eb6aacaa21f1d4aa5ce
5
5
  SHA512:
6
- metadata.gz: 0d08433f369b1f5d5943d089558daf52c875ca74d0d7e377ea7b93857b2bc7d905699a21d627a0d23c9ad398d8131a078ec509c36d7d83efb8175c98e877f956
7
- data.tar.gz: 9c28bdd515691f4f5ec0b211883a42372cfa28df76f23a2b9608ea9a871b83340f8cca539360747a1e16b9274aa1c9593ab434db3f06f700535c89361504e009
6
+ metadata.gz: fdd286fc6a29e0313aed8f7ebbea302f7737e17f44144a524396eadcde292d70d9a87b610d22ec3bd692a9a93572d7bb844c861c71dd1e87435c59a3cd465ade
7
+ data.tar.gz: 29719257b52dfd20f4f9da862e3ccca5df84f8dc64dfb564421a73cbe1b88e46e2a5b36891066ed1e43607e30a3e9a9c6ceecdb3697ac85ce53c2eb1a17b9afa
data/docs/CACHING.md CHANGED
@@ -205,26 +205,81 @@ how frequently staff update records.
205
205
 
206
206
  ---
207
207
 
208
- ## Future: Built-In Cache Adapter
208
+ ## Built-In Cache Adapter
209
209
 
210
- A future version of `printavo-ruby` may support an optional cache adapter
211
- passed directly to the client:
210
+ As of v0.18.0, `Printavo::Client` accepts an optional `cache:` argument — any
211
+ object responding to `fetch(key, expires_in:) { }` and `delete(key)`. This
212
+ matches the `Rails.cache` interface, so no adapter is needed in Rails apps.
213
+
214
+ ### With Rails.cache
215
+
216
+ ```ruby
217
+ client = Printavo::Client.new(
218
+ email: ENV["PRINTAVO_EMAIL"],
219
+ token: ENV["PRINTAVO_TOKEN"],
220
+ cache: Rails.cache, # Memcache, Redis, Solid Cache — anything Rails supports
221
+ default_ttl: 300 # seconds; default is 300 (5 minutes)
222
+ )
223
+
224
+ # All queries are automatically cached and deduped:
225
+ client.customers.all # fetches from API, stores result
226
+ client.customers.all # returns cached result — no HTTP request
227
+ ```
228
+
229
+ ### With Printavo::MemoryStore (no Rails dependency)
212
230
 
213
231
  ```ruby
214
- # Possible future API — not implemented in v0.x
215
232
  client = Printavo::Client.new(
216
233
  email: ENV["PRINTAVO_EMAIL"],
217
234
  token: ENV["PRINTAVO_TOKEN"],
218
- cache: Rails.cache, # any object responding to fetch/delete
219
- default_ttl: 300
235
+ cache: Printavo::MemoryStore.new,
236
+ default_ttl: 600
220
237
  )
238
+ ```
239
+
240
+ `Printavo::MemoryStore` is a thread-safe, TTL-aware in-memory store included
241
+ in the gem. No configuration required.
221
242
 
222
- # Would cache automatically:
223
- client.customers.all # cached for 300s by default
224
- client.orders.find(1) # cached per-id
243
+ ### Without a cache (default)
244
+
245
+ ```ruby
246
+ client = Printavo::Client.new(
247
+ email: ENV["PRINTAVO_EMAIL"],
248
+ token: ENV["PRINTAVO_TOKEN"]
249
+ )
250
+ # cache: nil — every query hits the API
251
+ ```
252
+
253
+ ### Cache key generation
254
+
255
+ Cache keys are generated from a SHA-256 digest of the normalized query document
256
+ and variables: `"printavo:gql:<16-hex-chars>"`. Whitespace differences in query
257
+ strings do not cause cache misses.
258
+
259
+ ### Mutations are never cached
260
+
261
+ `client.graphql.mutate(...)` always bypasses the cache, regardless of the
262
+ `cache:` setting. Only `client.graphql.query(...)` (and resource methods that
263
+ call it) are cache-aware.
264
+
265
+ ### Custom cache stores
266
+
267
+ Any object with this interface works:
268
+
269
+ ```ruby
270
+ class MyCache
271
+ def fetch(key, expires_in: nil)
272
+ # return cached value, or call block and store the result
273
+ end
274
+
275
+ def delete(key)
276
+ # remove key from cache
277
+ end
278
+ end
225
279
  ```
226
280
 
227
- Track this feature in [FUTURE.md](../FUTURE.md).
281
+ This is exactly the interface `ActiveSupport::Cache::Store` exposes, so
282
+ Solid Cache, Dalli (Memcache), and Redis Cache Store all work out of the box.
228
283
 
229
284
  ---
230
285
 
data/docs/FUTURE.md CHANGED
@@ -6,81 +6,40 @@ initial `0.x` releases. Contributions and discussions welcome!
6
6
 
7
7
  ## Planned Features
8
8
 
9
- ### CLI (Thor-based)
9
+ ### Client-Side Aggregation Helpers
10
10
 
11
- A `printavo` command-line tool built with [Thor](https://github.com/rails/thor):
11
+ Printavo's V2 GraphQL API is a transactional API — it has no pre-aggregated
12
+ analytics or reporting endpoints. Any analytics must be computed by paging
13
+ through existing resources in Ruby.
12
14
 
13
- ```bash
14
- printavo customers
15
- printavo orders
16
- printavo orders find 12345
17
- printavo analytics revenue
18
- printavo sync orders --to crm
19
- ```
20
-
21
- Planned version: `0.7.0`
22
-
23
- ### Retry/Backoff
24
-
25
- Intelligent rate limit handling with exponential backoff:
15
+ Potential helpers that would add value:
26
16
 
27
17
  ```ruby
28
- client = Printavo::Client.new(
29
- email: ENV["PRINTAVO_EMAIL"],
30
- token: ENV["PRINTAVO_TOKEN"],
31
- max_retries: 3,
32
- retry_on_rate_limit: true
33
- )
34
- ```
18
+ # Revenue across all invoices in a date range
19
+ client.invoices.revenue_summary(after: "2026-01-01")
20
+ # => { total: "142300.00", count: 87, average: "1636.78" }
35
21
 
36
- Planned version: `0.8.0`
22
+ # Order counts grouped by status
23
+ client.orders.status_breakdown
24
+ # => { in_production: 12, approved: 5, completed: 230, ... }
37
25
 
38
- ### Analytics / Reporting Expansion
26
+ # Most active customers by order count
27
+ client.customers.top(limit: 10, by: :order_count)
39
28
 
40
- Richer wrappers for Printavo's analytics queries (revenue, job counts,
41
- customer activity, turnaround times).
42
-
43
- Planned version: `0.6.0`
44
-
45
- ### Mutations (Create / Update)
46
-
47
- Support for creating and updating resources:
48
-
49
- ```ruby
50
- client.customers.create(first_name: "Jane", last_name: "Smith", email: "jane@example.com")
51
- client.orders.update("99", nickname: "Rush Job")
29
+ # Average turnaround time (created_at updated_at) per status
30
+ client.orders.avg_turnaround
52
31
  ```
53
32
 
54
- Planned version: `0.5.0`
33
+ These helpers would page all relevant records locally and compute aggregates
34
+ in Ruby. Because they require full pagination, using the built-in cache adapter
35
+ is strongly recommended before implementing these in production workflows.
55
36
 
56
- ### Built-In Cache Adapter
57
-
58
- Optional cache layer that plugs into any cache store:
59
-
60
- ```ruby
61
- client = Printavo::Client.new(
62
- email: ENV["PRINTAVO_EMAIL"],
63
- token: ENV["PRINTAVO_TOKEN"],
64
- cache: Rails.cache # or a Redis client, etc.
65
- )
66
- ```
67
-
68
- See [docs/CACHING.md](docs/CACHING.md) for current caching recommendations.
37
+ See [docs/CACHING.md](docs/CACHING.md) for caching options.
69
38
 
70
39
  ## Visualization
71
40
 
72
- ### Workflow Diagram Generation (SVG/PNG)
73
-
74
- Generate a visual map of a shop's Printavo status workflow:
75
-
76
- ```ruby
77
- client.workflow.diagram(format: :svg)
78
- # => Outputs an SVG flowchart: Quote → Approved → In Production → Completed
79
- ```
80
-
81
- Implementation options:
82
- - [ruby-graphviz](https://github.com/glejeune/Ruby-Graphviz) — DOT → SVG/PNG
83
- - Pure Ruby → Mermaid output (copy-paste into docs or GitHub markdown)
41
+ See **[docs/VISUALIZATION.md](VISUALIZATION.md)** for workflow diagram generation —
42
+ Mermaid, DOT, ASCII, and SVG output via the standalone example script.
84
43
 
85
44
  ## Multi-Language SDK Family
86
45
 
data/docs/TODO.md CHANGED
@@ -402,6 +402,30 @@ return values — they are exposed via model field accessors, not separate resou
402
402
  - [x] `Printavo::Enums::TransactionCategory`
403
403
  - [x] `Printavo::Enums::TransactionSource`
404
404
 
405
+ ### v0.17.0 — GraphQL Interface Field Coverage ✅
406
+
407
+ - [x] `VisualIDed` interface — `visualId` on all applicable models
408
+ - [x] `Timestamps` interface — `createdAt` / `updatedAt` on all applicable models
409
+ - [x] `MailAddress` interface — address fields on all applicable models
410
+
411
+ ### v0.18.0 — Built-In Cache Adapter ✅
412
+
413
+ - [x] Optional `cache:` kwarg on `Printavo::Client` (any `Rails.cache`-compatible store)
414
+ - [x] `Printavo::MemoryStore` — thread-safe TTL-aware in-memory store (no Rails dependency)
415
+ - [x] `GraphqlClient` caching: `#query` is cache-aware; `#mutate` always bypasses
416
+ - [x] Stable SHA-256 cache key from normalized query + variables
417
+ - [x] `default_ttl: 300` configurable per-client
418
+ - [x] `docs/CACHING.md` updated with built-in adapter usage examples
419
+
420
+ ### v0.18.1 — Examples: Diagramming & Reporting
421
+
422
+ - [x] `examples/diagramming/workflow_diagram.rb` — Mermaid, DOT, ASCII, SVG (dot CLI + ruby-graphviz gem)
423
+ - [x] `examples/reporting/sales_report.rb` — today, week, month, quarter, YTD, last year, custom range
424
+ - [x] `examples/reporting/sales_tax_report.rb` — invoice-level estimates + fee-level taxable detail
425
+ - [x] `examples/reporting/customers_export.rb` — CSV, XLSX, XLS, vCard (.vcf), HubSpot, Salesforce, Mailchimp, Constant Contact, Beehiiv, Brevo, ActiveCampaign
426
+ - [x] `examples/reporting/outstanding_tasks.rb` — overdue, due today, this week, upcoming, no due date; by-assignee summary; HTML calendar export
427
+ - [x] `docs/FUTURE.md` — Workflow Diagram section replaced with examples reference and rationale
428
+
405
429
  ### v0.99.0 — API Freeze
406
430
 
407
431
  - [ ] Community feedback integration
@@ -418,11 +442,26 @@ return values — they are exposed via model field accessors, not separate resou
418
442
 
419
443
  ## Future / Stretch Goals
420
444
 
421
- ### Built-In Cache Adapter
445
+ ### Client-Side Aggregation Helpers
422
446
 
423
- - [ ] Optional `cache:` kwarg on `Printavo::Client`
424
- - [ ] Adapter interface: `read(key)` / `write(key, value, ttl:)` / `delete(key)`
425
- - [ ] `Rails.cache`, Redis, and in-memory adapter support
447
+ > **Note:** Printavo's V2 GraphQL API exposes no pre-aggregated analytics or
448
+ > reporting endpoints. All "analytics" must be computed locally by paging
449
+ > through existing resources. A cache adapter (see below) is a prerequisite
450
+ > before these helpers would be practical in production.
451
+
452
+ - [ ] `Invoices#revenue_summary(after:, before:)` — page invoices, return total/count/average
453
+ - [ ] `Orders#status_breakdown` — group order count by status key
454
+ - [ ] `Customers#top(limit:, by:)` — rank customers by order count or revenue
455
+ - [ ] `Orders#avg_turnaround` — average time from `created_at` to most recent `updated_at`
456
+
457
+ ### Built-In Cache Adapter ✅ (v0.18.0)
458
+
459
+ - [x] Optional `cache:` kwarg on `Printavo::Client`
460
+ - [x] Duck-typed adapter interface: `fetch(key, expires_in:) { }` / `delete(key)` — matches `Rails.cache`
461
+ - [x] `Printavo::MemoryStore` — thread-safe in-memory store for non-Rails usage
462
+ - [x] `GraphqlClient#query` is cache-aware; `#mutate` always bypasses cache
463
+ - [x] Stable SHA-256 cache key from normalized query + variables
464
+ - [x] `default_ttl: 300` configurable per-client
426
465
 
427
466
  ### Workflow Diagram Generation
428
467
 
@@ -430,13 +469,6 @@ return values — they are exposed via model field accessors, not separate resou
430
469
  - [ ] `ruby-graphviz` backend (DOT → SVG/PNG)
431
470
  - [ ] Mermaid output option (embed in GitHub markdown)
432
471
 
433
- ### Multi-Language SDK Family
434
-
435
- - [ ] `printavo-python`
436
- - [ ] `printavo-swift`
437
- - [ ] `printavo-zig`
438
- - [ ] `printavo-odin`
439
-
440
472
  ---
441
473
 
442
474
 
@@ -0,0 +1,81 @@
1
+ <!-- docs/VISUALIZATION.md -->
2
+ # Visualization for [printavo-ruby](https://github.com/scarver2/printavo-ruby)
3
+
4
+ ## Workflow Diagram Generation
5
+
6
+ See **[examples/diagramming/workflow_diagram.rb](../examples/diagramming/workflow_diagram.rb)**
7
+ for a complete, runnable example covering all four output formats.
8
+
9
+ Adding diagram generation to the gem itself would require either shipping
10
+ `ruby-graphviz` as a hard dependency or assuming a system `dot` binary —
11
+ both are burdensome for a Ruby API client. The standalone example gives
12
+ consumers full control over their output format and toolchain without
13
+ bloating the gem.
14
+
15
+ **Supported formats in the example:**
16
+
17
+ | Format | Dependency | Best for |
18
+ |---|---|---|
19
+ | `:ascii` | none | Terminal output, quick sanity-check |
20
+ | `:mermaid` | none | GitHub README/issues/PRs, VS Code preview |
21
+ | `:dot` | none (text) | Piping to `dot -Tsvg` or any Graphviz renderer |
22
+ | `:svg` | `brew install graphviz` or `gem install ruby-graphviz` | Production-grade visual files |
23
+
24
+ Statuses are fetched via `client.statuses.all` and rendered as a linear
25
+ left-to-right flow in the order Printavo returns them. Printavo has no
26
+ explicit transition edges in its API — the workflow is an implied sequence.
27
+
28
+ ### Quick Start
29
+
30
+ ```bash
31
+ cd examples/diagramming
32
+ cp .env.example .env # fill in PRINTAVO_EMAIL and PRINTAVO_TOKEN
33
+ gem install printavo-ruby dotenv
34
+ ruby workflow_diagram.rb
35
+ ```
36
+
37
+ Output files written to the current directory:
38
+
39
+ | File | Format |
40
+ |---|---|
41
+ | *(stdout)* | ASCII chain |
42
+ | *(stdout)* | Mermaid fenced block |
43
+ | `workflow.dot` | Graphviz DOT source |
44
+ | `workflow_cli.svg` | SVG via `dot` CLI (if graphviz installed) |
45
+ | `workflow_gv.svg` | SVG via `ruby-graphviz` gem (if installed) |
46
+
47
+ ### Rendering DOT to Other Formats
48
+
49
+ ```bash
50
+ # SVG
51
+ dot -Tsvg workflow.dot > workflow.svg
52
+
53
+ # PNG
54
+ dot -Tpng workflow.dot > workflow.png
55
+
56
+ # PDF
57
+ dot -Tpdf workflow.dot > workflow.pdf
58
+ ```
59
+
60
+ ### Embedding Mermaid in GitHub Markdown
61
+
62
+ Paste the Mermaid output between fences in any `.md` file:
63
+
64
+ ````markdown
65
+ ```mermaid
66
+ flowchart LR
67
+ s_quote["Quote"] --> s_in_production["In Production"] --> s_completed["Completed"]
68
+ ```
69
+ ````
70
+
71
+ GitHub renders it automatically in READMEs, issues, PRs, and wiki pages.
72
+
73
+ ---
74
+
75
+ ## Colophon
76
+
77
+ [MIT License](LICENSE)
78
+
79
+ &copy;2026 [Stan Carver II](https://stancarver.com)
80
+
81
+ ![Made in Texas](https://raw.githubusercontent.com/scarver2/howdy-world/master/_dashboard/www/assets/made-in-texas.png)
@@ -8,21 +8,38 @@ module Printavo
8
8
  # Creates a new Printavo API client. Each instance is independent —
9
9
  # multiple clients with different credentials can coexist in one process.
10
10
  #
11
- # @param email [String] the email address associated with your Printavo account
12
- # @param token [String] the API token from your Printavo My Account page
13
- # @param timeout [Integer] HTTP timeout in seconds (default: 30)
14
- # @param max_retries [Integer] max retry attempts on 5xx/429 responses (default: 2)
15
- # @param retry_on_rate_limit [Boolean] retry automatically on 429 Too Many Requests (default: true)
11
+ # @param email [String] the email address associated with your Printavo account
12
+ # @param token [String] the API token from your Printavo My Account page
13
+ # @param timeout [Integer] HTTP timeout in seconds (default: 30)
14
+ # @param max_retries [Integer] max retry attempts on 5xx/429 responses (default: 2)
15
+ # @param retry_on_rate_limit [Boolean] retry automatically on 429 Too Many Requests (default: true)
16
+ # @param cache [#fetch, #delete] optional cache store; any object responding to
17
+ # +fetch(key, expires_in:) { }+ and +delete(key)+,
18
+ # e.g. +Rails.cache+ or +Printavo::MemoryStore.new+
19
+ # @param default_ttl [Integer] TTL in seconds for cached queries (default: 300)
16
20
  #
17
- # @example
21
+ # @example No caching (default)
18
22
  # client = Printavo::Client.new(
19
23
  # email: ENV["PRINTAVO_EMAIL"],
20
24
  # token: ENV["PRINTAVO_TOKEN"]
21
25
  # )
22
- # client.customers.all
23
- # client.orders.find("12345")
24
- # client.graphql.query("{ customers { nodes { id } } }")
25
- def initialize(email:, token:, timeout: 30, max_retries: 2, retry_on_rate_limit: true)
26
+ #
27
+ # @example With Rails.cache
28
+ # client = Printavo::Client.new(
29
+ # email: ENV["PRINTAVO_EMAIL"],
30
+ # token: ENV["PRINTAVO_TOKEN"],
31
+ # cache: Rails.cache,
32
+ # default_ttl: 300
33
+ # )
34
+ #
35
+ # @example With built-in in-memory store
36
+ # client = Printavo::Client.new(
37
+ # email: ENV["PRINTAVO_EMAIL"],
38
+ # token: ENV["PRINTAVO_TOKEN"],
39
+ # cache: Printavo::MemoryStore.new
40
+ # )
41
+ def initialize(email:, token:, timeout: 30, max_retries: 2, retry_on_rate_limit: true, # rubocop:disable Metrics/ParameterLists
42
+ cache: nil, default_ttl: 300)
26
43
  connection = Connection.new(
27
44
  email: email,
28
45
  token: token,
@@ -30,7 +47,7 @@ module Printavo
30
47
  max_retries: max_retries,
31
48
  retry_on_rate_limit: retry_on_rate_limit
32
49
  ).build
33
- @graphql = GraphqlClient.new(connection)
50
+ @graphql = GraphqlClient.new(connection, cache: cache, default_ttl: default_ttl)
34
51
  end
35
52
 
36
53
  def account
@@ -1,12 +1,20 @@
1
1
  # lib/printavo/graphql_client.rb
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'digest'
4
5
  require 'json'
5
6
 
6
7
  module Printavo
7
8
  class GraphqlClient
8
- def initialize(connection)
9
- @connection = connection
9
+ # @param connection [Faraday::Connection]
10
+ # @param cache [#fetch, #delete, nil] any cache store implementing
11
+ # +fetch(key, expires_in:) { }+ and +delete(key)+,
12
+ # e.g. +Rails.cache+, +Printavo::MemoryStore.new+, or +nil+
13
+ # @param default_ttl [Integer] default TTL in seconds applied to cached queries (default: 300)
14
+ def initialize(connection, cache: nil, default_ttl: 300)
15
+ @connection = connection
16
+ @cache = cache
17
+ @default_ttl = default_ttl
10
18
  end
11
19
 
12
20
  # Executes a GraphQL query and returns the parsed `data` hash.
@@ -22,7 +30,11 @@ module Printavo
22
30
  # variables: { id: "42" }
23
31
  # )
24
32
  def query(query_string, variables: {})
25
- execute(query_string, variables: variables)
33
+ return execute(query_string, variables: variables) unless @cache
34
+
35
+ @cache.fetch(cache_key(query_string, variables), expires_in: @default_ttl) do
36
+ execute(query_string, variables: variables)
37
+ end
26
38
  end
27
39
 
28
40
  # Executes a GraphQL mutation and returns the parsed `data` hash.
@@ -86,6 +98,13 @@ module Printavo
86
98
 
87
99
  private
88
100
 
101
+ # Generates a stable, namespaced cache key from the query document and variables.
102
+ # Whitespace in the query is collapsed so formatting differences don't cause misses.
103
+ def cache_key(query_string, variables)
104
+ payload = JSON.generate([query_string.gsub(/\s+/, ' ').strip, variables])
105
+ "printavo:gql:#{Digest::SHA256.hexdigest(payload)[0, 16]}"
106
+ end
107
+
89
108
  def execute(document, variables: {})
90
109
  response = @connection.post('') do |req|
91
110
  req.body = JSON.generate(query: document, variables: variables)
@@ -0,0 +1,76 @@
1
+ # lib/printavo/memory_store.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Printavo
5
+ # A simple thread-safe in-memory cache store for use without Rails or Redis.
6
+ # Implements the same +fetch+ / +delete+ interface as +Rails.cache+, so it
7
+ # can be swapped for any compatible store without changing call sites.
8
+ #
9
+ # @example Standalone use
10
+ # client = Printavo::Client.new(
11
+ # email: ENV["PRINTAVO_EMAIL"],
12
+ # token: ENV["PRINTAVO_TOKEN"],
13
+ # cache: Printavo::MemoryStore.new
14
+ # )
15
+ #
16
+ # @example With custom default TTL
17
+ # client = Printavo::Client.new(
18
+ # email: ENV["PRINTAVO_EMAIL"],
19
+ # token: ENV["PRINTAVO_TOKEN"],
20
+ # cache: Printavo::MemoryStore.new,
21
+ # default_ttl: 600 # 10 minutes
22
+ # )
23
+ class MemoryStore
24
+ def initialize
25
+ @store = {}
26
+ @expires_at = {}
27
+ @mutex = Mutex.new
28
+ end
29
+
30
+ # Returns the cached value for +key+, or calls the block, stores the
31
+ # result, and returns it. Expired entries are treated as missing.
32
+ #
33
+ # @param key [String]
34
+ # @param expires_in [Integer, nil] TTL in seconds; +nil+ means no expiry
35
+ # @yieldreturn the value to cache on a miss
36
+ # @return the cached or freshly-computed value
37
+ def fetch(key, expires_in: nil)
38
+ @mutex.synchronize do
39
+ cached = read(key)
40
+ return cached unless cached.nil?
41
+
42
+ yield.tap { |v| write(key, v, expires_in: expires_in) }
43
+ end
44
+ end
45
+
46
+ # Removes +key+ from the cache.
47
+ #
48
+ # @param key [String]
49
+ # @return [nil]
50
+ def delete(key)
51
+ @mutex.synchronize do
52
+ @store.delete(key)
53
+ @expires_at.delete(key)
54
+ end
55
+ nil
56
+ end
57
+
58
+ private
59
+
60
+ def read(key)
61
+ exp = @expires_at[key]
62
+ if @store.key?(key) && (exp.nil? || Time.now < exp)
63
+ @store[key]
64
+ else
65
+ @store.delete(key)
66
+ @expires_at.delete(key)
67
+ nil
68
+ end
69
+ end
70
+
71
+ def write(key, value, expires_in: nil)
72
+ @store[key] = value
73
+ @expires_at[key] = Time.now + expires_in if expires_in
74
+ end
75
+ end
76
+ end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Printavo
5
- VERSION = '0.17.0'
5
+ VERSION = '0.18.1'
6
6
  end
data/lib/printavo.rb CHANGED
@@ -11,6 +11,7 @@ require_relative 'printavo/connection'
11
11
  require_relative 'printavo/enums'
12
12
  require_relative 'printavo/errors'
13
13
  require_relative 'printavo/graphql_client'
14
+ require_relative 'printavo/memory_store'
14
15
  require_relative 'printavo/page'
15
16
  require_relative 'printavo/models/base'
16
17
  require_relative 'printavo/models/account'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: printavo-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.0
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stan Carver II
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-01 00:00:00.000000000 Z
11
+ date: 2026-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -285,6 +285,7 @@ files:
285
285
  - docs/CONTRIBUTING.md
286
286
  - docs/FUTURE.md
287
287
  - docs/TODO.md
288
+ - docs/VISUALIZATION.md
288
289
  - lib/printavo.rb
289
290
  - lib/printavo/cli.rb
290
291
  - lib/printavo/client.rb
@@ -465,6 +466,7 @@ files:
465
466
  - lib/printavo/graphql/vendors/all.graphql
466
467
  - lib/printavo/graphql/vendors/find.graphql
467
468
  - lib/printavo/graphql_client.rb
469
+ - lib/printavo/memory_store.rb
468
470
  - lib/printavo/models/account.rb
469
471
  - lib/printavo/models/approval_request.rb
470
472
  - lib/printavo/models/base.rb