bannerbear 0.1.3 → 0.2.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: f2da12377f479bda2685084b6de8bbaedd54b043f565e2b8d646ff9d36c1ec93
4
- data.tar.gz: 477db9f95c4df6f068667816546d813e4021288301103a207c61e6227aa199d6
3
+ metadata.gz: 850108dc51b0e1e90d1e7e6452ce69b65c972638e33d7f6b946f0f81ab4fe1cd
4
+ data.tar.gz: f4c1e8883281527d9c515a07d358a251629ec19fb06a2dab4506fb95cfd9576a
5
5
  SHA512:
6
- metadata.gz: 619e0c3e44fd9be99a86d0874418d6f4d6db14bf94c6200f3decb78a83f0fd7cb8e02ced1f86bd3d562a53d617e7e98f99165ce4372d5a49bafdaf4f55b411d8
7
- data.tar.gz: 64d0ed8cec035ce84a9976915515ab63106e0e9ed8e890b37887b57e41cc68401877e877a228e0cae7e6c594da4319cd5fc72a44c0390046cbab37a162ddd8e3
6
+ metadata.gz: de72ce94b734dbcfc73ca872ce5b2ce9f41c23b6f5c1bf40e2cf78bf8e2938b81933ab20113c41b5e8ad0af2dc034ff251b696ef310219063ea066dfdbf759ba
7
+ data.tar.gz: 92bfc10f330f5d318084b8e446491678029de74807ef353e2d679279590e22487780e15f4b093fa0bf374c8601fc6e685faf8d9d96f9fa4266f088638080186f
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /smoke_test_v5.rb
data/Gemfile.lock CHANGED
@@ -1,19 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bannerbear (0.1.2)
4
+ bannerbear (0.2.0)
5
5
  httparty
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- httparty (0.20.0)
11
- mime-types (~> 3.0)
10
+ bigdecimal (4.1.2)
11
+ csv (3.3.5)
12
+ httparty (0.24.2)
13
+ csv
14
+ mini_mime (>= 1.0.0)
12
15
  multi_xml (>= 0.5.2)
13
- mime-types (3.4.1)
14
- mime-types-data (~> 3.2015)
15
- mime-types-data (3.2021.1115)
16
- multi_xml (0.6.0)
16
+ mini_mime (1.1.5)
17
+ multi_xml (0.9.1)
18
+ bigdecimal (>= 3.1, < 5)
17
19
  rake (12.3.3)
18
20
 
19
21
  PLATFORMS
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Bannerbear
1
+ # Bannerbear Ruby
2
2
 
