v5.7.2 - Range & Not-Between Queries, Presigned Embeddings, Bug Fixes - #39
Conversation
The `.between` constraint now accepts Ruby Range objects in addition to 2-element arrays, providing a more idiomatic API for range queries. Inclusive ranges (`..`) map to `$lte` for the upper bound, while exclusive ranges (`...`) map to `$lt`. Beginless (`..end`) and endless (`begin..`) ranges are fully supported. All changes are backwards compatible with existing array-based queries. Includes comprehensive unit and integration tests, and snapshot tests for query compilation.
Fix `embed_image` to forward the file's presigned URL to embedding providers and downloaders instead of the bare canonical URL. This resolves 403 errors when using private-bucket file adapters (S3/GCS with `presignedUrl: true`), where the canonical `file.url` lacks the required signature. Falls back to the bare URL when no valid presigned URL is present. The stored digest remains keyed on the canonical URL to avoid re-embedding on signature rotation.
Two fixes for Query#get and _safe_warn: 1. Query#get now resolves aliased parse_class names correctly by passing the table name as a String to Parse::Object.build (which handles parse_class aliasing) instead of pre-resolving to a Class via Object.const_get, which only worked for exact constant name matches. 2. Parse::Client._safe_warn now routes warnings through Parse::Middleware::Logging.logger when configured, ensuring warnings appear in the app's configured logger (e.g., Rails.logger) instead of always going to STDERR. Falls back to STDERR when no logger is configured, preserving prior behavior.
There was a problem hiding this comment.
Pull request overview
Releases v5.7.2 with several SDK correctness fixes and feature enhancements across query constraints, embedding, logging, and request option documentation, backed by new unit/integration/snapshot tests.
Changes:
- Add Ruby
Rangesupport to thebetweenquery constraint (inclusive/exclusive, beginless/endless), plus tests and snapshots. - Fix
embed_imagerecompute to forward a valid presigned URL to providers/downloads while keeping digests stable on canonical URLs. - Fix
Query#getto resolveparse_classaliases correctly by delegating class resolution toParse::Object.build; route_safe_warnthrough the configured Parse logger when present.
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/snapshots/query_compile/between_range_inclusive.json | Adds snapshot for inclusive Range compilation ($gte/$lte). |
| test/snapshots/query_compile/between_range_exclusive.json | Adds snapshot for exclusive Range compilation ($gte/$lt). |
| test/lib/parse/query/constraints/between_test.rb | Adds unit tests for BetweenConstraint with arrays/ranges and invalid inputs. |
| test/lib/parse/query_get_test.rb | Adds unit tests ensuring Query#get honors parse_class aliasing and unregistered tables. |
| test/lib/parse/query_compile_snapshot_test.rb | Adds snapshot assertions for between with inclusive/exclusive Range. |
| test/lib/parse/embed_managed_image_test.rb | Adds tests for presigned URL forwarding and digest stability across signature rotation. |
| test/lib/parse/client/safe_warn_test.rb | Adds tests for logger routing behavior in _safe_warn. |
| test/lib/parse/between_constraint_integration_test.rb | Adds docker integration tests for between using Range variants. |
| lib/parse/stack/version.rb | Bumps gem version to 5.7.2. |
| lib/parse/query/constraints.rb | Implements between support for Ruby Range values and documents behavior. |
| lib/parse/query.rb | Fixes Query#get to pass table name through to Parse::Object.build for alias-aware resolution. |
| lib/parse/model/core/embed_managed.rb | For image embeddings, prefers a currently-valid presigned URL for fetch/forward, while digest remains canonical. |
| lib/parse/client/request.rb | Documents recognized per-request opts keys and clarifies cache accessor usage. |
| lib/parse/client.rb | Routes _safe_warn to the configured Parse logger when present; stderr remains fallback. |
| Gemfile.lock | Updates local gem version entry to 5.7.2. |
| CHANGELOG.md | Adds 5.7.2 release notes for the above changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Remove unused local variable assignments from library code. Add CodeQL suppressions in test code where assignments are intentional test setup but unused in assertions. Also removes unused exception variables from rescue clauses where the exception is not referenced.
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 67 out of 68 changed files in this pull request and generated no new comments.
Suppressed comments (4)
test/test_helper.rb:60
refute_raisescurrently discards the optional message argument and has an unusedexpreassignment. Instead of suppressing CodeQL, use the message in the failure output and remove the dead assignment so callers get the intended context in the flunk output.
test/lib/parse/query/group_by_aggregation_test.rb:37expected_pipelineis defined but never used in this test (the expectation only checkspipeline.any?). That makes the test less strict than intended and required a CodeQL suppression. Either removeexpected_pipelineor (preferably) assert the pipeline equals it, as done in the count test above.
test/lib/parse/query/constraints/nullability_test.rb:34- Inside the
assert_raisesblock,expected = build(value).as_jsonis unused and only exists to satisfy a CodeQL suppression. Removing it keeps the test focused on the behavior under test (thatconstraint.buildraises).
test/lib/parse/models/count_distinct_model_test.rb:76 - This mock client defines
aggregate_pipelinetwice; the first definition attempts to close over a local (response_data) viadef response.result, which won't work in Ruby and is immediately overwritten anyway. Removing the dead first definition avoids confusion and eliminates the need for the CodeQL suppression.
NEW: Query#where_not_between(field, value) adds the logical negation of between constraints, supporting Range and Array forms with proper handling of beginless/endless ranges. Raises ArgumentError if query already has an $or group to prevent silent collision. FIXED: embed_image presigned URL handling now uses zero safety buffer (not the default 60s) for immediate fetches, preventing deterministic 403s in the last minute of valid signatures on private-bucket adapters. Strips query strings from stored URLs to avoid leaking bearer credentials in logs/error output. FIXED: Parse::Client._safe_warn now falls back to STDERR if a configured logger itself raises, preventing logger errors from masking the real Parse error being reported. CHANGED: Test suite cleaned up to remove unused variable assignments flagged by CodeQL.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 76 out of 77 changed files in this pull request and generated no new comments.
Suppressed comments (2)
test/test_helper.rb:60
- This line relies on the side-effect of
exp.pop, but the interpolated string is unused and reads like a no-op (and can trip linting for a useless expression). Prefer popping the message explicitly to make intent clear.
test/lib/parse/models/count_distinct_model_test.rb:71 create_mock_client_with_responsedefinesaggregate_pipelinetwice; the first definition is immediately overwritten and contains adef response.resultthat referencesresponse_data(not in scope for adef). Removing the dead first definition avoids confusion and prevents accidentally reviving a broken method later.
Summary
Rangesupport to thebetweenquery constraint (Person.where(:age.between => 5..25)), including exclusive (...), beginless (..25), and endless (5..) ranges. The existing 2-element array form is unchanged.Query#where_not_between(field, value), the logical negation ofbetween(age < 5 OR age > 25), supporting the same Range and Array forms. Implemented as a query method rather than afield.not_between => valuesymbol constraint, since a range's negation is inherently an$orof two comparisons and this SDK can only safely merge one$orgroup per compiled query; the method composes correctly alongside other.whereconditions and raisesArgumentErrorinstead of silently corrupting a query that already has an$orgroup.embed_imageso it forwards aParse::File's presigned URL to embedding providers (and to the SDK's own bytes-mode downloader) when the source file has one, instead of always sending the bare, unsigned canonical URL. Private-bucket file adapters (S3/GCS with presigned URLs enabled) were returning 403s before this fix. The digest used to decide whether to re-embed stays keyed on the bare canonical URL, and the validity check no longer applies an artificial safety buffer that would 403 in the last minute of a valid signature.Query#get, which resolved the target model class with a rawObject.const_getlookup and silently missed any model that renames its table viaparse_class. It now resolves throughParse::Model.find_class, matching the rest of the codebase.Parse::Client._safe_warnthrough an app's configured logger (Parse.logger = ...) instead of always writing to STDERR, falling back to STDERR both when no logger is configured and when a configured logger itself raises.Test plan
bundle exec rake test:unitpasses.betweenandwhere_not_between(inclusive, exclusive, beginless, endless, nested-with-AND, and error cases), aliasedparse_classresolution inQuery#get, logger routing plus the STDERR fallback (including a raising logger) in_safe_warn, and presigned-URL forwarding/expiry/log-safety inembed_image.