Skip to content

Bump oauth2 + faraday to 2.x for Rails 8.0 compatibility - #14

Open
wmaciejak wants to merge 2 commits into
masterfrom
bump-dependencies
Open

Bump oauth2 + faraday to 2.x for Rails 8.0 compatibility#14
wmaciejak wants to merge 2 commits into
masterfrom
bump-dependencies

Conversation

@wmaciejak

Copy link
Copy Markdown

Bump oauth2 & faraday to 2.x — Rails 8.0 readiness

Why

This fork could not run under Rails 8.0. The blocker: BaseModel#as_json
mutated its options[:except] in place, and Rails 8 now freezes the options
Hash passed down through Array#as_json / Hash#as_json recursion
(rails/rails@5f73931) — so serializing any model nested in a container raised
FrozenError. housecall-web currently patches around this at runtime.

Fixing it cleanly also required moving off the old HTTP stack: oauth2 ~> 1.4
transitively pins faraday < 2, so oauth2 and faraday have to be bumped
together
(oauth2 2.0 relaxes the constraint to faraday < 3).

What changed

Rails 8 fix (the point of this PR)

  • BaseModel#as_json now dups the options Hash before mutating it. This folds
    in the workaround housecall-web carries as a monkey patch, so that patch can be
    removed once this ships.

Dependency bumps

  • oauth2 ~> 1.4~> 2.0
  • faraday < 2.0~> 2.0
  • + faraday-multipart ~> 1.0 — Faraday 2 extracted the :multipart request
    middleware and UploadIO/FilePart into this gem.
  • net-http-persistentfaraday-net_http_persistent ~> 2.0 — under Faraday 2
    the :net_http_persistent adapter lives in this gem, not the raw one (which was
    inert). Required lazily in Quickbooks.http_adapter= so the default :net_http
    path pays no load cost.
  • Added required_ruby_version >= 3.0 (Faraday 2's floor; a Rails 8 host adds
    >= 3.2 via activemodel). activemodel stays unpinned on the upper bound so
    activemodel 8.x is allowed.

Faraday 2 API migration

  • Faraday::Middleware.dependency was removed → plain require 'zlib' / require 'brotli'
    (brotli now raises an actionable LoadError if the optional gem is absent).
  • register_middleware no longer accepts the lambda { Klass } thunk form → register
    the class directly.
  • Connection#build was removed → the OAuth2 client's Faraday stack is now injected via
    client.options[:connection_build] (which oauth2 2.0 uses to build the connection
    lazily) instead of mutating client.connection.
  • Faraday::UploadIOFaraday::Multipart::FilePart.
  • Connection#basic_auth was removed → request :authorization, :basic, … (used in
    AccessToken#disconnect).

oauth2 2.0

  • Verified AccessToken#get/#post(…, raise_errors: false) still suppresses raising on
    non-2xx (QBO faults must be wrapped, not raised) — Client#request honors the
    per-call override.

Incidental fix (pre-existing bug)

  • ChangeService#url_for_query took 3 args while fetch_collection calls it with 4
    (options) → ArgumentError. Signature aligned with the base method. (This was
    failing before this PR, independent of the bump.)

Tests

  • Added BaseModel#as_json coverage (there was none), including regression cases that
    exercise the real Rails 8 trigger — a model nested in an Array/Hash serialized
    with options, which is what makes ActiveSupport freeze them.
  • Rewrote the url_for_query and request-hook specs to assert on decoded queries and
    structured RequestInfo instead of exact stdout — they were coupled to Faraday's
    + vs %20 encoding and Ruby's Hash#inspect format (=> vs => in Ruby 3.4).

Testing

  • bundle exec rspec464 examples, 0 failures on Ruby 3.4.1 with
    activemodel/activesupport 8.1.3 (Rails 8's ActiveModel/ActiveSupport).
  • The as_json regression was proven end-to-end: the container-recursion path delivers
    a frozen=true options Hash into as_json; the pre-fix code raises FrozenError on
    that exact path, the fix does not.

Downstream / migration notes

  • housecall-web: remove the Quickbooks::Model::BaseModel#as_json monkey patch only
    after the Gemfile points at a ref containing this change (until then it's a redundant
    no-op). Also re-verify any direct OAuth2::Client construction / token refresh
    (get_token, refresh!) against the oauth2 2.0 API.
  • Consumers using Quickbooks.http_adapter = :net_http_persistent now get the correct
    Faraday 2 adapter automatically.

Out of scope (follow-up)

  • BaseModelJSON#to_json and TaxRateDetailLine#to_json are arity-0 (def to_json), so
    a to_json(options) call during serialization would ArgumentError. Pre-existing and
    version-agnostic — not addressed here.

Prepares the gem for Rails 8. The blocker was BaseModel#as_json mutating a
frozen options Hash (Rails 8 freezes options during Array/Hash#as_json
recursion). oauth2 1.4 also pinned faraday < 2, so both had to move together.

- oauth2 ~> 2.0, faraday ~> 2.0, + faraday-multipart (:multipart / UploadIO
  moved out of Faraday core)
- Faraday 2 API: dependency -> require, register_middleware class form,
  connection_build rebuild, FilePart, request :authorization
- as_json dups options before mutating (folds in the housecall-web patch)
- required_ruby_version >= 3.0; net-http-persistent -> faraday-net_http_persistent
  (lazy-required only when that adapter is selected)
- Fix pre-existing ChangeService#url_for_query arity mismatch
- Version-independent specs; suite green on activemodel 8.1.3 / Ruby 3.4 (464/0)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@wmaciejak
wmaciejak requested a review from lukasz-hcp July 8, 2026 09:30
@wmaciejak wmaciejak self-assigned this Jul 8, 2026
Faraday 2 / oauth2 2 (and required_ruby_version >= 3.0) can't resolve on
Ruby < 3.0, so the 2.5/2.6/2.7 jobs failed at bundle install ("Could not
find compatible versions"). Align the matrix with the supported range
(3.0-3.4) and make ruby-head informational via continue-on-error.

Also drop the `ruby RUBY_VERSION` Gemfile pin, which broke bundle install
on prerelease Rubies (ruby-head: "Your Ruby version is 4.1.0.dev, but your
Gemfile specified 4.1.0"). The Ruby floor now lives in the gemspec.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

1 participant