Skip to content

Prepare uploadcare-ruby 5.0.0 release#189

Merged
vipulnsward merged 60 commits into
mainfrom
5-0-stable
May 18, 2026
Merged

Prepare uploadcare-ruby 5.0.0 release#189
vipulnsward merged 60 commits into
mainfrom
5-0-stable

Conversation

@vipulnsward
Copy link
Copy Markdown
Collaborator

@vipulnsward vipulnsward commented May 17, 2026

Summary

Prepare uploadcare-ruby for the final 5.0.0 release from the v5 rewrite branch.

This PR promotes the existing 5.0.0.rc1 cut to stable 5.0.0 and keeps the release-facing docs aligned with the client-first API shape.

What changed

  • Bump Uploadcare::VERSION and version spec from 5.0.0.rc1 to 5.0.0.
  • Replace the RC release notes with final docs/release-notes-5.0.0.md.
  • Update CHANGELOG.md with the final release date, rollout notes, and release-readiness fixes.
  • Add v5-aware context7.json rules for Uploadcare::Client, client.api.rest, client.api.upload, examples, and migration docs.
  • Port the relevant upstream file-handle fix for generated test fixtures.

Release notes

The final release notes call out:

  • v5 is stable with no API changes since 5.0.0.rc1.
  • Applications should use Uploadcare::Client as the primary API.
  • Raw endpoint parity remains available through client.api.rest and client.api.upload.
  • Upgrade risk areas: Ruby >= 3.3, removed internals, large multipart uploads, and custom REST signing integrations.
  • Post-deploy monitoring focus: upload errors, multipart worker failures, and REST signing errors.

Validation

Ran locally on 5-0-stable:

  • ruby -rjson -e 'JSON.parse(File.read("context7.json"))'
  • Ruby syntax check for all files under api_examples/ and examples/
  • rtk mise exec -- bundle exec rake
  • rtk mise exec -- gem build uploadcare-ruby.gemspec --output /tmp/uploadcare-ruby-5.0.0.gem
  • rtk mise exec -- gem specification /tmp/uploadcare-ruby-5.0.0.gem version

The built gem reports version 5.0.0.

I did not run live Uploadcare API examples because they require real credentials and perform external side effects.

Summary by CodeRabbit

  • New Features

    • Explicit client-based API for multi-account support
    • Enhanced file operations with typed resource objects
    • Smart upload routing (direct/multipart/URL-based)
    • Webhook integration and management
    • Document and video conversion helpers
    • Add-on execution API (ClamAV, Rekognition, Remove.bg)
  • Changed

    • Configuration now per-client instead of global
    • Ruby version requirement updated to 3.3+
    • API responses return typed resources with pagination support
  • Documentation

    • Migration guide from v4.x to v5
    • Updated README with new usage patterns
    • API examples and workflow demonstrations

Review Change Stack

…ubbing

- Fix auth/header handling, pagination, upload behavior, and README examples
- Add multi-account spec coverage for per-client configuration
- Scrub VCR cassettes and harden VCR filters to avoid leaking credentials
CI was failing because RuboCop runs as part of rake.

- Refactor RestClient signature body computation to reduce complexity
- Extract parallel upload worker to satisfy Metrics/BlockLength
- Clean up VCR scrubber regexes and multi-account spec naming
Use per-example generated secret keys to prevent secret scanners from flagging test fixtures.
Replace pubkey:secret placeholders in recorded Authorization headers with a neutral placeholder to avoid triggering secret scanners.
- split v5 migration notes into MIGRATING_V5.md and link from README
- remove duplicate README upload sections and correct API examples
- fix examples to unwrap Uploadcare::Result where needed and use current progress payload
- fix GroupClient path escaping to preserve '~' in group IDs (fixes signed REST calls)
- keep FileMetadata cache consistent when operating on explicit different UUIDs
Restructure the codebase into clean layered architecture:

- Internal layer (Authenticator, ErrorHandler, ThrottleHandler, UserAgent,
  SignatureGenerator, UploadParamsGenerator) under Internal::
- API parity layer with Api::Rest (8 endpoint classes) and Api::Upload
  (2 endpoint classes) providing 1:1 endpoint wrappers
- Resources layer (File, Group, Project, Webhook, AddonExecution,
  DocumentConversion, VideoConversion, FileMetadata) as domain objects
- Collections layer (Paginated, BatchResult) for list/batch responses
- Operations layer (UploadRouter, MultipartUpload) for workflow logic
- Client with domain accessors (files, groups, uploads, project, webhooks,
  addons, file_metadata, conversions) and raw API access

Remove old Clients::* namespace, Uploader resource, and param utilities.
Full test suite: 589 examples, 0 failures, 93% coverage.
Rewrite multipart_upload_spec.rb with 37 examples covering:
- Sequential upload: chunking, presigned URLs, progress callbacks,
  request_options/store/metadata passthrough, custom part_size
- Parallel upload: multi-threaded part upload, progress reporting,
  more threads than parts, worker error propagation
- Edge cases: file without #size (File.size fallback), #original_filename
  support, unknown MIME type fallback, multipart_start/complete failures,
  sequential upload_part_to_url errors, excess presigned URLs

Split exception/upload_error.rb into separate files per Zeitwerk
convention (UploadTimeoutError, MultipartUploadError, UnknownStatusError).

Remove unreachable ThreadError rescue in parallel worker.

100% line coverage on operations/multipart_upload.rb (70/70 lines).
Full suite: 619 examples, 0 failures, 95.64% coverage.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

Walkthrough

Major refactor to a client-scoped v5 SDK: adds Uploadcare::Client with Rest/Upload transports, resources for domains, upload operations (including multipart), examples and docs; removes legacy clients/entities/params; updates CI, RuboCop, .gitignore; adds env example; bumps version to 5.0.0.

Changes

V5 Client-First SDK

