og_pilot_ruby 0.4.2 → 0.4.4

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: 7dfbdfc6efc8c0fc36a6b92c6b99af56c0e0ef3b3831d7b91b8e56056b09dd16
4
- data.tar.gz: 4fe12f91a949bd6117a915b48b88df8d25fda2d9665a5bba1d78533557cad202
3
+ metadata.gz: 367bb4f8ea0c4a82c261f4403a42ae5bbf2afa25069bad87250c884ca343526b
4
+ data.tar.gz: 753f95ace14cc5d6b1895a286174810457837ba9e7b0fdb2dcb646bfa9f8ea78
5
5
  SHA512:
6
- metadata.gz: 6cb305db104f09b0fb0cfc430f8fd1fdc276d02d048e4f4936bde4ba5273569568e351e5fdafe9287bb33fc9f9ba5d94279c38f161ebb4f1a36a87aa56a331c0
7
- data.tar.gz: e144441605b592d95c672242cb90813c1056dc498fd045a10ee0882dfa157a30600585f11effa2fb976983189e14a53170888a3323c8f4cc3198f458c96a6ca8
6
+ metadata.gz: ec298c3d0d781c259aece2d385f1f53a0e26633295b0bc8b1624ab85123a6cb5840a0b4f81a4e303940c425b618d270b8f504e19cb7654466046c11dbbda666b
7
+ data.tar.gz: 29008c57e899486428e21c2395e0f3cdfc2010d1bc533cd93102e0ba17a41cabc96c94295bf4b3a46247ebe8a6ff5e183a7981e6c8d540406cbea3e22e29eb9f
data/README.md CHANGED
@@ -29,6 +29,8 @@ OgPilotRuby.configure do |config|
29
29
  config.api_key = ENV.fetch("OG_PILOT_API_KEY")
30
30
  config.domain = ENV.fetch("OG_PILOT_DOMAIN")
31
31
  # config.strip_extensions = true
32
+ # config.cache_store = Rails.cache
33
+ # config.cache_ttl = 86_400
32
34
  end
33
35
  ```
34
36
 
@@ -61,6 +63,12 @@ image_url = OgPilotRuby.create_image(
61
63
  If you omit `iat`, OG Pilot will cache the image indefinitely. Provide an `iat` to
62
64
  refresh the cache daily.
63
65
 
66
+ When `config.cache_store` is set, the client can also cache generated responses
67
+ locally:
68
+
69
+ - With `iat`: cache for `cache_ttl` seconds
70
+ - Without `iat`: cache for `7 * cache_ttl` seconds
71
+
64
72
  ### Fail-safe behavior
65
73
 
66
74
  `create_image` is non-blocking. If any error occurs (request, configuration,
@@ -73,6 +81,7 @@ error-level message instead.
73
81
  ### Template helpers
74
82
 
75
83
  `create_image` defaults to the `page` template when `template` is omitted.
84
+ Supported templates: `page`, `blog_post`, `podcast`, `product`, `event`, `book`, `company`, `portfolio`, `github`.
76
85
 
77
86
  Use these helpers to force a specific template:
78
87
 
@@ -83,6 +92,7 @@ Use these helpers to force a specific template:
83
92
  - `OgPilotRuby.create_book_image(...)`
84
93
  - `OgPilotRuby.create_company_image(...)`
85
94
  - `OgPilotRuby.create_portfolio_image(...)`
95
+ - For `github`, use `OgPilotRuby.create_image(template: "github", ...)` (no dedicated helper yet).
86
96
 
87
97
  Example:
88
98
 
@@ -94,6 +104,439 @@ image_url = OgPilotRuby.create_blog_post_image(
94
104
  )
95
105
  ```
96
106
 
107
+ <!-- OG_SAMPLES_START -->
108
+ ## OG Image Examples
109
+
110
+ All sample payloads set explicit `bg_color`, `text_color`, and logo/avatar URLs to avoid default branding fallbacks.
111
+
112
+ For templates that support custom images, this set includes both `with_custom_image` and `without_custom_image` variants. (`github` currently has no custom image slot.)
113
+
114
+ Avatar-style fields (for example `author_avatar_url`) use DiceBear Avataaars, e.g. `https://api.dicebear.com/7.x/avataaars/svg?seed=JaneSmith`.
115
+
116
+ ### Sample Gallery
117
+
118
+ | Template | Variant | Preview |
119
+ |---|---|---|
120
+ | `page` | `with custom image` | ![page_with_custom_image](https://img.ogpilot.com/1f6oY498I6SiNfqGDjwdHLNpwmeU264t2OL0k7tY8Mw/plain/s3://og-pilot-development/eoo5v45d766hf22j4r2al60ktali) |
121
+ | `page` | `without custom image` | ![page_without_custom_image](https://img.ogpilot.com/9MZdTcTRyOoRqpTLll__EvDimmgojZESfZWokDqXeZM/plain/s3://og-pilot-development/wfa9es2wuvp6btjiriekk53swp6n) |
122
+ | `blog_post` | `with custom image` | ![blog_post_with_custom_image](https://img.ogpilot.com/RBBQZnBrAKcVmFjJg6UtNqX8P6nRRQdGLrlJNWYif7I/plain/s3://og-pilot-development/je7pj816exul9umhyszqpnbxelmd) |
123
+ | `blog_post` | `without custom image` | ![blog_post_without_custom_image](https://img.ogpilot.com/yP1B7OrLOy9Iu9JDSNk9Veys3ESCuCSBM9il2wq13V4/plain/s3://og-pilot-development/6aei8frvun6kvqojoor1hqack31y) |
124
+ | `podcast` | `with custom image` | ![podcast_with_custom_image](https://img.ogpilot.com/rzOOt7PWJ44OEwpKnntMLZaPvtl76DA3yRlj6B6N-Cc/plain/s3://og-pilot-development/fmkeblwertneuy4p82foeg5rfltl) |
125
+ | `podcast` | `without custom image` | ![podcast_without_custom_image](https://img.ogpilot.com/5UWWFG2J5bNRDOhDkN96ZG_g0XI9ULGDFgQkuVTjCYQ/plain/s3://og-pilot-development/yyhmo7lamj1n99i2n6dyttwungmq) |
126
+ | `product` | `with custom image` | ![product_with_custom_image](https://img.ogpilot.com/mzmHDMjyAX4VlpJanMT3zpmIJgSuClYI5eofhFpSJNQ/plain/s3://og-pilot-development/8ls2legb316l9vak40nu388uzy2t) |
127
+ | `product` | `without custom image` | ![product_without_custom_image](https://img.ogpilot.com/kK6d7xU3EWT5WKC6jKCw1rhJDmv9bwvRn2S-nShV4NA/plain/s3://og-pilot-development/52ns2l1ll7hjhfg0p3wn5c9pqtr5) |
128
+ | `event` | `with custom image` | ![event_with_custom_image](https://img.ogpilot.com/A7nxHkYs4xN4c1PhH2KQSWoB4ALwBdpP0HPiAss9_70/plain/s3://og-pilot-development/vjkdf6cx82dvdxmhiwtvrvkl976d) |
129
+ | `event` | `without custom image` | ![event_without_custom_image](https://img.ogpilot.com/elpfu28vJ57XCGx3npKhCwXqqJnPoqwCm8Aj5SLkWsM/plain/s3://og-pilot-development/vpte818nvtegc60ta98q7pmw91c8) |
130
+ | `book` | `with custom image` | ![book_with_custom_image](https://img.ogpilot.com/7pidSkvU_l0RzY9xBOLSa2x-jDWWvx14Gtv-KMDCGLw/plain/s3://og-pilot-development/cwnhb631ls2olk0yzkukmr5dnf7e) |
131
+ | `book` | `without custom image` | ![book_without_custom_image](https://img.ogpilot.com/FpMbBN15SLgaK9FEX07xXcT5dQIWyZkFdHaZ9i5wx3U/plain/s3://og-pilot-development/0rzisfn2qswdzsz641rl3s29ngu9) |
132
+ | `portfolio` | `with custom image` | ![portfolio_with_custom_image](https://img.ogpilot.com/gauiw2MMcNLLSFnQ07Hq3flv4S0L-89lnnjUOO0VgYU/plain/s3://og-pilot-development/qok04llq0ff3d2lherudwlhqxslm) |
133
+ | `portfolio` | `without custom image` | ![portfolio_without_custom_image](https://img.ogpilot.com/jVck9SDPlei0gaHq44_itLoVzn2wINrCP3XCTQF3SYs/plain/s3://og-pilot-development/jxa1s7dtibaeqamh0fsymwz7uqrx) |
134
+ | `company` | `with custom image` | ![company_with_custom_image](https://img.ogpilot.com/rji7hNgoxRM1KF2GofIO9gqXJQNg5CqlPWbhbGpR4FA/plain/s3://og-pilot-development/2xl6zi3zgjm74izr46efduzhmbrr) |
135
+ | `company` | `without custom image` | ![company_without_custom_image](https://img.ogpilot.com/PgGl9a6xPmG0Tn7qKmZZUczrv43cNLxzyISsbHG8_oE/plain/s3://og-pilot-development/6gmm6jg5r8ya27r3tr215edfd972) |
136
+ | `github` | `default` | ![github_activity](https://img.ogpilot.com/0dxm83dTyNCg5Dq_GZFT4SqsRyTWUO31d0HQwjIq0-A/plain/s3://og-pilot-development/jlhwskjsx08x56attaljw3ce0p65) |
137
+
138
+ ### Parameters Used
139
+
140
+ <details>
141
+ <summary>Exact payloads for these samples</summary>
142
+
143
+ ```json
144
+ [
145
+ {
146
+ "id": "page_with_custom_image",
147
+ "template": "page",
148
+ "variant": "with_custom_image",
149
+ "params": {
150
+ "title": "Acme Robotics Product Updates",
151
+ "description": "See what shipped this week across the web app.",
152
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.notion.so",
153
+ "image_url": "https://images.unsplash.com/photo-1558655146-d09347e92766?w=1400&q=80",
154
+ "bg_color": "#0B1220",
155
+ "text_color": "#F8FAFC",
156
+ "template": "page"
157
+ },
158
+ "image_url": "https://img.ogpilot.com/1f6oY498I6SiNfqGDjwdHLNpwmeU264t2OL0k7tY8Mw/plain/s3://og-pilot-development/eoo5v45d766hf22j4r2al60ktali"
159
+ },
160
+ {
161
+ "id": "page_without_custom_image",
162
+ "template": "page",
163
+ "variant": "without_custom_image",
164
+ "params": {
165
+ "title": "Notion AI Workspace for Product Teams",
166
+ "description": "Docs, specs, and roadmaps in one living workspace.",
167
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.notion.so",
168
+ "bg_color": "#111827",
169
+ "text_color": "#E5E7EB",
170
+ "template": "page"
171
+ },
172
+ "image_url": "https://img.ogpilot.com/9MZdTcTRyOoRqpTLll__EvDimmgojZESfZWokDqXeZM/plain/s3://og-pilot-development/wfa9es2wuvp6btjiriekk53swp6n"
173
+ },
174
+ {
175
+ "id": "blog_post_with_custom_image",
176
+ "template": "blog_post",
177
+ "variant": "with_custom_image",
178
+ "params": {
179
+ "title": "How Stripe Reduced Checkout Abandonment by 18%",
180
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fstripe.com",
181
+ "image_url": "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=1400&q=80",
182
+ "author_name": "Maya Patel",
183
+ "author_avatar_url": "https://api.dicebear.com/7.x/avataaars/svg?seed=MayaPatel",
184
+ "publish_date": "2026-02-24",
185
+ "bg_color": "#0F172A",
186
+ "text_color": "#F8FAFC",
187
+ "template": "blog_post"
188
+ },
189
+ "image_url": "https://img.ogpilot.com/RBBQZnBrAKcVmFjJg6UtNqX8P6nRRQdGLrlJNWYif7I/plain/s3://og-pilot-development/je7pj816exul9umhyszqpnbxelmd"
190
+ },
191
+ {
192
+ "id": "blog_post_without_custom_image",
193
+ "template": "blog_post",
194
+ "variant": "without_custom_image",
195
+ "params": {
196
+ "title": "Inside Vercel's Edge Rendering Playbook",
197
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fvercel.com",
198
+ "author_name": "Daniel Kim",
199
+ "author_avatar_url": "https://api.dicebear.com/7.x/avataaars/svg?seed=DanielKim",
200
+ "publish_date": "2026-02-18",
201
+ "bg_color": "#111827",
202
+ "text_color": "#E5E7EB",
203
+ "template": "blog_post"
204
+ },
205
+ "image_url": "https://img.ogpilot.com/yP1B7OrLOy9Iu9JDSNk9Veys3ESCuCSBM9il2wq13V4/plain/s3://og-pilot-development/6aei8frvun6kvqojoor1hqack31y"
206
+ },
207
+ {
208
+ "id": "podcast_with_custom_image",
209
+ "template": "podcast",
210
+ "variant": "with_custom_image",
211
+ "params": {
212
+ "title": "Indie Hackers Podcast: Pricing Experiments That Worked",
213
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.spotify.com",
214
+ "image_url": "https://images.unsplash.com/photo-1478737270239-2f02b77fc618?w=1400&q=80",
215
+ "episode_date": "2026-02-21",
216
+ "bg_color": "#18181B",
217
+ "text_color": "#FAFAFA",
218
+ "template": "podcast"
219
+ },
220
+ "image_url": "https://img.ogpilot.com/rzOOt7PWJ44OEwpKnntMLZaPvtl76DA3yRlj6B6N-Cc/plain/s3://og-pilot-development/fmkeblwertneuy4p82foeg5rfltl"
221
+ },
222
+ {
223
+ "id": "podcast_without_custom_image",
224
+ "template": "podcast",
225
+ "variant": "without_custom_image",
226
+ "params": {
227
+ "title": "Shopify Masters: Building a 7-Figure DTC Brand",
228
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.shopify.com",
229
+ "episode_date": "2026-02-19",
230
+ "bg_color": "#0B1120",
231
+ "text_color": "#E2E8F0",
232
+ "template": "podcast"
233
+ },
234
+ "image_url": "https://img.ogpilot.com/5UWWFG2J5bNRDOhDkN96ZG_g0XI9ULGDFgQkuVTjCYQ/plain/s3://og-pilot-development/yyhmo7lamj1n99i2n6dyttwungmq"
235
+ },
236
+ {
237
+ "id": "product_with_custom_image",
238
+ "template": "product",
239
+ "variant": "with_custom_image",
240
+ "params": {
241
+ "title": "Allbirds Tree Dasher 3",
242
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.allbirds.com",
243
+ "image_url": "https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=1400&q=80",
244
+ "unique_selling_point": "Free shipping + 30-day returns",
245
+ "bg_color": "#F8FAFC",
246
+ "text_color": "#0F172A",
247
+ "template": "product"
248
+ },
249
+ "image_url": "https://img.ogpilot.com/mzmHDMjyAX4VlpJanMT3zpmIJgSuClYI5eofhFpSJNQ/plain/s3://og-pilot-development/8ls2legb316l9vak40nu388uzy2t"
250
+ },
251
+ {
252
+ "id": "product_without_custom_image",
253
+ "template": "product",
254
+ "variant": "without_custom_image",
255
+ "params": {
256
+ "title": "Bose QuietComfort Ultra",
257
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.bose.com",
258
+ "unique_selling_point": "Save $70 this weekend",
259
+ "bg_color": "#111827",
260
+ "text_color": "#F9FAFB",
261
+ "template": "product"
262
+ },
263
+ "image_url": "https://img.ogpilot.com/kK6d7xU3EWT5WKC6jKCw1rhJDmv9bwvRn2S-nShV4NA/plain/s3://og-pilot-development/52ns2l1ll7hjhfg0p3wn5c9pqtr5"
264
+ },
265
+ {
266
+ "id": "event_with_custom_image",
267
+ "template": "event",
268
+ "variant": "with_custom_image",
269
+ "params": {
270
+ "title": "Launch Week Berlin 2026",
271
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.eventbrite.com",
272
+ "image_url": "https://images.unsplash.com/photo-1540575467063-178a50c2df87?w=1400&q=80",
273
+ "event_date": "2026-06-12/2026-06-14",
274
+ "event_location": "Station Berlin",
275
+ "bg_color": "#1E1B4B",
276
+ "text_color": "#F5F3FF",
277
+ "template": "event"
278
+ },
279
+ "image_url": "https://img.ogpilot.com/A7nxHkYs4xN4c1PhH2KQSWoB4ALwBdpP0HPiAss9_70/plain/s3://og-pilot-development/vjkdf6cx82dvdxmhiwtvrvkl976d"
280
+ },
281
+ {
282
+ "id": "event_without_custom_image",
283
+ "template": "event",
284
+ "variant": "without_custom_image",
285
+ "params": {
286
+ "title": "RubyConf Chicago 2026",
287
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Frubyconf.org",
288
+ "event_date": "2026-11-17",
289
+ "event_location": "Chicago, IL",
290
+ "bg_color": "#312E81",
291
+ "text_color": "#EEF2FF",
292
+ "template": "event"
293
+ },
294
+ "image_url": "https://img.ogpilot.com/elpfu28vJ57XCGx3npKhCwXqqJnPoqwCm8Aj5SLkWsM/plain/s3://og-pilot-development/vpte818nvtegc60ta98q7pmw91c8"
295
+ },
296
+ {
297
+ "id": "book_with_custom_image",
298
+ "template": "book",
299
+ "variant": "with_custom_image",
300
+ "params": {
301
+ "title": "Designing APIs for Humans",
302
+ "description": "A practical handbook for product-minded engineers.",
303
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.oreilly.com",
304
+ "image_url": "https://images.unsplash.com/photo-1512820790803-83ca734da794?w=1400&q=80",
305
+ "book_author": "Alex Turner",
306
+ "book_series_number": "2",
307
+ "book_genre": "Technology",
308
+ "bg_color": "#172554",
309
+ "text_color": "#EFF6FF",
310
+ "template": "book"
311
+ },
312
+ "image_url": "https://img.ogpilot.com/7pidSkvU_l0RzY9xBOLSa2x-jDWWvx14Gtv-KMDCGLw/plain/s3://og-pilot-development/cwnhb631ls2olk0yzkukmr5dnf7e"
313
+ },
314
+ {
315
+ "id": "book_without_custom_image",
316
+ "template": "book",
317
+ "variant": "without_custom_image",
318
+ "params": {
319
+ "title": "The Product Operating System",
320
+ "description": "How modern teams ship with confidence.",
321
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.penguinrandomhouse.com",
322
+ "book_author": "Sofia Ramirez",
323
+ "book_series_number": "1",
324
+ "book_genre": "Business",
325
+ "bg_color": "#1E293B",
326
+ "text_color": "#F8FAFC",
327
+ "template": "book"
328
+ },
329
+ "image_url": "https://img.ogpilot.com/FpMbBN15SLgaK9FEX07xXcT5dQIWyZkFdHaZ9i5wx3U/plain/s3://og-pilot-development/0rzisfn2qswdzsz641rl3s29ngu9"
330
+ },
331
+ {
332
+ "id": "portfolio_with_custom_image",
333
+ "template": "portfolio",
334
+ "variant": "with_custom_image",
335
+ "params": {
336
+ "title": "Maya Chen • Product Designer",
337
+ "description": "Fintech UX, design systems, and prototyping.",
338
+ "logo_url": "https://api.dicebear.com/7.x/avataaars/svg?seed=MayaChen&size=64",
339
+ "image_url": "https://images.unsplash.com/photo-1557672172-298e090bd0f1?w=1400&q=80",
340
+ "bg_color": "#1F2937",
341
+ "text_color": "#F9FAFB",
342
+ "template": "portfolio"
343
+ },
344
+ "image_url": "https://img.ogpilot.com/gauiw2MMcNLLSFnQ07Hq3flv4S0L-89lnnjUOO0VgYU/plain/s3://og-pilot-development/qok04llq0ff3d2lherudwlhqxslm"
345
+ },
346
+ {
347
+ "id": "portfolio_without_custom_image",
348
+ "template": "portfolio",
349
+ "variant": "without_custom_image",
350
+ "params": {
351
+ "title": "Arjun Rao • Frontend Engineer",
352
+ "description": "React performance, accessibility, and DX.",
353
+ "logo_url": "https://api.dicebear.com/7.x/avataaars/svg?seed=ArjunRao&size=64",
354
+ "bg_color": "#0F172A",
355
+ "text_color": "#E2E8F0",
356
+ "template": "portfolio"
357
+ },
358
+ "image_url": "https://img.ogpilot.com/jVck9SDPlei0gaHq44_itLoVzn2wINrCP3XCTQF3SYs/plain/s3://og-pilot-development/jxa1s7dtibaeqamh0fsymwz7uqrx"
359
+ },
360
+ {
361
+ "id": "company_with_custom_image",
362
+ "template": "company",
363
+ "variant": "with_custom_image",
364
+ "params": {
365
+ "title": "Notion",
366
+ "description": "4 job openings",
367
+ "logo_url": "https://www.google.com/s2/favicons?sz=128&domain_url=https%3A%2F%2Fwww.notion.so",
368
+ "bg_color": "#111827",
369
+ "text_color": "#F9FAFB",
370
+ "template": "company"
371
+ },
372
+ "image_url": "https://img.ogpilot.com/rji7hNgoxRM1KF2GofIO9gqXJQNg5CqlPWbhbGpR4FA/plain/s3://og-pilot-development/2xl6zi3zgjm74izr46efduzhmbrr"
373
+ },
374
+ {
375
+ "id": "company_without_custom_image",
376
+ "template": "company",
377
+ "variant": "without_custom_image",
378
+ "params": {
379
+ "title": "Linear",
380
+ "description": "12 job openings",
381
+ "company_logo_url": "https://www.google.com/s2/favicons?sz=256&domain_url=https%3A%2F%2Flinear.app",
382
+ "bg_color": "#0B1220",
383
+ "text_color": "#E5E7EB",
384
+ "template": "company",
385
+ "iss": "gitdigest.ai"
386
+ },
387
+ "image_url": "https://img.ogpilot.com/PgGl9a6xPmG0Tn7qKmZZUczrv43cNLxzyISsbHG8_oE/plain/s3://og-pilot-development/6gmm6jg5r8ya27r3tr215edfd972"
388
+ },
389
+ {
390
+ "id": "github_activity",
391
+ "template": "github",
392
+ "variant": "default",
393
+ "params": {
394
+ "title": "rails/rails",
395
+ "description": "Recent merged PRs, reviews, and commit activity.",
396
+ "accent_color": "#22C55E",
397
+ "bg_color": "#0B1220",
398
+ "text_color": "#E5E7EB",
399
+ "contributions": [
400
+ {
401
+ "date": "2025-10-28",
402
+ "count": 6
403
+ },
404
+ {
405
+ "date": "2025-11-01",
406
+ "count": 3
407
+ },
408
+ {
409
+ "date": "2025-11-05",
410
+ "count": 9
411
+ },
412
+ {
413
+ "date": "2025-11-08",
414
+ "count": 12
415
+ },
416
+ {
417
+ "date": "2025-11-12",
418
+ "count": 7
419
+ },
420
+ {
421
+ "date": "2025-11-16",
422
+ "count": 11
423
+ },
424
+ {
425
+ "date": "2025-11-20",
426
+ "count": 5
427
+ },
428
+ {
429
+ "date": "2025-11-24",
430
+ "count": 14
431
+ },
432
+ {
433
+ "date": "2025-11-28",
434
+ "count": 8
435
+ },
436
+ {
437
+ "date": "2025-12-02",
438
+ "count": 4
439
+ },
440
+ {
441
+ "date": "2025-12-06",
442
+ "count": 10
443
+ },
444
+ {
445
+ "date": "2025-12-10",
446
+ "count": 15
447
+ },
448
+ {
449
+ "date": "2025-12-14",
450
+ "count": 6
451
+ },
452
+ {
453
+ "date": "2025-12-18",
454
+ "count": 9
455
+ },
456
+ {
457
+ "date": "2025-12-22",
458
+ "count": 13
459
+ },
460
+ {
461
+ "date": "2025-12-26",
462
+ "count": 4
463
+ },
464
+ {
465
+ "date": "2025-12-30",
466
+ "count": 7
467
+ },
468
+ {
469
+ "date": "2026-01-03",
470
+ "count": 11
471
+ },
472
+ {
473
+ "date": "2026-01-07",
474
+ "count": 16
475
+ },
476
+ {
477
+ "date": "2026-01-11",
478
+ "count": 12
479
+ },
480
+ {
481
+ "date": "2026-01-15",
482
+ "count": 6
483
+ },
484
+ {
485
+ "date": "2026-01-19",
486
+ "count": 8
487
+ },
488
+ {
489
+ "date": "2026-01-23",
490
+ "count": 14
491
+ },
492
+ {
493
+ "date": "2026-01-27",
494
+ "count": 9
495
+ },
496
+ {
497
+ "date": "2026-01-31",
498
+ "count": 5
499
+ },
500
+ {
501
+ "date": "2026-02-04",
502
+ "count": 13
503
+ },
504
+ {
505
+ "date": "2026-02-08",
506
+ "count": 17
507
+ },
508
+ {
509
+ "date": "2026-02-12",
510
+ "count": 10
511
+ },
512
+ {
513
+ "date": "2026-02-16",
514
+ "count": 7
515
+ },
516
+ {
517
+ "date": "2026-02-20",
518
+ "count": 12
519
+ },
520
+ {
521
+ "date": "2026-02-24",
522
+ "count": 9
523
+ }
524
+ ],
525
+ "template": "github"
526
+ },
527
+ "image_url": "https://img.ogpilot.com/0dxm83dTyNCg5Dq_GZFT4SqsRyTWUO31d0HQwjIq0-A/plain/s3://og-pilot-development/jlhwskjsx08x56attaljw3ce0p65"
528
+ }
529
+ ]
530
+ ```
531
+
532
+ </details>
533
+
534
+ <!-- OG_SAMPLES_END -->
535
+
536
+
537
+
538
+
539
+
97
540
  ## Parameters
98
541
 
99
542
  The client sends `POST /api/v1/images` requests. All parameters are embedded in the signed JWT payload; the only query param is `token`.
@@ -123,6 +566,8 @@ The gem handles `iss` (domain) and `sub` (API key prefix) automatically.
123
566
  | `open_timeout` | `5` | Connection timeout in seconds |
124
567
  | `read_timeout` | `10` | Read timeout in seconds |
125
568
  | `strip_extensions` | `true` | When `true`, file extensions are stripped from resolved paths (see [Strip extensions](#strip-extensions)) |
569
+ | `cache_store` | `nil` | Optional cache backend with `read`/`write` (for example `Rails.cache`) |
570
+ | `cache_ttl` | `86400` | Cache TTL in seconds when `cache_store` is enabled |
126
571
 
127
572
  ### Ruby options
128
573
 
@@ -144,6 +589,7 @@ The gem handles `iss` (domain) and `sub` (API key prefix) automatically.
144
589
  | `book` | `title`, `description`, `book_author`, `book_series_number`, `book_description`, `book_genre` |
145
590
  | `portfolio` | `title` |
146
591
  | `company` | `title`, `company_logo_url`, `description` (note: `image_url` is ignored) |
592
+ | `github` | `title`, `description`, `contributions` (array of `{ date, count }`), `accent_color` |
147
593
 
148
594
  ### Path handling
149
595
 
@@ -188,6 +634,26 @@ payload = {
188
634
  data = OgPilotRuby.create_image(**payload, json: true)
189
635
  ```
190
636
 
637
+ ### Local caching
638
+
639
+ You can optionally enable client-side caching to avoid repeated API calls for
640
+ identical payloads:
641
+
642
+ ```ruby
643
+ OgPilotRuby.configure do |config|
644
+ config.cache_store = Rails.cache
645
+ config.cache_ttl = 86_400 # 1 day
646
+ end
647
+ ```
648
+
649
+ Cache key inputs include the normalized payload, `iat`, output mode (`json`
650
+ vs URL), and configured domain.
651
+
652
+ TTL behavior:
653
+
654
+ - If `iat` is present: cached for `cache_ttl`
655
+ - If `iat` is omitted: cached for `7 * cache_ttl`
656
+
191
657
  ### Strip extensions
192
658
 
193
659
  When `strip_extensions` is enabled, the client removes file extensions from the
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "digest"
3
4
  require "json"
4
5
  require "logger"
5
6
  require "net/http"
@@ -25,14 +26,24 @@ module OgPilotRuby
25
26
  params.delete("path") if params.key?("path")
26
27
  params[:path] = manual_path.to_s.strip.empty? ? resolved_path(default:) : normalize_path(manual_path)
27
28
 
29
+ cache_key = cache_key_for(params, iat:, json:)
30
+ if config.cache_store && cache_key
31
+ cached = read_cached(cache_key)
32
+ return cached if cached
33
+ end
34
+
28
35
  uri = build_uri(params, iat:)
29
36
  response, final_uri = request(uri, json:, headers:)
30
37
 
31
- if json
38
+ result = if json
32
39
  JSON.parse(response.body)
33
40
  else
34
41
  response["Location"] || final_uri.to_s
35
42
  end
43
+
44
+ write_cached(cache_key, result, iat:) if config.cache_store && cache_key && result
45
+
46
+ result
36
47
  rescue StandardError => e
37
48
  log_create_image_failure(e, json:)
38
49
  json ? { "image_url" => nil } : nil
@@ -42,6 +53,25 @@ module OgPilotRuby
42
53
 
43
54
  attr_reader :config
44
55
 
56
+ def cache_key_for(params, iat:, json:)
57
+ key_data = params.transform_keys(&:to_sym).merge(iat: iat, json: json, domain: domain!)
58
+ normalized = key_data.sort_by { |k, _| k.to_s }.to_h
59
+ "og_pilot:#{Digest::SHA256.hexdigest(normalized.to_json)[0, 16]}"
60
+ end
61
+
62
+ def read_cached(cache_key)
63
+ config.cache_store.read(cache_key)
64
+ rescue StandardError
65
+ nil
66
+ end
67
+
68
+ def write_cached(cache_key, result, iat:)
69
+ ttl = iat ? config.cache_ttl : (7 * config.cache_ttl)
70
+ config.cache_store.write(cache_key, result, expires_in: ttl)
71
+ rescue StandardError
72
+ nil
73
+ end
74
+
45
75
  def log_create_image_failure(error, json:)
46
76
  mode = json ? "json" : "url"
47
77
  message = "OgPilotRuby create_image failed (mode=#{mode}): #{error.class}: #{error.message}"
@@ -5,7 +5,7 @@ module OgPilotRuby
5
5
  DEFAULT_BASE_URL = "https://ogpilot.com"
6
6
  private_constant :DEFAULT_BASE_URL
7
7
 
8
- attr_accessor :api_key, :domain, :base_url, :open_timeout, :read_timeout, :strip_extensions
8
+ attr_accessor :api_key, :domain, :base_url, :open_timeout, :read_timeout, :strip_extensions, :cache_store, :cache_ttl
9
9
 
10
10
  def initialize
11
11
  @api_key = ENV.fetch("OG_PILOT_API_KEY", nil)
@@ -14,6 +14,8 @@ module OgPilotRuby
14
14
  @open_timeout = 5
15
15
  @read_timeout = 10
16
16
  @strip_extensions = true
17
+ @cache_store = nil
18
+ @cache_ttl = 86400
17
19
  end
18
20
  end
19
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OgPilotRuby
4
- VERSION = "0.4.2"
4
+ VERSION = "0.4.4"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: og_pilot_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sunergos IT LLC