From 63c5e7d0d5ee105cccba1584c9cd140a6ec60763 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 2 Feb 2026 12:06:12 -0500 Subject: [PATCH 1/2] Fix ActiveStorage::FixtureSet compatibility ActiveStorage::FixtureSet.blob creates blobs with keys that don't have the tenant prefix, causing path_for to fail when splitting on "/". Keys without tenant prefix (e.g., from ActiveStorage::FixtureSet.blob) now fall back to the standard DiskService `path_for` behavior instead of erroring when trying to split on "/". --- lib/active_record/tenanted/storage.rb | 6 +----- test/integration/test/active_storage_test.rb | 5 +++++ .../test/fixtures/active_storage/attachments.yml | 4 ++++ test/smarty/test/fixtures/active_storage/blobs.yml | 1 + test/unit/storage_test.rb | 13 +++++++++++++ 5 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 test/smarty/test/fixtures/active_storage/attachments.yml create mode 100644 test/smarty/test/fixtures/active_storage/blobs.yml diff --git a/lib/active_record/tenanted/storage.rb b/lib/active_record/tenanted/storage.rb index da03343f..5c545140 100644 --- a/lib/active_record/tenanted/storage.rb +++ b/lib/active_record/tenanted/storage.rb @@ -17,11 +17,7 @@ def root end def path_for(key) - if ActiveRecord::Tenanted.connection_class - # TODO: this is brittle if the key isn't tenanted ... errors in folder_for: - # - # NoMethodError undefined method '[]' for nil (NoMethodError) [ key[0..1], key[2..3] ].join("/") - # + if ActiveRecord::Tenanted.connection_class && key.include?("/") tenant, key = key.split("/", 2) File.join(root, tenant, folder_for(key), key) else diff --git a/test/integration/test/active_storage_test.rb b/test/integration/test/active_storage_test.rb index 55ff0616..eb6b20b8 100644 --- a/test/integration/test/active_storage_test.rb +++ b/test/integration/test/active_storage_test.rb @@ -1,6 +1,11 @@ require "test_helper" class TestActiveStorage < ActionDispatch::IntegrationTest + test "fixtures work with ActiveStorage::FixtureSet" do + note = notes(:one) + assert_predicate(note.image, :attached?, "Expected note fixture to have an attached image from ActiveStorage fixtures") + end + test "can upload a file" do post(notes_path, params: { diff --git a/test/smarty/test/fixtures/active_storage/attachments.yml b/test/smarty/test/fixtures/active_storage/attachments.yml new file mode 100644 index 00000000..d2cee883 --- /dev/null +++ b/test/smarty/test/fixtures/active_storage/attachments.yml @@ -0,0 +1,4 @@ +one_image: + name: image + record: one (Note) + blob: one_image_blob diff --git a/test/smarty/test/fixtures/active_storage/blobs.yml b/test/smarty/test/fixtures/active_storage/blobs.yml new file mode 100644 index 00000000..d75f8e18 --- /dev/null +++ b/test/smarty/test/fixtures/active_storage/blobs.yml @@ -0,0 +1 @@ +one_image_blob: <%= ActiveStorage::FixtureSet.blob filename: "goruco.jpg", service_name: "test" %> diff --git a/test/unit/storage_test.rb b/test/unit/storage_test.rb index cf2ccc89..eb168212 100644 --- a/test/unit/storage_test.rb +++ b/test/unit/storage_test.rb @@ -85,6 +85,19 @@ end end end + + test "Disk Service path_for falls back for keys without tenant prefix" do + ActiveRecord::Tenanted.stub(:connection_class, TenantedApplicationRecord) do + TenantedApplicationRecord.create_tenant("foo") do + blob = ActiveStorage::Blob.new(filename: "foo.jpg", byte_size: 100, checksum: "abc123", service_name: service_name) + + # Keys from ActiveStorage::FixtureSet don't have tenant prefix + non_tenanted_key = "abc123def456" + expected_path = "/path/to/storage/ab/c1/#{non_tenanted_key}" + assert_equal expected_path, blob.service.path_for(non_tenanted_key) + end + end + end end end end From d3ef03bcf0aa6af90f2e9e75d8069ce87e8ff36a Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 2 Feb 2026 13:25:34 -0500 Subject: [PATCH 2/2] Update CHANGELOG for recent commits Move callbacks entries from v0.6.0 to unreleased section (they were committed after the v0.6.0 tag) and add entry for ActiveStorage FixtureSet compatibility fix. --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eede0b54..692f6f01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,15 @@ ## next / unreleased +### Added + +- Add callbacks for `:with_tenant` which are invoked when `.with_tenant` is called. @flavorjones +- Add callbacks for `:set_current_tenant` which are invoked when `.current_tenant=` is called. @flavorjones + ### Fixed - `.current_tenant = nil` now clears the tenant context, properly setting the shard to `UNTENANTED_SENTINEL` instead of `""` @flavorjones +- `Tenanted::DiskService#path_for` now handles keys without a tenant prefix (e.g., from `ActiveStorage::FixtureSet.blob`) by falling back to standard DiskService behavior. @flavorjones ## v0.6.0 / 2025-11-05 @@ -59,8 +65,6 @@ Read the [Rails Guide documentation on `config.active_record.query_log_tags`](ht ### Added -- Add callbacks for `:with_tenant` which are invoked when `.with_tenant` is called. -- Add callbacks for `:set_current_tenant` which are invoked when `.current_tenant=` is called. - `UntenantedConnectionPool#size` returns the database configuration's `max_connections` value, so that code (like Solid Queue) can inspect config params without a tenant context.