Layer / File(s) Summary
Client and transports
lib/uploadcare.rb, lib/uploadcare/client.rb, lib/uploadcare/client/api.rb, lib/uploadcare/api/rest*.rb, lib/uploadcare/api/upload*.rb
Introduces client-scoped API with REST/Upload transports, auth, error/throttle handling, and endpoint accessors.
Resource layer
lib/uploadcare/resources/*, lib/uploadcare/collections/*
Adds resource wrappers for files, groups, metadata, project, webhooks, conversions; paginated and batch results.
Upload operations
lib/uploadcare/operations/*, lib/uploadcare/internal/*
Implements smart upload router, multipart upload engine, IO/params/signature/user-agent helpers.
Legacy removal
lib/uploadcare/client/*_client.rb, lib/uploadcare/entity/*, lib/uploadcare/param/*
Removes old ApiStruct-based clients/entities/params and related helpers.
Examples and docs
README.md, MIGRATING_V5.md, docs/*, examples/*, api_examples/*
Rewrites docs, adds migration guide and runnable examples; standardizes example runners.
Tooling & meta
.github/workflows/*, .rubocop.yml, .gitignore, .env.example, CHANGELOG.md, mise.toml, Rakefile, lib/uploadcare/version.rb
Modernizes CI, lint config, ignores, adds env template, release notes, version 5.0.0, rake tasks.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App as Client App
  participant UC as Uploadcare::Client
  participant Up as Upload API
  participant S3 as S3 (presigned)
  participant Rest as REST API

  App->>UC: uploads.multipart_upload(file, store:true)
  UC->>Up: POST /multipart/start (filename,size,ctype)
  Up-->>UC: {uuid, parts:[urls]}
  loop For each part
    UC->>S3: PUT part to presigned URL
    S3-->>UC: 200 OK
  end
  UC->>Up: POST /multipart/complete (uuid)
  Up-->>UC: {uuid}
  UC->>Rest: GET /files/{uuid}/
  Rest-->>UC: file info
  UC-->>App: Uploadcare::Resources::File
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • rsedykh
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 5-0-stable

# Conflicts:
#	api_examples/upload_api/post_base.rb
#	api_examples/upload_api/post_multipart_complete.rb
#	api_examples/upload_api/post_multipart_start.rb
#	api_examples/upload_api/put_presigned_url_x.rb
#	context7.json
#	spec/uploadcare/entity/decorator/paginator_spec.rb
@vipulnsward vipulnsward marked this pull request as ready for review May 17, 2026 01:25
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🧹 Nitpick comments (6)
examples/README.md (1)

23-29: 💤 Low value

Consider varying sentence structure for improved readability.

Three consecutive bullet descriptions start with "Upload," which can feel repetitive. Consider rephrasing one or two for variety, such as "Demonstrate batch uploading" or "Show multipart upload with progress."

✨ Suggested rewording for variety
 - `examples/simple_upload.rb`
   Upload one file and print its UUID and CDN URL.
 - `examples/upload_with_progress.rb`
-  Upload one large file with multipart progress reporting.
+  Demonstrate multipart progress reporting for large files.
 - `examples/batch_upload.rb`
-  Upload multiple files in one call.
+  Batch-upload multiple files in one call.
 - `examples/large_file_upload.rb`
   Force multipart upload and show throughput details.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/README.md` around lines 23 - 29, The three adjacent bullets in the
README are repetitive (all begin with "Upload"); update the bullet text for the
entries referencing examples/upload_with_progress.rb, examples/batch_upload.rb,
and examples/large_file_upload.rb to vary sentence structure and improve
readability (e.g., change one to "Demonstrate batch uploading with multiple
files" and another to "Show multipart upload with progress and throughput
details") while keeping the filenames and intent the same.
lib/uploadcare/api/rest/webhooks.rb (1)

41-42: 💤 Low value

Consider more idiomatic optional parameter merging.

The current pattern creates single-key hashes, compacts them, then merges. This works but is unconventional.

♻️ More idiomatic alternatives
     payload = {
       target_url: options[:target_url],
       event: options[:event] || 'file.uploaded',
       is_active: options[:is_active].nil? || options[:is_active]
     }
-    payload.merge!({ signing_secret: options[:signing_secret] }.compact)
-    payload.merge!({ version: options[:version] }.compact)
+    payload.merge!(options.slice(:signing_secret, :version).compact)

Or:

payload[:signing_secret] = options[:signing_secret] if options.key?(:signing_secret)
payload[:version] = options[:version] if options.key?(:version)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/uploadcare/api/rest/webhooks.rb` around lines 41 - 42, Replace the two
single-key compact merges with idiomatic conditional assignments: set
payload[:signing_secret] = options[:signing_secret] if
options.key?(:signing_secret) and set payload[:version] = options[:version] if
options.key?(:version), referencing the existing payload and options variables
and the :signing_secret and :version keys (alternatively use
payload.merge!(options.slice(:signing_secret, :version)) only if Hash#slice is
guaranteed available).
examples/large_file_upload.rb (1)

8-8: ⚡ Quick win

Validate that threads count is positive.

If a user passes 0, a negative number, or invalid input, to_i will produce 0 or a negative value, which may cause unexpected behavior in the multipart upload.

🛡️ Proposed validation
 file_path = ARGV[0]
-threads = (ARGV[1] || 4).to_i
+threads = (ARGV[1] || 4).to_i
+threads = 4 if threads <= 0

 unless file_path && File.exist?(file_path)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/large_file_upload.rb` at line 8, The code sets threads = (ARGV[1] ||
4).to_i which can yield 0 or negative values; validate and normalize ARGV[1] so
threads is always a positive integer (fallback to 4). Replace the simple .to_i
with parsing (e.g., Integer(ARGV[1]) rescue nil), check that the parsed value is
>= 1, and if not assign the default 4 (or clamp to a max if desired); update any
references to the threads variable accordingly to ensure uploads never run with
non-positive thread counts.
.rubocop.yml (1)

7-8: ⚡ Quick win

Align lint target with the minimum supported Ruby version.

TargetRubyVersion: 3.4 weakens guardrails when your gem declares spec.required_ruby_version = '>= 3.3'. This allows 3.4-only syntax to slip into code that should remain compatible with Ruby 3.3.

Suggested fix
-  TargetRubyVersion: 3.4
+  TargetRubyVersion: 3.3
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.rubocop.yml around lines 7 - 8, The RuboCop target is set to 3.4 but your
gem declares spec.required_ruby_version = '>= 3.3', so change the
TargetRubyVersion value in .rubocop.yml from 3.4 to 3.3 (or otherwise align it
with spec.required_ruby_version) to prevent 3.4-only syntax from being allowed;
update the TargetRubyVersion setting so RuboCop enforces compatibility with Ruby
3.3.
lib/uploadcare/api/rest/document_conversions.rb (1)

1-3: ⚡ Quick win

Add an explicit require 'uri' for URI encoding in this file.

The info method uses URI.encode_www_form_component (lines 22–23) but lacks an explicit require. Similar files in the same directory (file_metadata.rb, groups.rb) explicitly require it; doing so here ensures no hidden load-order dependencies and maintains consistency.

Proposed patch
 # frozen_string_literal: true
+require 'uri'

 # REST API endpoint for document conversion operations.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/uploadcare/api/rest/document_conversions.rb` around lines 1 - 3, Add an
explicit require for URI at the top of the file to avoid load-order
dependencies: add "require 'uri'" near the other frozen string comment and file
header so the info method (which calls URI.encode_www_form_component) has the
needed library available, matching the pattern used in file_metadata.rb and
groups.rb.
lib/uploadcare/resources/webhook.rb (1)

57-64: ⚡ Quick win

Fail fast when webhook update payload is empty.

update currently sends a request even when no updatable fields are provided. Guarding this early makes failures clearer and avoids a pointless API call.

Proposed change
 def self.update(id:, client: nil, config: Uploadcare.configuration, request_options: {}, **options)
   resolved_client = resolve_client(client: client, config: config)
   payload = options.slice(:target_url, :event, :signing_secret, :version)
   payload[:is_active] = options[:is_active] if options.key?(:is_active)
+  raise ArgumentError, 'at least one webhook field must be provided' if payload.empty?
 
   response = Uploadcare::Result.unwrap(
     resolved_client.api.rest.webhooks.update(id: id, options: payload, request_options: request_options)
   )
   new(response, resolved_client)
 end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/uploadcare/resources/webhook.rb` around lines 57 - 64, The update method
builds payload from options.slice(:target_url, :event, :signing_secret,
:version) and conditionally sets :is_active, but currently proceeds to call
resolved_client.api.rest.webhooks.update even when payload is empty; change
Webhook.update to check if payload.empty? after building payload (and after
setting :is_active) and raise a clear ArgumentError (or custom error) like "no
updatable fields provided" to fail fast and avoid the unnecessary API call,
keeping the existing resolved_client/api call unchanged when payload is present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/batch_upload.rb`:
- Around line 15-27: Validate each path using File.file? and File.readable?
instead of File.exist? (check the file_paths loop) and defer opening files until
inside the protected begin block; move the File.open calls into the begin where
the ensure block can safely close any successfully opened file handles
(reference file_paths, File.file?, File.readable?, File.open, and the ensure
clause). Also make the ensure clause robust by iterating over the files
collection and closing only non-nil, open handles to avoid errors if some opens
failed.

In `@examples/large_file_upload.rb`:
- Line 55: The percentage calculation can divide by zero when progress[:total]
is 0; update the logic around the assignment to percentage so it guards against
zero or nil totals (e.g., check progress[:total].to_i > 0) and return 0 (or an
appropriate default) when total is zero, otherwise perform the existing
((progress[:uploaded].to_f / progress[:total]) * 100).round computation; locate
the assignment to percentage in examples/large_file_upload.rb and modify that
expression to include the guard using progress[:total] and progress[:uploaded]
symbols.

In `@examples/simple_upload.rb`:
- Around line 9-12: The current check uses File.exist?(file_path) which permits
directories and will break when the code later treats the path as a file; change
the validation around file_path to ensure it's a regular readable file (e.g.,
use File.file? and/or File.readable?) and exit if not. Also update the usage and
example strings ('Usage: ruby simple_upload.rb <file_path>' and 'Example: ruby
simple_upload.rb photo.jpg') to point to the repository script path (e.g.,
examples/simple_upload.rb) so users run the correct script from the repo root.
Ensure these changes are applied where file_path is validated and where the
usage/example strings are defined.

In `@examples/upload_with_progress.rb`:
- Line 50: The speed calculation can divide by zero when elapsed is 0; update
the progress callback where speed_mbps = uploaded_mb / elapsed to guard against
zero or near-zero elapsed (e.g., if elapsed <= 0 or elapsed < a small epsilon)
and set speed_mbps to 0 (or skip calculation) in that case; ensure you only
perform uploaded_mb / elapsed when elapsed is valid so speed_mbps, uploaded_mb
and elapsed remain stable and do not produce Infinity or errors.
- Line 45: The percentage calculation using percentage =
((progress[:uploaded].to_f / progress[:total]) * 100).round must guard against
division by zero; update the logic around the percentage assignment to check
progress[:total] (or progress[:total].to_f) for zero or nil and return a safe
default (e.g., 0) when total is zero, otherwise perform the current division and
rounding; reference the percentage variable and the progress[:total] operand to
locate and modify the expression.

In `@lib/uploadcare/api/rest/files.rb`:
- Around line 40-41: This file uses URI.encode_www_form_component (see the
assignment to encoded_uuid and the subsequent rest.get calls) but does not
require 'uri', creating a load-order dependency; fix by adding require 'uri' at
the top of lib/uploadcare/api/rest/files.rb so that calls to
URI.encode_www_form_component used in the encoded_uuid assignment and related
lines always resolve.

In `@lib/uploadcare/api/upload.rb`:
- Around line 106-117: The code reads part_data with part_data.read which
consumes an IO and causes empty retries; before calling read in the upload part
flow (the block that calls upload_part_request and uses
retry_part_upload_or_raise!), add a rewind guard: call part_data.rewind if
part_data.respond_to?(:rewind) immediately before reading to ensure the IO is
reset on the initial attempt and on any retry; keep the rest of the retry logic
(retry_part_upload_or_raise!, retries, max_retries, upload_part_request,
success_response?) unchanged so subsequent reads return the full data.
- Around line 102-103: The connection URL built in the upload setup (the line
creating conn = Faraday.new using "#{uri.scheme}://#{uri.host}") omits a
non-default port from presigned URLs; update the Faraday.new invocation to
include uri.port so the constructed base URL preserves any non-standard port
from the parsed uri (use uri.scheme, uri.host and uri.port when building the URL
for conn) to ensure requests target the correct endpoint.

In `@lib/uploadcare/internal/upload_io.rb`:
- Around line 53-64: The wrap_stream class method currently creates a Tempfile
via Tempfile.new and may leave it orphaned if IO.copy_stream or rewind raises;
modify wrap_stream to ensure the tempfile is closed and unlinked on any
exception during materialization (use begin/ensure or rescue to call
tempfile.close! or unlink/close) before re-raising the error, while still
returning the new(tempfile, ...) only on success; keep references to
Tempfile.new, IO.copy_stream, source.rewind and new(tempfile, original_filename:
..., cleanup: true) so the cleanup is applied around those operations.

In `@lib/uploadcare/resources/base_resource.rb`:
- Around line 30-34: The assign_attributes method in BaseResource should guard
against nil or non-enumerable inputs before calling attributes.each; update the
assign_attributes implementation to return early (or normalize) when attributes
is nil or does not respond_to?(:each) — e.g., add a check at the top of
assign_attributes that skips iteration unless attributes is present and
enumerable (attributes && attributes.respond_to?(:each) or
attributes.is_a?(Hash)) so public_send(setter, value) is only invoked for valid
attribute collections.

In `@lib/uploadcare/resources/document_conversion.rb`:
- Around line 31-36: The convert_document flow builds conversion paths without
checking inputs; update DocumentConversion.convert_document to validate required
params before using them: ensure params[:uuid] (or params[:uuids]) and
params[:format] are present and non-empty and raise a clear ArgumentError if
not, then proceed to call resolve_client and build paths; likewise, before
calling status (the method that uses token), validate that token is provided and
non-empty and raise an ArgumentError if it is missing so you fail fast with
clear errors.

In `@lib/uploadcare/resources/file_metadata.rb`:
- Around line 21-27: The index method currently replaces `@metadata` regardless of
the uuid argument which can leave `@uuid` and `@metadata` inconsistent; change index
to compute a target_uuid = uuid || `@uuid` and, if the fetch was for a different
uuid, update the instance's `@uuid` to that target_uuid before assigning `@metadata`
(i.e., set `@uuid` = target_uuid then set `@metadata` = response... only after using
client.api.rest.file_metadata.index with target_uuid) so the instance cache
remains consistent with the bound UUID; refer to the index method and instance
vars `@uuid` and `@metadata` (and other instance-mutating methods
update/show/delete) when applying this fix.

In `@lib/uploadcare/resources/file.rb`:
- Around line 268-285: Update the YARD `@return` for the conversion helper methods
(e.g., convert_to_document and convert_to_video) to reflect that they may return
either an Uploadcare::Resources::File or the raw conversion result when no UUID
is available yet; specifically change the documented return type from
"Uploadcare::Resources::File" to something like "Uploadcare::Resources::File,
Hash" (or the appropriate raw result type) so it matches
process_convert_result's fallback behavior.
- Around line 328-337: The conversion dispatch is omitting the client argument
so converter.convert_document and converter.convert fall back to the global
client; update both calls to pass client: client (alongside params:
params_with_symbolized_keys, options: options, config: config, request_options:
request_options) so the converter uses the same client instance as the resource
(matching how find preserves client: client).

In
`@spec/fixtures/vcr_cassettes/Upload_API_Integration/Performance/Parallel_multipart_upload/parallel_upload_is_faster_than_sequential.yml`:
- Around line 20-21: The VCR cassette for the test
parallel_upload_is_faster_than_sequential recorded a 400 limit error ("File size
exceeds project limit") instead of the intended successful benchmark; re-record
the cassette used by the parallel_upload_is_faster_than_sequential test with a
payload that is within the project's allowed file size (or adjust the test to
intentionally assert a limit-failure and rename/scope it accordingly), so the
YAML fixture no longer contains the 400/limit-error response and reflects the
true performance path.

In `@spec/TESTING.md`:
- Line 67: Rename the heading that currently mentions "allow_any_instance_of" to
avoid promoting that API; update the section title to something like "Use RSpec
Mocks (receiver-scoped stubs)" or "Use RSpec Mocks (avoid
allow_any_instance_of)" so the heading aligns with the example and preferred
pattern, and ensure any internal references to the old heading text
"allow_any_instance_of" are adjusted accordingly.
- Line 84: The fenced code block containing the directory tree (the
triple-backtick block showing "spec/ ├── fixtures/ … vcr_cassettes") is
unlabeled and triggers markdownlint MD040; add a language identifier (e.g.,
"text") immediately after the opening backticks so the block becomes ```text and
the linter stops flagging it.

---

Nitpick comments:
In @.rubocop.yml:
- Around line 7-8: The RuboCop target is set to 3.4 but your gem declares
spec.required_ruby_version = '>= 3.3', so change the TargetRubyVersion value in
.rubocop.yml from 3.4 to 3.3 (or otherwise align it with
spec.required_ruby_version) to prevent 3.4-only syntax from being allowed;
update the TargetRubyVersion setting so RuboCop enforces compatibility with Ruby
3.3.

In `@examples/large_file_upload.rb`:
- Line 8: The code sets threads = (ARGV[1] || 4).to_i which can yield 0 or
negative values; validate and normalize ARGV[1] so threads is always a positive
integer (fallback to 4). Replace the simple .to_i with parsing (e.g.,
Integer(ARGV[1]) rescue nil), check that the parsed value is >= 1, and if not
assign the default 4 (or clamp to a max if desired); update any references to
the threads variable accordingly to ensure uploads never run with non-positive
thread counts.

In `@examples/README.md`:
- Around line 23-29: The three adjacent bullets in the README are repetitive
(all begin with "Upload"); update the bullet text for the entries referencing
examples/upload_with_progress.rb, examples/batch_upload.rb, and
examples/large_file_upload.rb to vary sentence structure and improve readability
(e.g., change one to "Demonstrate batch uploading with multiple files" and
another to "Show multipart upload with progress and throughput details") while
keeping the filenames and intent the same.

In `@lib/uploadcare/api/rest/document_conversions.rb`:
- Around line 1-3: Add an explicit require for URI at the top of the file to
avoid load-order dependencies: add "require 'uri'" near the other frozen string
comment and file header so the info method (which calls
URI.encode_www_form_component) has the needed library available, matching the
pattern used in file_metadata.rb and groups.rb.

In `@lib/uploadcare/api/rest/webhooks.rb`:
- Around line 41-42: Replace the two single-key compact merges with idiomatic
conditional assignments: set payload[:signing_secret] = options[:signing_secret]
if options.key?(:signing_secret) and set payload[:version] = options[:version]
if options.key?(:version), referencing the existing payload and options
variables and the :signing_secret and :version keys (alternatively use
payload.merge!(options.slice(:signing_secret, :version)) only if Hash#slice is
guaranteed available).

In `@lib/uploadcare/resources/webhook.rb`:
- Around line 57-64: The update method builds payload from
options.slice(:target_url, :event, :signing_secret, :version) and conditionally
sets :is_active, but currently proceeds to call
resolved_client.api.rest.webhooks.update even when payload is empty; change
Webhook.update to check if payload.empty? after building payload (and after
setting :is_active) and raise a clear ArgumentError (or custom error) like "no
updatable fields provided" to fail fast and avoid the unnecessary API call,
keeping the existing resolved_client/api call unchanged when payload is present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: deed7f08-8cf8-43b6-a6de-f46adc989bba

📥 Commits

Reviewing files that changed from the base of the PR and between 840f920 and ac75818.

📒 Files selected for processing (300)
  • .env.example
  • .github/workflows/gem-push.yml
  • .github/workflows/ruby.yml
  • .gitignore
  • .rubocop.yml
  • CHANGELOG.md
  • Gemfile
  • MIGRATING_V5.md
  • README.md
  • Rakefile
  • api_examples/README.md
  • api_examples/rest_api/delete_files_storage.rb
  • api_examples/rest_api/delete_files_uuid_metadata_key.rb
  • api_examples/rest_api/delete_files_uuid_storage.rb
  • api_examples/rest_api/delete_groups_uuid.rb
  • api_examples/rest_api/delete_webhooks_unsubscribe.rb
  • api_examples/rest_api/get_addons_aws_rekognition_detect_labels_execute_status.rb
  • api_examples/rest_api/get_addons_aws_rekognition_detect_moderation_labels_execute_status.rb
  • api_examples/rest_api/get_addons_remove_bg_execute_status.rb
  • api_examples/rest_api/get_addons_uc_clamav_virus_scan_execute_status.rb
  • api_examples/rest_api/get_convert_document_status_token.rb
  • api_examples/rest_api/get_convert_document_uuid.rb
  • api_examples/rest_api/get_convert_video_status_token.rb
  • api_examples/rest_api/get_files.rb
  • api_examples/rest_api/get_files_uuid.rb
  • api_examples/rest_api/get_files_uuid_metadata.rb
  • api_examples/rest_api/get_files_uuid_metadata_key.rb
  • api_examples/rest_api/get_groups.rb
  • api_examples/rest_api/get_groups_uuid.rb
  • api_examples/rest_api/get_project.rb
  • api_examples/rest_api/get_webhooks.rb
  • api_examples/rest_api/post_addons_aws_rekognition_detect_labels_execute.rb
  • api_examples/rest_api/post_addons_aws_rekognition_detect_moderation_labels_execute.rb
  • api_examples/rest_api/post_addons_remove_bg_execute.rb
  • api_examples/rest_api/post_addons_uc_clamav_virus_scan_execute.rb
  • api_examples/rest_api/post_convert_document.rb
  • api_examples/rest_api/post_convert_video.rb
  • api_examples/rest_api/post_files_local_copy.rb
  • api_examples/rest_api/post_files_remote_copy.rb
  • api_examples/rest_api/post_webhooks.rb
  • api_examples/rest_api/put_files_storage.rb
  • api_examples/rest_api/put_files_uuid_metadata_key.rb
  • api_examples/rest_api/put_files_uuid_storage.rb
  • api_examples/rest_api/put_webhooks_id.rb
  • api_examples/support/example_helper.rb
  • api_examples/support/run_rest_example.rb
  • api_examples/support/run_upload_example.rb
  • api_examples/upload_api/get_from_url_status.rb
  • api_examples/upload_api/get_group_info.rb
  • api_examples/upload_api/get_info.rb
  • api_examples/upload_api/post_base.rb
  • api_examples/upload_api/post_from_url.rb
  • api_examples/upload_api/post_group.rb
  • api_examples/upload_api/post_multipart_complete.rb
  • api_examples/upload_api/post_multipart_start.rb
  • api_examples/upload_api/put_multipart_part.rb
  • api_examples/upload_api/put_presigned_url_x.rb
  • bin/console
  • context7.json
  • docs/release-notes-5.0.0.md
  • examples/README.md
  • examples/batch_upload.rb
  • examples/group_creation.rb
  • examples/large_file_upload.rb
  • examples/simple_upload.rb
  • examples/upload_with_progress.rb
  • examples/url_upload.rb
  • lib/uploadcare.rb
  • lib/uploadcare/api/api.rb
  • lib/uploadcare/api/rest.rb
  • lib/uploadcare/api/rest/addons.rb
  • lib/uploadcare/api/rest/document_conversions.rb
  • lib/uploadcare/api/rest/file_metadata.rb
  • lib/uploadcare/api/rest/files.rb
  • lib/uploadcare/api/rest/groups.rb
  • lib/uploadcare/api/rest/project.rb
  • lib/uploadcare/api/rest/video_conversions.rb
  • lib/uploadcare/api/rest/webhooks.rb
  • lib/uploadcare/api/upload.rb
  • lib/uploadcare/api/upload/files.rb
  • lib/uploadcare/api/upload/groups.rb
  • lib/uploadcare/client.rb
  • lib/uploadcare/client/addons_accessor.rb
  • lib/uploadcare/client/addons_client.rb
  • lib/uploadcare/client/api.rb
  • lib/uploadcare/client/conversion/base_conversion_client.rb
  • lib/uploadcare/client/conversion/document_conversion_client.rb
  • lib/uploadcare/client/conversion/video_conversion_client.rb
  • lib/uploadcare/client/conversions_accessor.rb
  • lib/uploadcare/client/document_conversions_accessor.rb
  • lib/uploadcare/client/file_client.rb
  • lib/uploadcare/client/file_list_client.rb
  • lib/uploadcare/client/file_metadata_accessor.rb
  • lib/uploadcare/client/file_metadata_client.rb
  • lib/uploadcare/client/files_accessor.rb
  • lib/uploadcare/client/group_client.rb
  • lib/uploadcare/client/groups_accessor.rb
  • lib/uploadcare/client/multipart_upload/chunks_client.rb
  • lib/uploadcare/client/multipart_upload_client.rb
  • lib/uploadcare/client/project_accessor.rb
  • lib/uploadcare/client/project_client.rb
  • lib/uploadcare/client/rest_client.rb
  • lib/uploadcare/client/rest_group_client.rb
  • lib/uploadcare/client/upload_client.rb
  • lib/uploadcare/client/uploader_client.rb
  • lib/uploadcare/client/video_conversions_accessor.rb
  • lib/uploadcare/client/webhook_client.rb
  • lib/uploadcare/client/webhooks_accessor.rb
  • lib/uploadcare/cname_generator.rb
  • lib/uploadcare/collections/batch_result.rb
  • lib/uploadcare/collections/paginated.rb
  • lib/uploadcare/concern/error_handler.rb
  • lib/uploadcare/concern/throttle_handler.rb
  • lib/uploadcare/concern/upload_error_handler.rb
  • lib/uploadcare/configuration.rb
  • lib/uploadcare/entity/addons.rb
  • lib/uploadcare/entity/conversion/base_converter.rb
  • lib/uploadcare/entity/conversion/document_converter.rb
  • lib/uploadcare/entity/conversion/video_converter.rb
  • lib/uploadcare/entity/decorator/paginator.rb
  • lib/uploadcare/entity/entity.rb
  • lib/uploadcare/entity/file.rb
  • lib/uploadcare/entity/file_list.rb
  • lib/uploadcare/entity/file_metadata.rb
  • lib/uploadcare/entity/group.rb
  • lib/uploadcare/entity/group_list.rb
  • lib/uploadcare/entity/project.rb
  • lib/uploadcare/entity/uploader.rb
  • lib/uploadcare/entity/webhook.rb
  • lib/uploadcare/exception/auth_error.rb
  • lib/uploadcare/exception/configuration_error.rb
  • lib/uploadcare/exception/conversion_error.rb
  • lib/uploadcare/exception/invalid_request_error.rb
  • lib/uploadcare/exception/multipart_upload_error.rb
  • lib/uploadcare/exception/not_found_error.rb
  • lib/uploadcare/exception/request_error.rb
  • lib/uploadcare/exception/retry_error.rb
  • lib/uploadcare/exception/throttle_error.rb
  • lib/uploadcare/exception/unknown_status_error.rb
  • lib/uploadcare/exception/upload_error.rb
  • lib/uploadcare/exception/upload_timeout_error.rb
  • lib/uploadcare/internal/authenticator.rb
  • lib/uploadcare/internal/error_handler.rb
  • lib/uploadcare/internal/signature_generator.rb
  • lib/uploadcare/internal/throttle_handler.rb
  • lib/uploadcare/internal/upload_io.rb
  • lib/uploadcare/internal/upload_params_generator.rb
  • lib/uploadcare/internal/user_agent.rb
  • lib/uploadcare/operations/multipart_upload.rb
  • lib/uploadcare/operations/upload_router.rb
  • lib/uploadcare/param/authentication_header.rb
  • lib/uploadcare/param/conversion/document/processing_job_url_builder.rb
  • lib/uploadcare/param/conversion/video/processing_job_url_builder.rb
  • lib/uploadcare/param/param.rb
  • lib/uploadcare/param/secure_auth_header.rb
  • lib/uploadcare/param/simple_auth_header.rb
  • lib/uploadcare/param/upload/signature_generator.rb
  • lib/uploadcare/param/upload/upload_params_generator.rb
  • lib/uploadcare/param/user_agent.rb
  • lib/uploadcare/param/webhook_signature_verifier.rb
  • lib/uploadcare/resources/addon_execution.rb
  • lib/uploadcare/resources/base_resource.rb
  • lib/uploadcare/resources/document_conversion.rb
  • lib/uploadcare/resources/file.rb
  • lib/uploadcare/resources/file_metadata.rb
  • lib/uploadcare/resources/group.rb
  • lib/uploadcare/resources/project.rb
  • lib/uploadcare/resources/video_conversion.rb
  • lib/uploadcare/resources/webhook.rb
  • lib/uploadcare/result.rb
  • lib/uploadcare/ruby/version.rb
  • lib/uploadcare/signed_url_generators/akamai_generator.rb
  • lib/uploadcare/signed_url_generators/base_generator.rb
  • lib/uploadcare/version.rb
  • lib/uploadcare/webhook_signature_verifier.rb
  • mise.toml
  • spec/TESTING.md
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/Base_Upload_Store_Retrieve/uploads_stores_and_retrieves_file_information.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/Batch_Upload_Verify_All/uploads_multiple_files_and_verifies_all.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/Group_Creation_Info_Verify/creates_group_and_retrieves_information.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/URL_Upload_Poll_Complete/handles_async_URL_upload_with_status_checking.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/URL_Upload_Poll_Complete/uploads_from_URL_and_polls_until_complete.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/when_batch_uploading/uploads_multiple_files_and_verifies_all.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/when_creating_and_verifying_groups/creates_group_and_retrieves_information.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/when_uploading_from_URL/handles_async_URL_upload_with_status_checking.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/when_uploading_from_URL/uploads_from_URL_and_polls_until_complete.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Complete_Upload_Workflow/when_uploading_storing_and_retrieving/uploads_stores_and_retrieves_file_information.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Edge_Cases/Basic_file_uploads/handles_basic_file_uploads.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Edge_Cases/Concurrent_uploads/handles_multiple_simultaneous_uploads.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Edge_Cases/Files_with_special_characters/handles_filenames_with_special_characters.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Edge_Cases/Metadata/preserves_metadata_through_upload.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Edge_Cases/Very_small_files/handles_small_fixture_files.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Edge_Cases/when_metadata_is_provided/preserves_metadata_through_upload.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Edge_Cases/when_uploading_basic_files/handles_basic_file_uploads.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Edge_Cases/when_uploading_concurrently/handles_multiple_simultaneous_uploads.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Edge_Cases/when_uploading_very_small_files/handles_small_fixture_files.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Performance/Parallel_multipart_upload/parallel_upload_is_faster_than_sequential.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Performance/Upload_speed/uploads_files_in_reasonable_time.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Performance/when_measuring_upload_speed/uploads_files_in_reasonable_time.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Smart_Upload_Detection/detects_and_uses_correct_upload_method_for_URLs.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Smart_Upload_Detection/detects_and_uses_correct_upload_method_for_arrays.yml
  • spec/fixtures/vcr_cassettes/Upload_API_Integration/Smart_Upload_Detection/detects_and_uses_correct_upload_method_for_small_files.yml
  • spec/fixtures/vcr_cassettes/amazon_upload.yml
  • spec/fixtures/vcr_cassettes/document_convert_convert_many.yml
  • spec/fixtures/vcr_cassettes/document_convert_convert_many_with_error.yml
  • spec/fixtures/vcr_cassettes/document_convert_convert_multipage_group.yml
  • spec/fixtures/vcr_cassettes/document_convert_convert_multipage_zip.yml
  • spec/fixtures/vcr_cassettes/document_convert_file_info.yml
  • spec/fixtures/vcr_cassettes/document_convert_get_status.yml
  • spec/fixtures/vcr_cassettes/document_convert_info.yml
  • spec/fixtures/vcr_cassettes/document_convert_to_multipage.yml
  • spec/fixtures/vcr_cassettes/file_info.yml
  • spec/fixtures/vcr_cassettes/file_metadata_create.yml
  • spec/fixtures/vcr_cassettes/file_metadata_delete.yml
  • spec/fixtures/vcr_cassettes/file_metadata_index.yml
  • spec/fixtures/vcr_cassettes/file_metadata_index_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/file_metadata_show.yml
  • spec/fixtures/vcr_cassettes/file_metadata_show_nonexistent_key.yml
  • spec/fixtures/vcr_cassettes/file_metadata_update.yml
  • spec/fixtures/vcr_cassettes/group_delete_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/http_fail.yml
  • spec/fixtures/vcr_cassettes/project.yml
  • spec/fixtures/vcr_cassettes/remove_bg.yml
  • spec/fixtures/vcr_cassettes/remove_bg_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/remove_bg_status.yml
  • spec/fixtures/vcr_cassettes/remove_bg_status_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/rest_file_batch_delete.yml
  • spec/fixtures/vcr_cassettes/rest_file_batch_delete_fail.yml
  • spec/fixtures/vcr_cassettes/rest_file_batch_store.yml
  • spec/fixtures/vcr_cassettes/rest_file_batch_store_fail.yml
  • spec/fixtures/vcr_cassettes/rest_file_copy_dont_strip_operations.yml
  • spec/fixtures/vcr_cassettes/rest_file_copy_strip_operations.yml
  • spec/fixtures/vcr_cassettes/rest_file_delete.yml
  • spec/fixtures/vcr_cassettes/rest_file_delete_nonexistent.yml
  • spec/fixtures/vcr_cassettes/rest_file_external_copy.yml
  • spec/fixtures/vcr_cassettes/rest_file_info.yml
  • spec/fixtures/vcr_cassettes/rest_file_info_fail.yml
  • spec/fixtures/vcr_cassettes/rest_file_info_no_connection.yml
  • spec/fixtures/vcr_cassettes/rest_file_internal_copy.yml
  • spec/fixtures/vcr_cassettes/rest_file_list.yml
  • spec/fixtures/vcr_cassettes/rest_file_list_each.yml
  • spec/fixtures/vcr_cassettes/rest_file_list_limited.yml
  • spec/fixtures/vcr_cassettes/rest_file_list_load.yml
  • spec/fixtures/vcr_cassettes/rest_file_list_pages.yml
  • spec/fixtures/vcr_cassettes/rest_file_list_params.yml
  • spec/fixtures/vcr_cassettes/rest_file_list_previous_page.yml
  • spec/fixtures/vcr_cassettes/rest_file_remote_copy.yml
  • spec/fixtures/vcr_cassettes/rest_file_store.yml
  • spec/fixtures/vcr_cassettes/rest_info_group.yml
  • spec/fixtures/vcr_cassettes/rest_list_groups.yml
  • spec/fixtures/vcr_cassettes/rest_list_groups_limited.yml
  • spec/fixtures/vcr_cassettes/rest_store_group.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_create.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_destroy.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_destroy1.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_list.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_list_unpaid.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_update.yml
  • spec/fixtures/vcr_cassettes/throttling.yml
  • spec/fixtures/vcr_cassettes/uc_clamav_virus_scan.yml
  • spec/fixtures/vcr_cassettes/uc_clamav_virus_scan_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/uc_clamav_virus_scan_status.yml
  • spec/fixtures/vcr_cassettes/uc_clamav_virus_scan_status_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/upload_create_group.yml
  • spec/fixtures/vcr_cassettes/upload_create_group_from_files.yml
  • spec/fixtures/vcr_cassettes/upload_error.yml
  • spec/fixtures/vcr_cassettes/upload_file_info.yml
  • spec/fixtures/vcr_cassettes/upload_from_url_basic.yml
  • spec/fixtures/vcr_cassettes/upload_get_upload_from_url_status.yml
  • spec/fixtures/vcr_cassettes/upload_group_delete.yml
  • spec/fixtures/vcr_cassettes/upload_group_info.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_complete.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_complete_unfinished.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_complete_wrong_id.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_small.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_start.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_start_large.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_start_small.yml
  • spec/fixtures/vcr_cassettes/upload_throttling.yml
  • spec/fixtures/vcr_cassettes/upload_upload_files.yml
  • spec/fixtures/vcr_cassettes/upload_upload_from_url.yml
  • spec/fixtures/vcr_cassettes/upload_upload_from_url_async.yml
  • spec/fixtures/vcr_cassettes/upload_upload_from_url_progress.yml
  • spec/fixtures/vcr_cassettes/upload_upload_from_url_with_signature.yml
  • spec/fixtures/vcr_cassettes/upload_upload_invalid.yml
  • spec/fixtures/vcr_cassettes/upload_upload_many.yml
  • spec/fixtures/vcr_cassettes/upload_upload_one.yml
  • spec/fixtures/vcr_cassettes/upload_upload_one_without_secret_key.yml
  • spec/fixtures/vcr_cassettes/uploader_multipart_upload.yml
  • spec/fixtures/vcr_cassettes/video_convert_convert_many.yml
  • spec/fixtures/vcr_cassettes/video_convert_convert_many_with_error.yml
  • spec/fixtures/vcr_cassettes/video_convert_file_info.yml
  • spec/fixtures/vcr_cassettes/video_convert_get_status.yml
  • spec/fixtures/vcr_cassettes/ws_rekognition_detect_labels.yml
  • spec/fixtures/vcr_cassettes/ws_rekognition_detect_labels_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/ws_rekognition_detect_labels_status.yml
  • spec/fixtures/vcr_cassettes/ws_rekognition_detect_labels_status_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/ws_rekognition_detect_moderation_labels.yml
  • spec/fixtures/vcr_cassettes/ws_rekognition_detect_moderation_labels_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/ws_rekognition_detect_moderation_labels_status.yml
💤 Files with no reviewable changes (121)
  • spec/fixtures/vcr_cassettes/rest_file_delete_nonexistent.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_update.yml
  • spec/fixtures/vcr_cassettes/file_metadata_create.yml
  • spec/fixtures/vcr_cassettes/rest_file_delete.yml
  • spec/fixtures/vcr_cassettes/upload_create_group_from_files.yml
  • lib/uploadcare/param/upload/signature_generator.rb
  • spec/fixtures/vcr_cassettes/file_info.yml
  • lib/uploadcare/client/conversion/base_conversion_client.rb
  • api_examples/upload_api/put_presigned_url_x.rb
  • spec/fixtures/vcr_cassettes/document_convert_get_status.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_list.yml
  • spec/fixtures/vcr_cassettes/uc_clamav_virus_scan_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/document_convert_file_info.yml
  • spec/fixtures/vcr_cassettes/file_metadata_index.yml
  • spec/fixtures/vcr_cassettes/uc_clamav_virus_scan.yml
  • spec/fixtures/vcr_cassettes/rest_file_info_no_connection.yml
  • spec/fixtures/vcr_cassettes/group_delete_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/document_convert_to_multipage.yml
  • spec/fixtures/vcr_cassettes/file_metadata_show.yml
  • spec/fixtures/vcr_cassettes/file_metadata_update.yml
  • spec/fixtures/vcr_cassettes/upload_upload_from_url_async.yml
  • spec/fixtures/vcr_cassettes/rest_file_info.yml
  • lib/uploadcare/entity/conversion/base_converter.rb
  • spec/fixtures/vcr_cassettes/upload_group_info.yml
  • spec/fixtures/vcr_cassettes/http_fail.yml
  • spec/fixtures/vcr_cassettes/rest_file_internal_copy.yml
  • spec/fixtures/vcr_cassettes/rest_info_group.yml
  • spec/fixtures/vcr_cassettes/document_convert_convert_multipage_zip.yml
  • spec/fixtures/vcr_cassettes/rest_file_copy_strip_operations.yml
  • spec/fixtures/vcr_cassettes/document_convert_info.yml
  • lib/uploadcare/param/user_agent.rb
  • lib/uploadcare/client/conversion/video_conversion_client.rb
  • spec/fixtures/vcr_cassettes/upload_create_group.yml
  • lib/uploadcare/api/api.rb
  • spec/fixtures/vcr_cassettes/uc_clamav_virus_scan_status_nonexistent_uuid.yml
  • lib/uploadcare/client/multipart_upload_client.rb
  • spec/fixtures/vcr_cassettes/rest_webhook_destroy.yml
  • lib/uploadcare/client/upload_client.rb
  • spec/fixtures/vcr_cassettes/throttling.yml
  • lib/uploadcare/param/authentication_header.rb
  • lib/uploadcare/concern/throttle_handler.rb
  • spec/fixtures/vcr_cassettes/rest_store_group.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_complete.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_complete_wrong_id.yml
  • lib/uploadcare/param/conversion/video/processing_job_url_builder.rb
  • lib/uploadcare/client/file_client.rb
  • spec/fixtures/vcr_cassettes/upload_error.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_destroy1.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_small.yml
  • spec/fixtures/vcr_cassettes/remove_bg_status.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_list_unpaid.yml
  • spec/fixtures/vcr_cassettes/file_metadata_delete.yml
  • spec/fixtures/vcr_cassettes/file_metadata_show_nonexistent_key.yml
  • lib/uploadcare/concern/error_handler.rb
  • spec/fixtures/vcr_cassettes/rest_file_external_copy.yml
  • spec/fixtures/vcr_cassettes/rest_file_info_fail.yml
  • spec/fixtures/vcr_cassettes/uc_clamav_virus_scan_status.yml
  • spec/fixtures/vcr_cassettes/rest_file_batch_delete.yml
  • lib/uploadcare/entity/project.rb
  • lib/uploadcare/entity/group_list.rb
  • spec/fixtures/vcr_cassettes/rest_file_list_pages.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_start.yml
  • lib/uploadcare/entity/file_list.rb
  • spec/fixtures/vcr_cassettes/remove_bg.yml
  • lib/uploadcare/client/uploader_client.rb
  • lib/uploadcare/client/conversion/document_conversion_client.rb
  • lib/uploadcare/param/webhook_signature_verifier.rb
  • spec/fixtures/vcr_cassettes/rest_file_list_params.yml
  • lib/uploadcare/entity/decorator/paginator.rb
  • lib/uploadcare/client/rest_client.rb
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_start_small.yml
  • spec/fixtures/vcr_cassettes/rest_file_remote_copy.yml
  • spec/fixtures/vcr_cassettes/rest_file_batch_delete_fail.yml
  • spec/fixtures/vcr_cassettes/rest_file_batch_store_fail.yml
  • spec/fixtures/vcr_cassettes/rest_list_groups_limited.yml
  • lib/uploadcare/client/group_client.rb
  • lib/uploadcare/entity/file.rb
  • lib/uploadcare/client/rest_group_client.rb
  • lib/uploadcare/entity/webhook.rb
  • spec/fixtures/vcr_cassettes/rest_file_copy_dont_strip_operations.yml
  • lib/uploadcare/param/upload/upload_params_generator.rb
  • lib/uploadcare/client/project_client.rb
  • spec/fixtures/vcr_cassettes/amazon_upload.yml
  • spec/fixtures/vcr_cassettes/upload_throttling.yml
  • spec/fixtures/vcr_cassettes/project.yml
  • spec/fixtures/vcr_cassettes/file_metadata_index_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/document_convert_convert_multipage_group.yml
  • spec/fixtures/vcr_cassettes/rest_webhook_create.yml
  • spec/fixtures/vcr_cassettes/remove_bg_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_complete_unfinished.yml
  • spec/fixtures/vcr_cassettes/rest_list_groups.yml
  • lib/uploadcare/entity/entity.rb
  • spec/fixtures/vcr_cassettes/upload_file_info.yml
  • spec/fixtures/vcr_cassettes/upload_multipart_upload_start_large.yml
  • spec/fixtures/vcr_cassettes/rest_file_list_each.yml
  • spec/fixtures/vcr_cassettes/upload_group_delete.yml
  • lib/uploadcare/entity/addons.rb
  • lib/uploadcare/concern/upload_error_handler.rb
  • lib/uploadcare/param/simple_auth_header.rb
  • lib/uploadcare/entity/group.rb
  • lib/uploadcare/param/conversion/document/processing_job_url_builder.rb
  • lib/uploadcare/client/webhook_client.rb
  • lib/uploadcare/client/file_list_client.rb
  • lib/uploadcare/client/file_metadata_client.rb
  • lib/uploadcare/client/addons_client.rb
  • lib/uploadcare/ruby/version.rb
  • spec/fixtures/vcr_cassettes/rest_file_store.yml
  • spec/fixtures/vcr_cassettes/document_convert_convert_many.yml
  • spec/fixtures/vcr_cassettes/rest_file_batch_store.yml
  • spec/fixtures/vcr_cassettes/remove_bg_status_nonexistent_uuid.yml
  • spec/fixtures/vcr_cassettes/rest_file_list.yml
  • spec/fixtures/vcr_cassettes/rest_file_list_previous_page.yml
  • spec/fixtures/vcr_cassettes/rest_file_list_limited.yml
  • lib/uploadcare/entity/conversion/video_converter.rb
  • spec/fixtures/vcr_cassettes/document_convert_convert_many_with_error.yml
  • lib/uploadcare/param/secure_auth_header.rb
  • lib/uploadcare/param/param.rb
  • lib/uploadcare/client/multipart_upload/chunks_client.rb
  • lib/uploadcare/entity/conversion/document_converter.rb
  • lib/uploadcare/entity/file_metadata.rb
  • lib/uploadcare/entity/uploader.rb

Comment thread examples/batch_upload.rb
Comment on lines +15 to +27
file_paths.each do |path|
unless File.exist?(path)
puts "Error: File not found: #{path}"
exit 1
end
end

puts "Batch Upload - #{file_paths.length} files"
puts '=' * 50
puts

files = file_paths.map { |path| File.open(path, 'rb') }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n examples/batch_upload.rb

Repository: uploadcare/uploadcare-ruby

Length of output: 1623


Move file opening inside the protected block and strengthen file validation.

Line 16 only checks if a path exists; a directory or unreadable file would pass this check but fail when File.open is called at line 26. Additionally, since files are opened before the begin block (line 28), any exception during opening—including from inaccessible files—will bypass the ensure block and leak file handles.

Replace File.exist? with File.file? and File.readable? to validate file type and access permissions upfront, and move the file opening into the begin block. Harden the ensure clause to safely close files even if opening was incomplete.

Suggested fix
 file_paths.each do |path|
-  unless File.exist?(path)
-    puts "Error: File not found: #{path}"
+  unless File.file?(path) && File.readable?(path)
+    puts "Error: Not a readable file: #{path}"
     exit 1
   end
 end
@@
-files = file_paths.map { |path| File.open(path, 'rb') }
-
 begin
+  files = file_paths.map { |path| File.open(path, 'rb') }
   client = Uploadcare::Client.new(
@@
 ensure
-  files.each(&:close)
+  files&.each { |f| f.close unless f.closed? }
 end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/batch_upload.rb` around lines 15 - 27, Validate each path using
File.file? and File.readable? instead of File.exist? (check the file_paths loop)
and defer opening files until inside the protected begin block; move the
File.open calls into the begin where the ensure block can safely close any
successfully opened file handles (reference file_paths, File.file?,
File.readable?, File.open, and the ensure clause). Also make the ensure clause
robust by iterating over the files collection and closing only non-nil, open
handles to avoid errors if some opens failed.

) do |progress|
uploaded_mb = (progress[:uploaded] / 1024.0 / 1024.0).round(2)
total_mb = (progress[:total] / 1024.0 / 1024.0).round(2)
percentage = ((progress[:uploaded].to_f / progress[:total]) * 100).round
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against division by zero in progress calculation.

If progress[:total] is 0, the percentage calculation will raise a ZeroDivisionError.

🛡️ Proposed guard
     uploaded_mb = (progress[:uploaded] / 1024.0 / 1024.0).round(2)
     total_mb = (progress[:total] / 1024.0 / 1024.0).round(2)
-    percentage = ((progress[:uploaded].to_f / progress[:total]) * 100).round
+    percentage = progress[:total].positive? ? ((progress[:uploaded].to_f / progress[:total]) * 100).round : 0
     part = progress[:part]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/large_file_upload.rb` at line 55, The percentage calculation can
divide by zero when progress[:total] is 0; update the logic around the
assignment to percentage so it guards against zero or nil totals (e.g., check
progress[:total].to_i > 0) and return 0 (or an appropriate default) when total
is zero, otherwise perform the existing ((progress[:uploaded].to_f /
progress[:total]) * 100).round computation; locate the assignment to percentage
in examples/large_file_upload.rb and modify that expression to include the guard
using progress[:total] and progress[:uploaded] symbols.

Comment thread examples/simple_upload.rb
Comment on lines +9 to +12
unless file_path && File.exist?(file_path)
puts 'Usage: ruby simple_upload.rb <file_path>'
puts 'Example: ruby simple_upload.rb photo.jpg'
exit 1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Tighten CLI input validation and usage path.

Line 9 accepts directories (File.exist?), which can fail later when treated as a file. Also, the usage string should point to the script path users run from repo root.

Suggested fix
-unless file_path && File.exist?(file_path)
-  puts 'Usage: ruby simple_upload.rb <file_path>'
-  puts 'Example: ruby simple_upload.rb photo.jpg'
+unless file_path && File.file?(file_path)
+  puts 'Usage: ruby examples/simple_upload.rb <file_path>'
+  puts 'Example: ruby examples/simple_upload.rb photo.jpg'
   exit 1
 end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
unless file_path && File.exist?(file_path)
puts 'Usage: ruby simple_upload.rb <file_path>'
puts 'Example: ruby simple_upload.rb photo.jpg'
exit 1
unless file_path && File.file?(file_path)
puts 'Usage: ruby examples/simple_upload.rb <file_path>'
puts 'Example: ruby examples/simple_upload.rb photo.jpg'
exit 1
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/simple_upload.rb` around lines 9 - 12, The current check uses
File.exist?(file_path) which permits directories and will break when the code
later treats the path as a file; change the validation around file_path to
ensure it's a regular readable file (e.g., use File.file? and/or File.readable?)
and exit if not. Also update the usage and example strings ('Usage: ruby
simple_upload.rb <file_path>' and 'Example: ruby simple_upload.rb photo.jpg') to
point to the repository script path (e.g., examples/simple_upload.rb) so users
run the correct script from the repo root. Ensure these changes are applied
where file_path is validated and where the usage/example strings are defined.

result = client.uploads.upload(file, store: true) do |progress|
uploaded_mb = (progress[:uploaded] / 1024.0 / 1024.0).round(2)
total_mb = (progress[:total] / 1024.0 / 1024.0).round(2)
percentage = ((progress[:uploaded].to_f / progress[:total]) * 100).round
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against division by zero in progress percentage.

If progress[:total] is 0, the calculation will raise a ZeroDivisionError.

🛡️ Proposed guard
     uploaded_mb = (progress[:uploaded] / 1024.0 / 1024.0).round(2)
     total_mb = (progress[:total] / 1024.0 / 1024.0).round(2)
-    percentage = ((progress[:uploaded].to_f / progress[:total]) * 100).round
+    percentage = progress[:total].positive? ? ((progress[:uploaded].to_f / progress[:total]) * 100).round : 0
     part = progress[:part]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
percentage = ((progress[:uploaded].to_f / progress[:total]) * 100).round
uploaded_mb = (progress[:uploaded] / 1024.0 / 1024.0).round(2)
total_mb = (progress[:total] / 1024.0 / 1024.0).round(2)
percentage = progress[:total].positive? ? ((progress[:uploaded].to_f / progress[:total]) * 100).round : 0
part = progress[:part]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/upload_with_progress.rb` at line 45, The percentage calculation
using percentage = ((progress[:uploaded].to_f / progress[:total]) * 100).round
must guard against division by zero; update the logic around the percentage
assignment to check progress[:total] (or progress[:total].to_f) for zero or nil
and return a safe default (e.g., 0) when total is zero, otherwise perform the
current division and rounding; reference the percentage variable and the
progress[:total] operand to locate and modify the expression.

total_parts = progress[:total_parts]

elapsed = Time.now - start_time
speed_mbps = uploaded_mb / elapsed
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against division by zero in speed calculation.

If the progress callback is invoked immediately after start (elapsed ≈ 0), dividing by elapsed will produce Infinity or raise an error.

🛡️ Proposed guard
     elapsed = Time.now - start_time
-    speed_mbps = uploaded_mb / elapsed
+    speed_mbps = elapsed.positive? ? uploaded_mb / elapsed : 0.0
     remaining_mb = total_mb - uploaded_mb
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
speed_mbps = uploaded_mb / elapsed
elapsed = Time.now - start_time
speed_mbps = elapsed.positive? ? uploaded_mb / elapsed : 0.0
remaining_mb = total_mb - uploaded_mb
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/upload_with_progress.rb` at line 50, The speed calculation can
divide by zero when elapsed is 0; update the progress callback where speed_mbps
= uploaded_mb / elapsed to guard against zero or near-zero elapsed (e.g., if
elapsed <= 0 or elapsed < a small epsilon) and set speed_mbps to 0 (or skip
calculation) in that case; ensure you only perform uploaded_mb / elapsed when
elapsed is valid so speed_mbps, uploaded_mb and elapsed remain stable and do not
produce Infinity or errors.

Comment on lines +268 to +285
# Convert this file to a document format.
#
# @param params [Hash] Conversion parameters (:format, etc.)
# @param options [Hash] Optional parameters (:store, etc.)
# @param request_options [Hash] Request options
# @return [Uploadcare::Resources::File] The converted file
def convert_to_document(params: {}, options: {}, request_options: {})
convert_file(params, Uploadcare::Resources::DocumentConversion, options, request_options: request_options)
end

# Convert this file to a video format.
#
# @param params [Hash] Conversion parameters (:format, :quality, etc.)
# @param options [Hash] Optional parameters (:store, etc.)
# @param request_options [Hash] Request options
# @return [Uploadcare::Resources::File] The converted file
def convert_to_video(params: {}, options: {}, request_options: {})
convert_file(params, Uploadcare::Resources::VideoConversion, options, request_options: request_options)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Widen the documented return type for conversion helpers.

These methods do not always return Uploadcare::Resources::File: process_convert_result can fall back to the raw conversion result when no UUID is available yet. The current YARD contract is therefore misleading for async/pending conversions.

📝 Suggested doc fix
-  # `@return` [Uploadcare::Resources::File] The converted file
+  # `@return` [Uploadcare::Resources::File, Object] The converted file, or the raw conversion result when no UUID is available yet
   def convert_to_document(params: {}, options: {}, request_options: {})
     convert_file(params, Uploadcare::Resources::DocumentConversion, options, request_options: request_options)
   end
@@
-  # `@return` [Uploadcare::Resources::File] The converted file
+  # `@return` [Uploadcare::Resources::File, Object] The converted file, or the raw conversion result when no UUID is available yet
   def convert_to_video(params: {}, options: {}, request_options: {})
     convert_file(params, Uploadcare::Resources::VideoConversion, options, request_options: request_options)
   end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Convert this file to a document format.
#
# @param params [Hash] Conversion parameters (:format, etc.)
# @param options [Hash] Optional parameters (:store, etc.)
# @param request_options [Hash] Request options
# @return [Uploadcare::Resources::File] The converted file
def convert_to_document(params: {}, options: {}, request_options: {})
convert_file(params, Uploadcare::Resources::DocumentConversion, options, request_options: request_options)
end
# Convert this file to a video format.
#
# @param params [Hash] Conversion parameters (:format, :quality, etc.)
# @param options [Hash] Optional parameters (:store, etc.)
# @param request_options [Hash] Request options
# @return [Uploadcare::Resources::File] The converted file
def convert_to_video(params: {}, options: {}, request_options: {})
convert_file(params, Uploadcare::Resources::VideoConversion, options, request_options: request_options)
# Convert this file to a document format.
#
# `@param` params [Hash] Conversion parameters (:format, etc.)
# `@param` options [Hash] Optional parameters (:store, etc.)
# `@param` request_options [Hash] Request options
# `@return` [Uploadcare::Resources::File, Object] The converted file, or the raw conversion result when no UUID is available yet
def convert_to_document(params: {}, options: {}, request_options: {})
convert_file(params, Uploadcare::Resources::DocumentConversion, options, request_options: request_options)
end
# Convert this file to a video format.
#
# `@param` params [Hash] Conversion parameters (:format, :quality, etc.)
# `@param` options [Hash] Optional parameters (:store, etc.)
# `@param` request_options [Hash] Request options
# `@return` [Uploadcare::Resources::File, Object] The converted file, or the raw conversion result when no UUID is available yet
def convert_to_video(params: {}, options: {}, request_options: {})
convert_file(params, Uploadcare::Resources::VideoConversion, options, request_options: request_options)
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/uploadcare/resources/file.rb` around lines 268 - 285, Update the YARD
`@return` for the conversion helper methods (e.g., convert_to_document and
convert_to_video) to reflect that they may return either an
Uploadcare::Resources::File or the raw conversion result when no UUID is
available yet; specifically change the documented return type from
"Uploadcare::Resources::File" to something like "Uploadcare::Resources::File,
Hash" (or the appropriate raw result type) so it matches
process_convert_result's fallback behavior.

Comment on lines +328 to +337
result = if converter.respond_to?(:convert_document)
converter.convert_document(
params: params_with_symbolized_keys, options: options, config: config,
request_options: request_options
)
elsif converter.respond_to?(:convert)
converter.convert(
params: params_with_symbolized_keys, options: options, config: config,
request_options: request_options
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 'def self\.(convert_document|convert)\b|client:|resolve_client' \
  lib/uploadcare/resources/document_conversion.rb \
  lib/uploadcare/resources/video_conversion.rb

Repository: uploadcare/uploadcare-ruby

Length of output: 4923


🏁 Script executed:

#!/bin/bash
sed -n '320,365p' lib/uploadcare/resources/file.rb | cat -n

Repository: uploadcare/uploadcare-ruby

Length of output: 2112


🏁 Script executed:

#!/bin/bash
rg -n 'class File|def client|`@client`|attr_accessor.*client|attr_reader.*client' lib/uploadcare/resources/file.rb | head -20

Repository: uploadcare/uploadcare-ruby

Length of output: 52


🏁 Script executed:

#!/bin/bash
head -30 lib/uploadcare/resources/file.rb | grep -n "class File"

Repository: uploadcare/uploadcare-ruby

Length of output: 52


🏁 Script executed:

#!/bin/bash
rg -n '^class File' lib/uploadcare/resources/file.rb

Repository: uploadcare/uploadcare-ruby

Length of output: 52


🏁 Script executed:

#!/bin/bash
rg -n 'class.*File' lib/uploadcare/resources/file.rb | head -5

Repository: uploadcare/uploadcare-ruby

Length of output: 145


🏁 Script executed:

#!/bin/bash
rg -n 'def client|`@client`|attr_accessor.*client|attr_reader.*client' lib/uploadcare/resources/base_resource.rb | head -20

Repository: uploadcare/uploadcare-ruby

Length of output: 188


Pass the client to conversion dispatchers for consistency.

The convert_document and convert methods accept a client: parameter, but lines 330-331 and 335-336 omit it. This causes conversion requests to default to the global client instead of using the same client that invoked the resource. Line 361's find call already preserves client: client, creating an inconsistency where conversion and subsequent fetch use different clients.

🔧 Suggested fix
    result = if converter.respond_to?(:convert_document)
               converter.convert_document(
-                 params: params_with_symbolized_keys, options: options, config: config,
+                 params: params_with_symbolized_keys, options: options, client: client, config: config,
                  request_options: request_options
                )
              elsif converter.respond_to?(:convert)
                converter.convert(
-                 params: params_with_symbolized_keys, options: options, config: config,
+                 params: params_with_symbolized_keys, options: options, client: client, config: config,
                  request_options: request_options
                )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/uploadcare/resources/file.rb` around lines 328 - 337, The conversion
dispatch is omitting the client argument so converter.convert_document and
converter.convert fall back to the global client; update both calls to pass
client: client (alongside params: params_with_symbolized_keys, options: options,
config: config, request_options: request_options) so the converter uses the same
client instance as the resource (matching how find preserves client: client).

Comment on lines +20 to +21
code: 400
message: Bad Request
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Performance fixture captures a limit-error path instead of the benchmark path.

Line 20 records 400 Bad Request, and Line 57 shows File size exceeds project limit. For parallel_upload_is_faster_than_sequential, this can invalidate or mislead the performance assertion. Please re-record this cassette with an allowed file size/project limit (or rename/re-scope the test if this is intentionally a limit-failure scenario).

Also applies to: 57-57

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@spec/fixtures/vcr_cassettes/Upload_API_Integration/Performance/Parallel_multipart_upload/parallel_upload_is_faster_than_sequential.yml`
around lines 20 - 21, The VCR cassette for the test
parallel_upload_is_faster_than_sequential recorded a 400 limit error ("File size
exceeds project limit") instead of the intended successful benchmark; re-record
the cassette used by the parallel_upload_is_faster_than_sequential test with a
payload that is within the project's allowed file size (or adjust the test to
intentionally assert a limit-failure and rename/scope it accordingly), so the
YAML fixture no longer contains the 400/limit-error response and reflects the
true performance path.

Comment thread spec/TESTING.md
- Ensures tests match actual API behavior
- Good for regression testing

### Use RSpec Mocks (`allow_any_instance_of`) for:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid promoting allow_any_instance_of in the section title.

Line 67 names allow_any_instance_of, but the example and preferred pattern use receiver-scoped stubs. Rename the heading to avoid encouraging brittle mocks.

Suggested doc tweak
-### Use RSpec Mocks (`allow_any_instance_of`) for:
+### Use RSpec Mocks (`allow(...).to receive`) for:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Use RSpec Mocks (`allow_any_instance_of`) for:
### Use RSpec Mocks (`allow(...).to receive`) for:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/TESTING.md` at line 67, Rename the heading that currently mentions
"allow_any_instance_of" to avoid promoting that API; update the section title to
something like "Use RSpec Mocks (receiver-scoped stubs)" or "Use RSpec Mocks
(avoid allow_any_instance_of)" so the heading aligns with the example and
preferred pattern, and ensure any internal references to the old heading text
"allow_any_instance_of" are adjusted accordingly.

Comment thread spec/TESTING.md

## Directory Structure

```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language identifier to the fenced block.

Line 84 uses an unlabeled fenced code block, which triggers markdownlint MD040.

Suggested fix
-```
+```text
 spec/
 ├── fixtures/
 │   └── vcr_cassettes/           # VCR recorded cassettes
@@
-```
+```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 84-84: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/TESTING.md` at line 84, The fenced code block containing the directory
tree (the triple-backtick block showing "spec/ ├── fixtures/ … vcr_cassettes")
is unlabeled and triggers markdownlint MD040; add a language identifier (e.g.,
"text") immediately after the opening backticks so the block becomes ```text and
the linter stops flagging it.

@alxgsv alxgsv self-requested a review May 18, 2026 07:21
@vipulnsward vipulnsward merged commit e76dd6a into main May 18, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants