Replace URI.encode, removed in Ruby 3.0, with Fog::OpenStack.escape - #554
Open
tas50 wants to merge 1 commit into
Open
Replace URI.encode, removed in Ruby 3.0, with Fog::OpenStack.escape#554tas50 wants to merge 1 commit into
tas50 wants to merge 1 commit into
Conversation
URI.encode (an alias for URI.escape) was removed in Ruby 3.0, so every
one of these call sites raises NoMethodError on Ruby 3 and later. The
library still loads; it fails when the request is actually made.
This gem already has the right helper for the job. Fog::OpenStack.escape
is documented as 'CGI.escape, but without special treatment on spaces'
and is what the storage requests have always used. The thirteen call
sites here are simply the ones that never got migrated to it.
CGI.escape is not a suitable replacement: it encodes a space as '+',
which is only correct in a query string. In a URL path a '+' is a
literal plus, so any resource name containing a space would be
corrupted.
Two call sites need care:
- delete_multiple_objects escapes the joined 'container/object'
string, so the separator has to survive. It passes '/' as an
extra excluded character, matching how Storage::Files#get_url
already does this.
- The compute service requests escaped their optional params by
assigning back into the hash while iterating it, which mutated the
caller's hash. transform_values escapes the values without that
side effect.
Also declares a development dependency on base64. It became a bundled
gem in Ruby 3.4, and webmock requires it without depending on it, so
without this neither the spec nor the unit suite can even load on Ruby
3.4 or later.
Adds spec/escaping_spec.rb covering the escaping behaviour of each
group of call sites, the space and separator cases that distinguish
Fog::OpenStack.escape from CGI.escape, and a guard that URI.encode does
not reappear anywhere in lib.
Signed-off-by: Tim Smith <tsmith84@proton.me>
houndci-bot
reviewed
Aug 24, 2026
| captured | ||
| end | ||
|
|
||
| describe 'URL escaping' do |
There was a problem hiding this comment.
Metrics/BlockLength: Block has too many lines. [65/25]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
URI.encode(an alias forURI.escape) was removed in Ruby 3.0, so all thirteen call sites below raiseNoMethodErroron Ruby 3 and later. The library still loads fine — I confirmedrequire "fog/openstack"works on Ruby 4.0.6 — it fails only when the request is actually made:Why
Fog::OpenStack.escaperather thanCGI.escapeThis gem already ships the right helper.
Fog::OpenStack.escapeis documented as "CGI.escape, but without special treatment on spaces" and is what roughly twenty storage call sites already use. The thirteen here are simply the ones that never got migrated to it.CGI.escapeis not a safe replacement, because it encodes a space as+:+only means "space" in a query string. In a URL path it is a literal plus, so any resource name containing a space would be silently corrupted. There is a spec pinning this down.This overlaps #527, which found the same thirteen call sites — credit there for spotting it — but converts them to
CGI.escape. I have left a comment on that PR with the detail.Two call sites that need care
delete_multiple_objectsescapes the joined"#{container}/#{name}", so the separator has to survive. It passes'/'as an extra excluded character, matching howStorage::Files#get_urlalready does this. A blanket replacement here would emitcontainer%2Fobjectand break Swift bulk delete outright.The compute service requests escaped their optional params like this:
That assigns back into the hash while iterating it, mutating the caller's hash as a side effect.
transform_valuesescapes the values without touching the original. There is a spec for that too.base64 development dependency
Neither the spec nor the unit suite can load at all on Ruby 3.4+ without this:
base64became a bundled gem in Ruby 3.4, andwebmockrequires it without declaring a dependency on it. Declaring it is required for any of this to be testable on a current Ruby, so it is included here.Tests
spec/escaping_spec.rbadds ten specs: the escaping behaviour of each group of call sites, the space and separator cases that distinguishFog::OpenStack.escapefromCGI.escape, the non-mutation of the caller hash, and a guard thatURI.encodedoes not reappear anywhere inlib/.They build a
Realinstance withallocateand stub#request, so no credentials or network are needed.RuboCop is unchanged on
lib/(0 offences before and after on the touched files) and clean on the new spec.State of the rest of the suite
Being straightforward about this, since the numbers look alarming out of context.
On master both suites fail to load on Ruby 4.0.6 with the
base64LoadError. With that fixed they run, and report pre-existing failures:rake tests:specrake tests:unitNone of those errors are in files this PR touches, and all ten new specs pass inside the full run. They are almost entirely one separate issue: the suites use Minitest's bare expectation style (
x.must_equal y), which Minitest 6 removed in favour of_(x).must_equal y.minitestis unpinned in the gemspec, so a current bundle resolves 6.0.6 and those calls becomeNoMethodError.That is a real blocker for Ruby 3.4/4.x support, but it is a large mechanical change across the whole test suite and unrelated to URL escaping, so I have deliberately kept it out of this PR. Happy to open it separately if useful.