smallpict 0.0.5 → 0.0.7

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: 07f81540dbb9ffdc18b971d6b7519d928505ba89713483e842ce7d5591b7283f
4
- data.tar.gz: ad857c51ab6a2c763ce57c8ee213ed793c22d6750a709fe505b5bd4343bb8d33
3
+ metadata.gz: 4c99d1d9f8d685017396f2c3e94e7715fbed0aa6a2e2c2315bf7dca11a1ec336
4
+ data.tar.gz: d87ea5708336c3600cc10d2e7bd11c219f0f355014eedeab0041accb30fc57fa
5
5
  SHA512:
6
- metadata.gz: 3bebb0287a363cc4822f714b892ca1d179cb128ce1b93da684fd02e27a6eb3618adf03fea5f323733fcc991586bab0a1acb66db44646ca7390e46272fd9911fb
7
- data.tar.gz: a9c4ba6d3d0dc9ec41699a8b66c2b44ab32b664e06726c39fce4afbcb7dd7f5417f8d4d002a0cab65c8124ec1cef10806143bd11e43b275efc46645953325679
6
+ metadata.gz: 0ae9beee1528a5242fa7bede52a15186be036bce30f85456c03a6210bf5a6eaa7e533d73f54d3bd0fe54d1259e87286080cf1c79f04fdc489f8d72dd8b1b6bec
7
+ data.tar.gz: b7166c544f39637266ee9633a7cc7cdc4bf6912e89657da7fd3707744c3186411707499614a3c79047ec66e4a7c07c6cb9857c52a1a6d1fe171ce4af0dd6ddf6
data/CHANGELOG.md CHANGED
@@ -5,6 +5,31 @@ All notable changes to the `smallpict` gem will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.0.7] - 2026-10-31
11
+
12
+ ### Added
13
+ - `SmallPict::Mode::AUTO` (`"auto"`, also in `Mode::ALL`): the server picks the quality per image from a visual score (CMP-07). Plans: WP Pro and Business, Velocity, Momentum and sandbox; a server without auto enabled answers `400 invalid_mode` (`ValidationError`).
14
+ - `mode:` option on `optimize` (`"lossy"` default, `"extreme"`, `"lossless"`), with constants `SmallPict::Mode::LOSSY`, `EXTREME`, `LOSSLESS` and the frozen list `SmallPict::Mode::ALL`. The server checks the mode against the plan: `403 mode_not_in_plan` raises `PermissionDeniedError` with `details["mode"]` and `details["allowed_modes"]`; `400 invalid_mode` raises `ValidationError`.
15
+ - `JobStatusResult#mode_applied` and `#quality_applied`, as reported by the server (`nil` for jobs created before modes existed).
16
+ - `folder:` option on `optimize`, sent as `options.folder` and omitted when unset. The server stores the file under that folder of your account and lower-cases the value; `OptimizeResult#folder` and `JobStatusResult#folder` return it as stored (`nil` without one). The client rejects empty segments (`a//b`, leading or trailing `/`), `..` segments and more than 3 segments with `ValidationError` before any request; the server checks the full format (`400 invalid_folder` raises `ValidationError` with the server's message).
17
+ - `purge_cdn(purge_type: "folder", folder: "...")` purges one upload folder. `folder:` is required for that type (`ValidationError` before any request when missing or malformed) and is sent only with it; `url` and `all` purges are unchanged.
18
+ - `QuotaResponse#capacity`: the read-only capacity of `GET /v1/quota` as `SmallPict::Models::Capacity` (`period_end`, `storage`, `cdn`, `booster` with `next_expiry`, `lapse`, `manage_url`). Members that do not apply to the plan are `nil`; `capacity` is `nil` when the server could not read it and on older servers that do not send it. Existing quota fields are unchanged. Capacity is bought only in the dashboard; the SDK exposes `manage_url` as a string and never opens or fetches it.
19
+ - `SmallPict::Error#upgrade_url` on every error: `details["upgrade_url"]` when it is a string (sent with `402` quota, storage and read-only errors), otherwise `nil`. `PermissionDeniedError#allowed_modes` returns `details["allowed_modes"]` of `403 mode_not_in_plan`, otherwise `nil`. Status-to-class mapping is unchanged.
20
+
21
+ ### Changed
22
+ - README: example of `mode: SmallPict::Mode::EXTREME` without `quality:`.
23
+ - `429 purge_rate_limited` (hourly limit: 10 `folder` and 2 `all` purges per hour) raises `RateLimitError` without automatic retries, since the limit does not reset within seconds. Other 429 responses are still retried as before.
24
+ - Errors raised as the base `SmallPict::Error` (statuses without a dedicated class, such as `409`) now carry the server's `error.code` in `#code` instead of `"INTERNAL_ERROR"`; `"INTERNAL_ERROR"` remains when the body has no code. Typed errors keep their fixed codes.
25
+
26
+ ### Deprecated
27
+ - `lossless:` option on `optimize`. Use `mode: SmallPict::Mode::LOSSLESS` instead. `lossless: true` without `mode:` is sent as `mode: "lossless"`; `lossless: true` with a different `mode:` raises `ValidationError` before any request. Using it emits a `:deprecated` category warning (shown when `Warning[:deprecated]` is enabled). It will not be removed before the next major version.
28
+
29
+ ### Fixed
30
+ - `quality:` is no longer sent when unset (its default is now `nil` instead of `80`), so the server picks the default per mode: lossy stays at 80, and `extreme` now gets its per-codec default instead of a lossy-sized Q80 result. A quality you set is still sent. `OptimizeOptions#quality` returns `nil` when unset.
31
+ - `lossless: true` used to be ignored by the server; it is now honoured (sent as `mode: "lossless"`). The SDK no longer sends `lossless` on the wire.
32
+
8
33
  ## [0.0.2] - 2026-08-22
9
34
 
10
35
  ### Added
data/README.md CHANGED
@@ -55,13 +55,27 @@ result = SmallPict.optimize(
55
55
  image_data,
56
56
  format: "avif",
57
57
  quality: 80,
58
- max_width: 1920
58
+ max_width: 1920,
59
+ mode: SmallPict::Mode::LOSSY, # LOSSY (default) | EXTREME | LOSSLESS | AUTO; replaces the deprecated `lossless:`
60
+ folder: "products/shoes" # optional, 1-3 segments; stored lower case, returned as result.folder
59
61
  )
60
62
 
61
63
  puts "Optimized CDN URL: #{result.url}"
62
64
  puts "Saved: #{result.savings_percentage}% (#{result.bytes_saved} bytes)"
63
65
  ```
64
66
 
67
+ `AUTO` picks, per image, the lowest quality that keeps the visual score (SSIMULACRA 2) at or above 70, and ignores `quality`. It is on WP Pro and Business, Velocity, Momentum and sandbox keys (5 auto jobs per minute per sandbox key); where it cannot search (PNG or GIF output, animation, time limit) the job is lossy and `mode_applied` says so.
68
+
69
+ `EXTREME` gives the smallest files on every plan. Leave `quality` out so the server uses its default for the output format; a quality you set is kept or raised to the format floor. Mode comparison: https://smallpict.app/en/docs/image-settings
70
+
71
+ ```ruby
72
+ result = SmallPict.optimize(
73
+ File.binread("hero-banner.png"),
74
+ format: "webp",
75
+ mode: SmallPict::Mode::EXTREME # no quality: the server picks it per format
76
+ )
77
+ ```
78
+
65
79
  ### 2. Rails Initializer (`config/initializers/smallpict.rb`)
66
80
 
67
81
  ```ruby
@@ -106,8 +120,37 @@ puts "Plan: #{quota.plan}, Quota Used: #{quota.quota_percentage}%"
106
120
  # 2. Invalidate CDN cache for updated images
107
121
  purge = SmallPict.purge_cdn(["https://cdn.smallpict.app/opt/hero-banner.avif"])
108
122
  puts purge.message
123
+
124
+ # 3. Purge one upload folder, or all of your files
125
+ SmallPict.purge_cdn(purge_type: "folder", folder: "products/shoes")
126
+ SmallPict.purge_cdn(purge_type: "all")
127
+ # Folder purges are limited to 10 per hour and "all" to 2 per hour: one more raises
128
+ # SmallPict::RateLimitError (429 purge_rate_limited), which the client does not retry.
109
129
  ```
110
130
 
131
+ ### Capacity and upgrade links
132
+
133
+ `get_quota` also returns the read-only `capacity` of the key's product. Members that do not apply to your plan are `nil`, and `capacity` itself is `nil` when the server could not read it or does not send it.
134
+
135
+ ```ruby
136
+ cap = SmallPict.get_quota.capacity
137
+ if cap
138
+ puts "Storage: #{cap.storage.used_bytes}/#{cap.storage.limit_bytes}" if cap.storage
139
+ puts "CDN status: #{cap.cdn.status}" if cap.cdn
140
+ puts "Account lapsed (#{cap.lapse.stage}), manage it at #{cap.manage_url}" if cap.lapse
141
+ end
142
+
143
+ begin
144
+ SmallPict.optimize("photo.jpg")
145
+ rescue SmallPict::QuotaExceededError => e
146
+ puts "Add capacity at #{e.upgrade_url}" if e.upgrade_url
147
+ rescue SmallPict::PermissionDeniedError => e
148
+ puts "Allowed modes: #{e.allowed_modes.join(', ')}" if e.allowed_modes
149
+ end
150
+ ```
151
+
152
+ Capacity is bought only in the dashboard. `manage_url` and `upgrade_url` are plain strings to show your users; the SDK never opens or fetches them.
153
+
111
154
  ---
112
155
 
113
156
  ## 🧪 Testing
@@ -68,12 +68,18 @@ module SmallPict
68
68
  Models::QuotaResponse.new(res)
69
69
  end
70
70
 
71
- def purge_cdn(urls = [], purge_type: "url")
71
+ # purge_type: "url" (default), "folder" (with `folder:`) or "all".
72
+ def purge_cdn(urls = [], purge_type: "url", folder: nil)
72
73
  url_list = urls.is_a?(Array) ? urls : [urls]
73
74
  payload = {
74
75
  purge_type: purge_type.to_s,
75
76
  urls: url_list
76
77
  }
78
+ if payload[:purge_type] == "folder"
79
+ raise ValidationError, "folder is required for purge_type \"folder\"" if folder.nil? || folder.to_s.empty?
80
+
81
+ payload[:folder] = Folder.validate!(folder)
82
+ end
77
83
 
78
84
  res = request(:post, "/v1/purge", payload)
79
85
  Models::PurgeResponse.new(res)
@@ -145,7 +151,7 @@ module SmallPict
145
151
  request_id = response.headers["x-request-id"]
146
152
  retry_after = response.headers["retry-after"]&.to_i
147
153
 
148
- if status == 429 || (status >= 500 && status <= 504)
154
+ if (status == 429 && !hourly_purge_limit?(response.body)) || (status >= 500 && status <= 504)
149
155
  if attempt <= @max_retries
150
156
  delay = base_delay * (2**(attempt - 1))
151
157
  delay = retry_after if retry_after && retry_after.positive?
@@ -184,6 +190,12 @@ module SmallPict
184
190
  end
185
191
  end
186
192
 
193
+ # 429 purge_rate_limited is an hourly limit: retrying within seconds cannot succeed.
194
+ def hourly_purge_limit?(body_str)
195
+ body = parse_body(body_str)
196
+ body.is_a?(Hash) && body["error"].is_a?(Hash) && body["error"]["code"] == "purge_rate_limited"
197
+ end
198
+
187
199
  def parse_body(body_str)
188
200
  return {} if body_str.nil? || body_str.empty?
189
201
 
@@ -195,11 +207,13 @@ module SmallPict
195
207
  def handle_error_response(status, body, request_id, retry_after)
196
208
  message = "API request failed with HTTP #{status}"
197
209
  details = nil
210
+ code = "INTERNAL_ERROR"
198
211
 
199
212
  if body.is_a?(Hash)
200
213
  if body["error"].is_a?(Hash)
201
214
  message = body["error"]["message"] || message
202
215
  details = body["error"]["details"]
216
+ code = body["error"]["code"] if body["error"]["code"].is_a?(String)
203
217
  elsif body["error"].is_a?(String)
204
218
  message = body["error"]
205
219
  elsif body["message"].is_a?(String)
@@ -219,7 +233,7 @@ module SmallPict
219
233
  raise ServerError.new(message, status_code: status, request_id: request_id, details: details)
220
234
  end
221
235
 
222
- raise Error.new(message, status_code: status, request_id: request_id, details: details)
236
+ raise Error.new(message, code: code, status_code: status, request_id: request_id, details: details)
223
237
  end
224
238
  end
225
239
 
@@ -2,7 +2,9 @@
2
2
 
3
3
  module SmallPict
4
4
  class Error < StandardError
5
- attr_reader :code, :status_code, :request_id, :details
5
+ # upgrade_url: dashboard page where the missing capacity is added
6
+ # (details.upgrade_url), or nil. The SDK never opens or fetches it.
7
+ attr_reader :code, :status_code, :request_id, :details, :upgrade_url
6
8
 
7
9
  def initialize(message = "", code: "INTERNAL_ERROR", status_code: nil, request_id: nil, details: nil)
8
10
  @raw_message = self.class.sanitize(message)
@@ -10,6 +12,8 @@ module SmallPict
10
12
  @status_code = status_code
11
13
  @request_id = request_id
12
14
  @details = details
15
+ url = details.is_a?(Hash) ? (details["upgrade_url"] || details[:upgrade_url]) : nil
16
+ @upgrade_url = url.is_a?(String) ? url : nil
13
17
  super(@raw_message)
14
18
  end
15
19
 
@@ -58,8 +62,13 @@ module SmallPict
58
62
  end
59
63
 
60
64
  class PermissionDeniedError < Error
65
+ # Modes the plan allows, from details.allowed_modes of 403 mode_not_in_plan; otherwise nil.
66
+ attr_reader :allowed_modes
67
+
61
68
  def initialize(message = "Permission denied for this resource", request_id: nil, details: nil)
62
69
  super(message, code: "FORBIDDEN", status_code: 403, request_id: request_id, details: details)
70
+ modes = details.is_a?(Hash) ? (details["allowed_modes"] || details[:allowed_modes]) : nil
71
+ @allowed_modes = modes.is_a?(Array) ? modes.grep(String) : nil
63
72
  end
64
73
  end
65
74
 
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmallPict
4
+ # Light client-side check of an upload folder. The server is the authority
5
+ # (400 invalid_folder): it lower-cases the value and enforces the full format.
6
+ module Folder
7
+ MAX_SEGMENTS = 3
8
+
9
+ def self.validate!(folder)
10
+ segments = folder.to_s.split("/", -1)
11
+ if segments.empty? || segments.any?(&:empty?) || segments.include?("..") || segments.size > MAX_SEGMENTS
12
+ raise ValidationError, "Invalid folder #{folder.inspect}: use 1-#{MAX_SEGMENTS} non-empty segments separated by '/', without '..'."
13
+ end
14
+
15
+ folder.to_s
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmallPict
4
+ # Compression modes for `optimize(mode: ...)`. The server checks the mode
5
+ # against the plan (403 mode_not_in_plan); the SDK does not.
6
+ module Mode
7
+ LOSSY = "lossy"
8
+ EXTREME = "extreme"
9
+ LOSSLESS = "lossless"
10
+ AUTO = "auto" # the server picks the quality per image from a visual score (CMP-07)
11
+ ALL = [LOSSY, EXTREME, LOSSLESS, AUTO].freeze
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmallPict
4
+ module Models
5
+ CapacityStorage = Data.define(:limit_bytes, :used_bytes, :base_bytes, :addon_blocks)
6
+ CapacityCdn = Data.define(:limit_bytes, :used_bytes, :status)
7
+ CapacityBooster = Data.define(:active, :wp_quota_bytes, :api_transforms, :cdn_bytes, :next_expiry)
8
+ CapacityExpiry = Data.define(:kind, :units, :expires_at)
9
+ CapacityLapse = Data.define(:stage, :delete_after)
10
+
11
+ # Read-only capacity of the key's product from GET /v1/quota. A member that
12
+ # does not apply to the product or plan is nil. manage_url is a plain string:
13
+ # the SDK never opens or fetches it; capacity is bought only in the dashboard.
14
+ Capacity = Data.define(:period_end, :storage, :cdn, :booster, :lapse, :manage_url) do
15
+ def self.from_hash(data)
16
+ return nil unless data.is_a?(Hash)
17
+
18
+ s = get(data, :storage)
19
+ cd = get(data, :cdn)
20
+ b = get(data, :booster) || {}
21
+ e = get(b, :next_expiry)
22
+ l = get(data, :lapse)
23
+ new(
24
+ period_end: get(data, :period_end),
25
+ storage: s && CapacityStorage.new(limit_bytes: get(s, :limit_bytes), used_bytes: get(s, :used_bytes),
26
+ base_bytes: get(s, :base_bytes), addon_blocks: get(s, :addon_blocks)),
27
+ cdn: cd && CapacityCdn.new(limit_bytes: get(cd, :limit_bytes), used_bytes: get(cd, :used_bytes),
28
+ status: get(cd, :status)),
29
+ booster: CapacityBooster.new(
30
+ active: get(b, :active) == true,
31
+ wp_quota_bytes: get(b, :wp_quota_bytes),
32
+ api_transforms: get(b, :api_transforms),
33
+ cdn_bytes: get(b, :cdn_bytes),
34
+ next_expiry: e && CapacityExpiry.new(kind: get(e, :kind), units: get(e, :units),
35
+ expires_at: get(e, :expires_at))
36
+ ),
37
+ lapse: l && CapacityLapse.new(stage: get(l, :stage), delete_after: get(l, :delete_after)),
38
+ manage_url: get(data, :manage_url)
39
+ )
40
+ end
41
+
42
+ # String keys from JSON, symbol keys from callers; keeps false and nil as sent.
43
+ def self.get(hash, key)
44
+ hash.is_a?(Hash) ? hash.fetch(key.to_s) { hash[key] } : nil
45
+ end
46
+ end
47
+ end
48
+ end
@@ -4,7 +4,7 @@ module SmallPict
4
4
  module Models
5
5
  class JobStatusResult
6
6
  attr_reader :job_id, :status, :url, :format, :bytes_saved,
7
- :error, :created_at, :updated_at
7
+ :error, :created_at, :updated_at, :mode_applied, :quality_applied, :folder
8
8
 
9
9
  def initialize(attributes = {})
10
10
  @job_id = attributes[:job_id] || attributes["job_id"]
@@ -15,6 +15,10 @@ module SmallPict
15
15
  @error = attributes[:error] || attributes["error"]
16
16
  @created_at = attributes[:created_at] || attributes["created_at"]
17
17
  @updated_at = attributes[:updated_at] || attributes["updated_at"]
18
+ # Reported by the server; nil for jobs created before modes existed.
19
+ @mode_applied = attributes[:mode_applied] || attributes["mode_applied"]
20
+ @quality_applied = attributes[:quality_applied] || attributes["quality_applied"]
21
+ @folder = attributes[:folder] || attributes["folder"]
18
22
  end
19
23
  end
20
24
  end
@@ -4,16 +4,22 @@ module SmallPict
4
4
  module Models
5
5
  class OptimizeOptions
6
6
  attr_reader :format, :quality, :max_width, :max_height, :max_dimension, :fit,
7
- :lossless, :strip_metadata, :filename, :mime_type, :content_type, :idempotency_key
7
+ :mode, :lossless, :folder, :strip_metadata, :filename, :mime_type, :content_type, :idempotency_key
8
8
 
9
+ # mode: SmallPict::Mode::LOSSY (server default when omitted), EXTREME or LOSSLESS.
10
+ # quality: optional 1-100; omitted when nil so the server picks the default per mode.
11
+ # folder: optional upload folder, e.g. "products/shoes"; the server lower-cases it.
12
+ # lossless: deprecated, use `mode: SmallPict::Mode::LOSSLESS`; removal not before the next major.
9
13
  def initialize(
10
14
  format: "auto",
11
- quality: 80,
15
+ quality: nil,
12
16
  max_width: nil,
13
17
  max_height: nil,
14
18
  max_dimension: nil,
15
19
  fit: "cover",
16
- lossless: false,
20
+ mode: nil,
21
+ lossless: nil,
22
+ folder: nil,
17
23
  strip_metadata: true,
18
24
  filename: nil,
19
25
  mime_type: nil,
@@ -27,6 +33,8 @@ module SmallPict
27
33
  @max_dimension = max_dimension
28
34
  @fit = fit.to_s
29
35
  @lossless = lossless
36
+ @mode = resolve_mode(mode, lossless)
37
+ @folder = folder.nil? ? nil : Folder.validate!(folder)
30
38
  @strip_metadata = strip_metadata
31
39
  @filename = filename
32
40
  @mime_type = mime_type || content_type
@@ -42,10 +50,26 @@ module SmallPict
42
50
  max_height: @max_height,
43
51
  max_dimension: @max_dimension,
44
52
  fit: @fit,
45
- lossless: @lossless,
53
+ mode: @mode,
54
+ folder: @folder,
46
55
  strip_metadata: @strip_metadata
47
56
  }.compact
48
57
  end
58
+
59
+ private
60
+
61
+ # `lossless:` is deprecated: map it to `mode:` and never send it on the wire.
62
+ def resolve_mode(mode, lossless)
63
+ mode = mode&.to_s
64
+ unless lossless.nil?
65
+ Kernel.warn("SmallPict: `lossless:` is deprecated, use `mode: SmallPict::Mode::LOSSLESS`.",
66
+ category: :deprecated)
67
+ end
68
+ return mode unless lossless == true
69
+ return Mode::LOSSLESS if mode.nil? || mode == Mode::LOSSLESS
70
+
71
+ raise ValidationError, "`lossless: true` contradicts `mode: #{mode.inspect}`; set only `mode:`."
72
+ end
49
73
  end
50
74
  end
51
75
  end
@@ -5,7 +5,7 @@ module SmallPict
5
5
  class OptimizeResult
6
6
  attr_reader :job_id, :status, :url, :format, :original_size,
7
7
  :compressed_size, :bytes_saved, :savings_percentage,
8
- :upload_url, :data
8
+ :upload_url, :data, :folder
9
9
 
10
10
  def initialize(attributes = {})
11
11
  @job_id = attributes[:job_id] || attributes["job_id"] || "sync"
@@ -18,6 +18,8 @@ module SmallPict
18
18
  @savings_percentage = (attributes[:savings_percentage] || attributes["savings_percentage"] || calculate_savings_percentage).to_f.round(2)
19
19
  @upload_url = attributes[:upload_url] || attributes["upload_url"]
20
20
  @data = attributes[:data]
21
+ # Normalized (lower case) by the server; nil without a folder.
22
+ @folder = attributes[:folder] || attributes["folder"]
21
23
  end
22
24
 
23
25
  private
@@ -5,7 +5,7 @@ module SmallPict
5
5
  class QuotaResponse
6
6
  attr_reader :plan, :bytes_used, :quota_limit, :quota_percentage,
7
7
  :cdn_egress_used_bytes, :cdn_egress_quota_bytes,
8
- :active_keys_count, :active_sites_count
8
+ :active_keys_count, :active_sites_count, :capacity
9
9
 
10
10
  def initialize(attributes = {})
11
11
  @plan = attributes[:plan] || attributes["plan"] || "free"
@@ -16,6 +16,8 @@ module SmallPict
16
16
  @cdn_egress_quota_bytes = attributes[:cdn_egress_quota_bytes] || attributes["cdn_egress_quota_bytes"]
17
17
  @active_keys_count = attributes[:active_keys_count] || attributes["active_keys_count"]
18
18
  @active_sites_count = attributes[:active_sites_count] || attributes["active_sites_count"]
19
+ # nil when the server could not read capacity, and from older servers that omit it.
20
+ @capacity = Capacity.from_hash(attributes[:capacity] || attributes["capacity"])
19
21
  end
20
22
 
21
23
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SmallPict
4
- VERSION = "0.0.5"
4
+ VERSION = "0.0.7"
5
5
  end
data/lib/smallpict.rb CHANGED
@@ -4,8 +4,11 @@ require_relative "smallpict/version"
4
4
  require_relative "smallpict/configuration"
5
5
  require_relative "smallpict/crypto"
6
6
  require_relative "smallpict/errors"
7
+ require_relative "smallpict/mode"
8
+ require_relative "smallpict/folder"
7
9
  require_relative "smallpict/models/optimize_options"
8
10
  require_relative "smallpict/models/optimize_result"
11
+ require_relative "smallpict/models/capacity"
9
12
  require_relative "smallpict/models/quota_response"
10
13
  require_relative "smallpict/models/purge_response"
11
14
  require_relative "smallpict/models/job_status_result"
@@ -39,8 +42,8 @@ module SmallPict
39
42
  client.get_quota
40
43
  end
41
44
 
42
- def purge_cdn(urls = [], purge_type: "url")
43
- client.purge_cdn(urls, purge_type: purge_type)
45
+ def purge_cdn(urls = [], purge_type: "url", folder: nil)
46
+ client.purge_cdn(urls, purge_type: purge_type, folder: folder)
44
47
  end
45
48
 
46
49
  def validate_key
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smallpict
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - SmallPict Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-02 00:00:00.000000000 Z
11
+ date: 2026-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -98,6 +98,9 @@ files:
98
98
  - lib/smallpict/configuration.rb
99
99
  - lib/smallpict/crypto.rb
100
100
  - lib/smallpict/errors.rb
101
+ - lib/smallpict/folder.rb
102
+ - lib/smallpict/mode.rb
103
+ - lib/smallpict/models/capacity.rb
101
104
  - lib/smallpict/models/job_status_result.rb
102
105
  - lib/smallpict/models/optimize_options.rb
103
106
  - lib/smallpict/models/optimize_result.rb
@@ -109,8 +112,8 @@ licenses:
109
112
  - MIT
110
113
  metadata:
111
114
  homepage_uri: https://smallpict.app
112
- source_code_uri: https://github.com/tuxnoob/smallpict-ruby
113
- changelog_uri: https://github.com/tuxnoob/smallpict-ruby/blob/main/CHANGELOG.md
115
+ source_code_uri: https://github.com/smallPict/smallpict-ruby
116
+ changelog_uri: https://github.com/smallPict/smallpict-ruby/blob/main/CHANGELOG.md
114
117
  post_install_message:
115
118
  rdoc_options: []
116
119
  require_paths: