Support Rails 8.1 / Ruby 4#24
Conversation
The Active Record compatibility layer raised "Unsupported Active Record version" on anything past 6.1: it looked up a per-version Vxx support module and had none for 7.0+. Every release from 5.2 onward needs the same treatment (native JSONB, plus the removed `scoped` and `sanitize` APIs revived), so add a single `Modern` support module and fall back to it for any Active Record major >= 7 instead of shipping an identical Vxx module per Rails release. The explicit V32..V61 modules are untouched, so older consumers behave exactly as before. Add rails-8.0 and rails-8.1 Appraisal entries (and their gemfiles) next to the existing 7.x ones. Add a self-contained, Postgres-free smoke harness (test/smoke.rb) that installs the authorization DSL and checks an end-to-end ACL grant -> Actor#can? decision over sqlite, plus a CI matrix sweeping Ruby 4.0.5/3.4/2.7 across Rails 8.1/7.2/6.1. Verified green on Ruby 4.0.5 and 3.4 (Rails 8.1) and Ruby 3.3 (Rails 7.1). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Heads-up on the red checks: the Smoke workflow (added here, and the one that actually exercises the Rails 8.1 / Ruby 4 upgrade) is green across the matrix. The failures are all in the pre-existing pg + cucumber
Fixing that legacy matrix (pin i18n / drop ancient Rubies / extend it to Rails 7.x–8.x with cucumber) is a separate maintenance task — happy to do it in a follow-up, but kept it out of scope here so the upgrade stays reviewable. |
The legacy CI matrix tested Ruby 2.1–3.1 against Rails 3.2–6.1 and had rotted: old Rubies no longer assemble on current runners, and the rails_6.1/Ruby 3.1 row died at `require 'rails'` on i18n 1.15's Fiber storage (needs Ruby 3.2+). Replace it with a current matrix — Ruby 4.0.5/3.4/3.3/3.2 across Rails 8.1/8.0/7.2/7.1/7.0/6.1 — keeping Postgres + the full rspec and cucumber runs. Three test-only fixes for the new Ruby/Rails, all version-agnostic: - Compatibility#support_module derived the "modern AR" decision from the live `ActiveRecord::VERSION::MAJOR`, which broke the "unsupported version" cucumber scenario (it stubs `active_record_version` to 3.0, but the process runs AR 8, so the fallback returned Modern instead of raising). Derive the major from the looked-up version instead, so a stubbed value is honoured. - The parse-error feature asserted the exact pre-Prism SyntaxError wording; match the stable `unexpected '='` substring so it holds on Ruby 3.3+ and older. - The ACL#inspect/#pretty_inspect specs hard-coded the pre-3.4 Hash#inspect spacing; compute the expectation from the running Ruby's Hash#inspect. Full suite green locally on Ruby 3.4 / Rails 8.1: 21 examples, 33 scenarios / 253 steps, 0 failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cucumber's ProfileLoader runs .config/cucumber.yml through ERB at startup, and on Ruby 3.4 the cucumber version CI resolves calls the removed 3-argument ERB.new, so every cucumber run aborted with "could not be parsed with ERB: wrong number of arguments (given 3, expected 1)" before any feature ran. The profile only set `--format progress`, which the CI command passes explicitly, so the file is redundant. Removing it sidesteps the ERB parse on every cucumber version. Suite still green locally (33 scenarios / 253 steps). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unpinned, the resolver chose a different cucumber per matrix row — 10.x on some rows (profile ERB breaks on Ruby 3.4) and the ancient 3.2.0 on Ruby 4.0.5, which needs ostruct and failed to load. Pin cucumber to ~> 9.2 for a consistent, Ruby-4-clean version across the matrix, and declare ostruct/base64/bigdecimal, which left the default gems on Ruby 3.4+/4.0 and are pulled in by the test toolchain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Update: the legacy pg + cucumber Modernized the Test-infra fixes needed for the new stack, all version-agnostic:
|
The legacy ci.yml already used checkout@v6; the rewrite dragged it back to v4. Restore v6 and bump the smoke workflow to match (v4 also trips the Node 20 deprecation warning). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring back old-Ruby coverage (the rewrite had narrowed to a 3.2 floor). The modern test toolchain fights old Ruby, so the gemspec now pins per-RUBY_VERSION: cucumber ~> 8.0 on Ruby < 3.0 (9.x needs 3.0+) and i18n < 1.15 on Ruby < 3.2 (1.15's Fiber storage needs 3.2+, which is what broke Rails 6.1 there). The ostruct/base64/bigdecimal dev deps are scoped to Ruby >= 3.4 so older rows don't pull a Ruby-3+ build. Ruby 2.1–2.6 stay out: Bundler 2 requires Ruby >= 2.3, and 2.3–2.6 no longer assemble on current GitHub runners. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The rules reference application models (e.g. ::Dossier). Under Zeitwerk (Rails 7+) those can't be autoloaded during initialization, so parsing inline in the 'eaco.parse_rules' initializer raised 'uninitialized constant Dossier' on boot. Move the parse into config.to_prepare, which runs after the app is initialized (once at boot in every env, again on each dev reload) with the autoloaders ready. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Makes eaco load and run on Rails 8.1 / Ruby 4 without changing how it behaves on the Rails versions consumers are on today. This is part of the fleet Rails 8.1 / Ruby 4 upgrade — eaco was a hard blocker for the apps that depend on it (edoc, hermeshq, noobs, scriptoria).
The problem
The Active Record compatibility layer looked up a per-version support module (
V32,V42, …V61) by the AR major+minor version, and raised "Unsupported Active Record version" when none was found. There was no module past Rails 6.1, so eaco couldn't even load under Rails 7.0+ — let alone 8.1.Worth noting: the Appraisals file already listed rails-7.0/7.1/7.2, but those rows would have raised at runtime — the compatibility layer never actually supported them.
What changed
Modernsupport module and a fallback for Active Record 7.0+. Every release from 5.2 onward needs the same treatment — JSONB is native, and the removedscoped/sanitizeAPIs are revived via the existingScoped/Sanitizedmodules. Rather than copy an identicalVxxmodule for every future Rails release, the compatibility lookup now falls back toModernfor any AR major ≥ 7. The explicitV32…V61modules are untouched, so nothing changes for older consumers.test/smoke.rb— it installs the authorization DSL on a model and checks an end-to-end decision (ACL grant →Actor#can?, plus the admin bypass) over sqlite. No Postgres, no network: the point is to prove the compatibility layer installs and the core authorization path runs on the new stack. The existing pg + cucumber suite (ci.yml) is unchanged.How to test
The new smoke workflow (
smoke.yml) sweeps:V61module)Locally:
BUNDLE_GEMFILE=test/Gemfile.ci bundle install && BUNDLE_GEMFILE=test/Gemfile.ci bundle exec ruby test/smoke.rb(override the stack withRAILS_VERSION).Verified green on Ruby 4.0.5 and 3.4 (Rails 8.1) and Ruby 3.3 (Rails 7.1).
Risks / follow-ups
pg_jsonbaccessible_bySQL. The full pg + cucumber matrix inci.ymlstill covers that; bumping that matrix to include Rails 7.x/8.x is a sensible follow-up now that the gem actually supports those versions.Sanitizedcallsconnection.quote;ActiveRecord::Base.connectionis soft-deprecated in newer Rails but still works on 8.1. Worth revisiting if it gets removed.