3
3
  Ruby wrapper for the [Bannerbear API](https://developers.bannerbear.com) - an image and video generation service.
4
4
 
@@ -18,6 +18,228 @@ Or install it yourself as:
18
18
 
19
19
  $ gem install bannerbear
20
20
 
21
+ ## V5 API
22
+
23
+ The [V5 API](https://developers.bannerbear.com/v5/) is a new generation of the Bannerbear API. **V5 API keys do not work with V2 endpoints, and V2 API keys do not work with V5 endpoints** — you must use the right client class for your key.
24
+
25
+ For the **V5 API**, use `Bannerbear::V5::Client` (this section).
26
+ For the **legacy V2 API**, see [Usage](#usage) below — that section is unchanged.
27
+
28
+ ### Table of Contents
29
+
30
+ - [Authentication (V5)](#authentication-v5)
31
+ - [Account (V5)](#account-v5)
32
+ - [Image Templates (V5)](#image-templates-v5)
33
+ - [Images (V5)](#images-v5)
34
+ - [Batches (V5)](#batches-v5)
35
+ - [Webhooks (V5)](#webhooks-v5)
36
+ - [Instant URLs (V5)](#instant-urls-v5)
37
+
38
+ ### Authentication (V5)
39
+
40
+ ```ruby
41
+ bb = Bannerbear::V5::Client.new("your V5 API key")
42
+ ```
43
+
44
+ Or set `BANNERBEAR_API_KEY` and call without arguments:
45
+
46
+ ```ruby
47
+ bb = Bannerbear::V5::Client.new
48
+ ```
49
+
50
+ ### Account (V5)
51
+
52
+ ```ruby
53
+ bb.account
54
+ ```
55
+
56
+ ### Image Templates (V5)
57
+
58
+ V5 renames V2's `templates` resource to `image_templates`.
59
+
60
+ ```ruby
61
+ bb.list_image_templates(page: 1)
62
+ bb.get_image_template("template uid")
63
+ bb.update_image_template("template uid", name: "New Name", description: "...", tags: ["portrait"])
64
+ ```
65
+
66
+ ### Images (V5)
67
+
68
+ V5's `modifications` is an **object** with two sub-keys:
69
+
70
+ - `template` — template-level changes (width, height, etc.)
71
+ - `objects` — array of per-layer changes (equivalent to V2's flat modifications array)
72
+
73
+ ```ruby
74
+ bb.create_image("template uid",
75
+ modifications: {
76
+ template: { width: 1080, height: 1080 },
77
+ objects: [
78
+ { name: "headline", text: "Hello World!" },
79
+ { name: "photo", image_url: "https://images.unsplash.com/photo-1555400038-63f5ba517a47?w=1000&q=80" }
80
+ ]
81
+ }
82
+ )
83
+ ```
84
+
85
+ Synchronous generation routes to `sync.api.bannerbear.com/v5` (10s timeout). The `sync:` flag is a Ruby-level switch — it is **not** sent in the request body:
86
+
87
+ ```ruby
88
+ bb.create_image("template uid", sync: true, modifications: { objects: [...] })
89
+ ```
90
+
91
+ ##### Options for `create_image`
92
+
93
+ - `modifications`: V5 modifications object (`hash`)
94
+ - `formats`: output formats, e.g. `["jpg", "pdf"]` (`array`)
95
+ - `scale`: scale multiplier, 1–4 (`integer`)
96
+ - `dpi`: DPI metadata (`integer`)
97
+ - `quality`: quality control (`integer`)
98
+ - `proxy`: proxy server for asset fetching (`string`)
99
+ - `metadata`: include any metadata to reference at a later point (`string`)
100
+ - `version`: pin template version (`integer`)
101
+ - `sync`: route to the sync host (`boolean`; Ruby-only, not sent to the API)
102
+
103
+ ```ruby
104
+ bb.get_image("image uid")
105
+ bb.list_images(page: 1)
106
+ ```
107
+
108
+ ### Batches (V5)
109
+
110
+ Generate multiple images in one request (up to 100).
111
+
112
+ ```ruby
113
+ bb.create_batch(
114
+ type: "image",
115
+ items: [
116
+ { template: "template uid 1", modifications: { objects: [...] } },
117
+ { template: "template uid 2", modifications: { objects: [...] } }
118
+ ]
119
+ )
120
+ bb.get_batch("batch uid")
121
+ bb.list_batches(page: 1)
122
+ ```
123
+
124
+ ### Webhooks (V5)
125
+
126
+ Webhooks are managed as a first-class resource in V5 (instead of being a per-request `webhook_url` parameter).
127
+
128
+ ```ruby
129
+ hook = bb.create_webhook(
130
+ name: "my-webhook",
131
+ url: "https://example.com/hook",
132
+ resource: "image",
133
+ event: "completed",
134
+ status: "active",
135
+ scope: "all",
136
+ templates: []
137
+ )
138
+
139
+ # IMPORTANT: signing_key is ONLY returned in the create response. Store it now —
140
+ # subsequent get_webhook calls will not include it.
141
+ puts hook["signing_key"]
142
+ ```
143
+
144
+ CRUD:
145
+
146
+ ```ruby
147
+ bb.get_webhook("webhook uid")
148
+ bb.update_webhook("webhook uid",
149
+ name: "renamed",
150
+ url: "https://example.com/hook",
151
+ resource: "image",
152
+ event: "completed",
153
+ status: "active",
154
+ scope: "all"
155
+ )
156
+ bb.delete_webhook("webhook uid")
157
+ bb.list_webhooks(page: 1)
158
+ ```
159
+
160
+ ### Instant URLs (V5)
161
+
162
+ Instant URLs are URLs bound to a template that can be manipulated with query strings — the V5 equivalent of V2's "Signed URLs" feature.
163
+
164
+ #### Create an Instant URL base
165
+
166
+ ```ruby
167
+ iurl = bb.create_instant_url(
168
+ name: "my-instant-url",
169
+ template: "template uid",
170
+ mode: "encoded", # or "named_params"
171
+ security: "signed", # or "open"
172
+ status: "active",
173
+ scale: 1 # 1, 2, 3, or 4
174
+ )
175
+
176
+ # IMPORTANT: signing_key is ONLY returned in the create response. Store it now.
177
+ puts iurl["signing_key"]
178
+ puts iurl["base_url"]
179
+ ```
180
+
181
+ ##### Options for `create_instant_url` / `update_instant_url`
182
+
183
+ - `name` *required* (`string`)
184
+ - `template` *required* — image template UID (`string`)
185
+ - `mode`: `"encoded"` or `"named_params"` (`string`)
186
+ - `security`: `"signed"` or `"open"` (`string`)
187
+ - `status`: `"active"` or `"disabled"` (`string`)
188
+ - `scale`: 1, 2, 3, or 4 (`integer`)
189
+ - `rate_limit`: enable per-IP rate limiting (`boolean`)
190
+ - `template_version`: pin template version (`integer`, nullable)
191
+ - `max_renders`: cap total renders (`integer`, nullable)
192
+ - `expires_at`: ISO 8601 expiry (`string`, nullable)
193
+
194
+ CRUD:
195
+
196
+ ```ruby
197
+ bb.get_instant_url("uid")
198
+ bb.update_instant_url("uid", name: "...", template: "...", ...)
199
+ bb.delete_instant_url("uid")
200
+ bb.list_instant_urls(page: 1)
201
+ ```
202
+
203
+ #### Build an Instant URL with modifications
204
+
205
+ `build_instant_url` is a pure local helper — no API call. It composes the URL from a base + modifications and, if a signing key is provided, appends the HMAC signature.
206
+
207
+ ```ruby
208
+ # Encoded mode, signed
209
+ bb.build_instant_url(iurl["base_url"],
210
+ mode: "encoded",
211
+ signing_key: iurl["signing_key"],
212
+ modifications: {
213
+ template: { width: 1030, height: 890 },
214
+ objects: [{ name: "title", text: "Hello!", color: "#ffffff" }]
215
+ }
216
+ )
217
+
218
+ # Named params mode, signed
219
+ bb.build_instant_url(iurl["base_url"],
220
+ mode: "named_params",
221
+ signing_key: iurl["signing_key"],
222
+ modifications: {
223
+ template: { width: 1030, height: 890 },
224
+ objects: [{ name: "title", text: "Hello!" }]
225
+ }
226
+ )
227
+
228
+ # Open (unsigned): omit signing_key
229
+ bb.build_instant_url(iurl["base_url"],
230
+ mode: "encoded",
231
+ modifications: { objects: [{ name: "title", text: "Hello!" }] }
232
+ )
233
+ ```
234
+
235
+ ##### Options for `build_instant_url`
236
+
237
+ - `mode`: `"encoded"` (default) or `"named_params"` (`string`)
238
+ - `signing_key`: only needed when the instant URL was created with `security: "signed"` (`string`)
239
+ - `modifications`: same shape as `create_image`'s modifications (`hash`)
240
+
241
+ ---
242
+
21
243
  ## Usage
22
244
 
23
245
  ### Table of Contents
@@ -33,6 +255,7 @@ Or install it yourself as:
33
255
  - [Templates](#templates)
34
256
  - [Template Sets](#template-sets)
35
257
  - [Video Templates](#video-templates)
258
+ - [Signed URLs](#signed-urls)
36
259
 
37
260
  ### Authentication
38
261
 
@@ -333,6 +556,29 @@ bb.get_video_template("video template uid")
333
556
  bb.list_video_templates(:page => 2)
334
557
  ```
335
558
 
559
+ ### Signed URLs
560
+
561
+ This gem also includes a convenient utility for generating signed urls. Authenticate as above, then:
562
+
563
+ ```ruby
564
+ bb.generate_signed_url("base uid", :modifications => [])
565
+
566
+ # example
567
+ bb.generate_signed_url("A89wavQyY3Bebk3djP",
568
+ :modifications => [
569
+ {
570
+ :name => "country",
571
+ :text => "testing!"
572
+ },
573
+ {
574
+ :name => "photo",
575
+ :image_url => "https://images.unsplash.com/photo-1638356435991-4c79b00ebef3?w=764&q=80"
576
+ }
577
+ ]
578
+ )
579
+ # https://ondemand.bannerbear.com/signedurl/A89wavQyY3Bebk3djP/image.jpg?modifications=W3sibmFtZSI6ImNvdW50cnkiLCJ0ZXh0IjoidGVzdGluZyEifSx7Im5hbWUiOiJwaG90byIsImltYWdlX3VybCI6Imh0dHBzOi8vaW1hZ2VzLnVuc3BsYXNoLmNvbS9waG90by0xNjM4MzU2NDM1OTkxLTRjNzliMDBlYmVmMz93PTc2NCZxPTgwIn1d&s=40e7c9d4902b86ea83e0c400e57d7cc580534fd527e234d40a0c7ace589a16eb
580
+ ```
581
+
336
582
  ## Contributing
337
583
 
338
584
  Bug reports and pull requests are welcome on GitHub at https://github.com/yongfook/bannerbear-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/yongfook/bannerbear-ruby/blob/master/CODE_OF_CONDUCT.md).
@@ -109,8 +109,8 @@ module Bannerbear
109
109
 
110
110
  # Templates
111
111
 
112
- def get_template(uid)
113
- get_response "/templates/#{uid}"
112
+ def get_template(uid, params = {})
113
+ get_response "/templates/#{uid}?#{URI.encode_www_form(params.slice(:extended))}"
114
114
  end
115
115
 
116
116
  def update_template(uid, payload = {})
@@ -118,17 +118,17 @@ module Bannerbear
118
118
  end
119
119
 
120
120
  def list_templates(params = {})
121
- get_response "/templates?#{URI.encode_www_form(params.slice(:page, :tag, :limit, :name))}"
121
+ get_response "/templates?#{URI.encode_www_form(params.slice(:page, :tag, :limit, :name, :extended))}"
122
122
  end
123
123
 
124
124
  # Template Sets
125
125
 
126
- def get_template_set(uid)
127
- get_response "/template_sets/#{uid}"
126
+ def get_template_set(uid, params = {})
127
+ get_response "/template_sets/#{uid}?#{URI.encode_www_form(params.slice(:extended))}"
128
128
  end
129
129
 
130
130
  def list_template_sets(params = {})
131
- get_response "/template_sets?#{URI.encode_www_form(params.slice(:page))}"
131
+ get_response "/template_sets?#{URI.encode_www_form(params.slice(:page, :extended))}"
132
132
  end
133
133
 
134
134
  # Video Templates
@@ -0,0 +1,199 @@
1
+ module Bannerbear
2
+ module V5
3
+
4
+ class Client
5
+
6
+ def initialize(api_key = nil)
7
+ @api_key = api_key || ENV["BANNERBEAR_API_KEY"]
8
+ end
9
+
10
+ def account
11
+ get_response "/account"
12
+ end
13
+
14
+ # Image Templates
15
+
16
+ def list_image_templates(params = {})
17
+ get_response "/image_templates?#{URI.encode_www_form(params.slice(:page))}"
18
+ end
19
+
20
+ def get_image_template(uid)
21
+ get_response "/image_templates/#{uid}"
22
+ end
23
+
24
+ def update_image_template(uid, payload = {})
25
+ patch_response "/image_templates/#{uid}", payload.slice(:name, :description, :tags)
26
+ end
27
+
28
+ # Images
29
+
30
+ def list_images(params = {})
31
+ get_response "/images?#{URI.encode_www_form(params.slice(:page))}"
32
+ end
33
+
34
+ def get_image(uid)
35
+ get_response "/images/#{uid}"
36
+ end
37
+
38
+ def create_image(uid, payload = {})
39
+ post_response "/images", payload.slice(:modifications, :formats, :scale, :dpi, :quality, :proxy, :metadata, :version).merge({:template => uid}), payload[:sync]
40
+ end
41
+
42
+ # Batches
43
+
44
+ def list_batches(params = {})
45
+ get_response "/batches?#{URI.encode_www_form(params.slice(:page))}"
46
+ end
47
+
48
+ def get_batch(uid)
49
+ get_response "/batches/#{uid}"
50
+ end
51
+
52
+ def create_batch(payload = {})
53
+ post_response "/batches", payload.slice(:type, :items)
54
+ end
55
+
56
+ # Webhooks
57
+
58
+ def list_webhooks(params = {})
59
+ get_response "/webhooks?#{URI.encode_www_form(params.slice(:page))}"
60
+ end
61
+
62
+ def get_webhook(uid)
63
+ get_response "/webhooks/#{uid}"
64
+ end
65
+
66
+ def create_webhook(payload = {})
67
+ post_response "/webhooks", payload.slice(:name, :url, :resource, :event, :status, :scope, :templates)
68
+ end
69
+
70
+ def update_webhook(uid, payload = {})
71
+ patch_response "/webhooks/#{uid}", payload.slice(:name, :url, :resource, :event, :status, :scope, :templates)
72
+ end
73
+
74
+ def delete_webhook(uid)
75
+ delete_response "/webhooks/#{uid}"
76
+ end
77
+
78
+ # Instant URLs
79
+
80
+ def list_instant_urls(params = {})
81
+ get_response "/instant_urls?#{URI.encode_www_form(params.slice(:page))}"
82
+ end
83
+
84
+ def get_instant_url(uid)
85
+ get_response "/instant_urls/#{uid}"
86
+ end
87
+
88
+ def create_instant_url(payload = {})
89
+ post_response "/instant_urls", payload.slice(:name, :template, :mode, :security, :status, :scale, :rate_limit, :template_version, :max_renders, :expires_at)
90
+ end
91
+
92
+ def update_instant_url(uid, payload = {})
93
+ patch_response "/instant_urls/#{uid}", payload.slice(:name, :template, :mode, :security, :status, :scale, :rate_limit, :template_version, :max_renders, :expires_at)
94
+ end
95
+
96
+ def delete_instant_url(uid)
97
+ delete_response "/instant_urls/#{uid}"
98
+ end
99
+
100
+ def build_instant_url(base_url, payload = {})
101
+ modifications = payload[:modifications]
102
+ mode = (payload[:mode] || "encoded").to_s
103
+
104
+ url = case mode
105
+ when "encoded"
106
+ data =
107
+ if modifications.is_a?(Hash) && modifications.keys.map(&:to_s) == ["objects"]
108
+ modifications[:objects] || modifications["objects"]
109
+ else
110
+ modifications
111
+ end
112
+ "#{base_url}?modifications=#{Base64.urlsafe_encode64(data.to_json, padding: false)}"
113
+ when "named_params"
114
+ template = modifications.is_a?(Hash) ? (modifications[:template] || modifications["template"]) : nil
115
+ objects = modifications.is_a?(Hash) ? (modifications[:objects] || modifications["objects"]) : modifications
116
+ parts = []
117
+ (template || {}).each do |k, v|
118
+ parts << "template:#{k}=#{URI.encode_www_form_component(v)}"
119
+ end
120
+ (objects || []).each do |obj|
121
+ name = obj[:name] || obj["name"]
122
+ obj.each do |k, v|
123
+ key = k.to_s
124
+ next if key == "name"
125
+ parts << "#{name}:#{key}=#{URI.encode_www_form_component(v)}"
126
+ end
127
+ end
128
+ "#{base_url}?#{parts.join('&')}"
129
+ else
130
+ raise ArgumentError, "unknown instant URL mode: #{mode.inspect}"
131
+ end
132
+
133
+ signing_key = payload[:signing_key]
134
+ return url if signing_key.nil? || signing_key.empty?
135
+ sig = OpenSSL::HMAC.hexdigest("SHA256", signing_key, url)
136
+ "#{url}&s=#{sig}"
137
+ end
138
+
139
+
140
+ private
141
+
142
+ BB_API_ENDPOINT = "https://api.bannerbear.com/v5"
143
+ BB_API_ENDPOINT_SYNCHRONOUS = "https://sync.api.bannerbear.com/v5"
144
+
145
+ def get_response(url)
146
+ response = HTTParty.get("#{BB_API_ENDPOINT}#{url}", timeout: 3, headers: { 'Authorization' => "Bearer #{@api_key}" })
147
+ body = JSON.parse(response.body)
148
+ return {"error" => body['message'], "code" => response.code} if response.code >= 400
149
+ return body
150
+ end
151
+
152
+ def patch_response(url, payload)
153
+ response = HTTParty.patch("#{BB_API_ENDPOINT}#{url}",
154
+ body: payload.to_json,
155
+ timeout: 5,
156
+ headers: {
157
+ 'Authorization' => "Bearer #{@api_key}",
158
+ 'Content-Type' => 'application/json'
159
+ }
160
+ )
161
+ body = JSON.parse(response.body)
162
+ return {"error" => body['message'], "code" => response.code} if response.code >= 400
163
+ return body
164
+ end
165
+
166
+ def post_response(url, payload, sync = false)
167
+ endpoint = BB_API_ENDPOINT
168
+ timeout = 5
169
+ if sync == true
170
+ endpoint = BB_API_ENDPOINT_SYNCHRONOUS
171
+ timeout = 10
172
+ end
173
+ response = HTTParty.post("#{endpoint}#{url}",
174
+ body: payload.to_json,
175
+ timeout: timeout,
176
+ headers: {
177
+ 'Authorization' => "Bearer #{@api_key}",
178
+ 'Content-Type' => 'application/json'
179
+ }
180
+ )
181
+ body = JSON.parse(response.body)
182
+ return {"error" => body['message'], "code" => response.code} if response.code >= 400
183
+ return body
184
+ end
185
+
186
+ def delete_response(url)
187
+ response = HTTParty.delete("#{BB_API_ENDPOINT}#{url}",
188
+ timeout: 5,
189
+ headers: { 'Authorization' => "Bearer #{@api_key}" }
190
+ )
191
+ body = response.body.to_s.empty? ? {} : JSON.parse(response.body)
192
+ return {"error" => body['message'], "code" => response.code} if response.code >= 400
193
+ return {"code" => response.code}
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+ end
@@ -1,3 +1,3 @@
1
1
  module Bannerbear
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/bannerbear.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  require "httparty"
2
2
  require "openssl"
3
3
  require "base64"
4
+ require "json"
4
5
  require "bannerbear/version"
5
6
  require "bannerbear/client"
7
+ require "bannerbear/v5/client"
6
8
 
7
9
  module Bannerbear
8
10
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bannerbear
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Yongfook
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-02 00:00:00.000000000 Z
11
+ date: 2026-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -43,6 +43,7 @@ files:
43
43
  - bin/setup
44
44
  - lib/bannerbear.rb
45
45
  - lib/bannerbear/client.rb
46
+ - lib/bannerbear/v5/client.rb
46
47
  - lib/bannerbear/version.rb
47
48
  homepage: https://github.com/yongfook/bannerbear-ruby
48
49
  licenses:
@@ -64,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
65
  - !ruby/object:Gem::Version
65
66
  version: '0'
66
67
  requirements: []
67
- rubygems_version: 3.1.6
68
+ rubygems_version: 3.5.22
68
69
  signing_key:
69
70
  specification_version: 4
70
71
  summary: Ruby wrapper for the Bannerbear API