From e93cd7f42909a32134234676914cd094909c693d Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sun, 2 Nov 2025 18:50:24 +0100 Subject: [PATCH 1/4] Add grant_fixture_access with all the caveats --- README.md | 36 ++++++++++++++++++- lib/oaken/seeds.rb | 25 +++++++++++++ .../app/models/account/fixture_access.rb | 3 ++ ...2174456_create_account_fixture_accesses.rb | 10 ++++++ test/dummy/db/schema.rb | 10 +++++- test/dummy/db/seeds/setup.rb | 2 ++ .../fixtures/account/fixture_accesses.yml | 3 ++ test/dummy/test/models/oaken_test.rb | 5 +++ test/dummy/test/test_helper.rb | 1 + 9 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 test/dummy/app/models/account/fixture_access.rb create mode 100644 test/dummy/db/migrate/20251102174456_create_account_fixture_accesses.rb create mode 100644 test/dummy/test/fixtures/account/fixture_accesses.yml diff --git a/README.md b/README.md index 454c3cb..06adb98 100644 --- a/README.md +++ b/README.md @@ -567,7 +567,41 @@ You can convert your Rails fixtures to Oaken's seeds by running: bin/rails generate oaken:convert:fixtures ``` -This will convert anything in test/fixtures to db/seeds. E.g. `test/fixtures/users.yml` becomes `db/seeds/users.rb` and so on. +This will convert anything in test/fixtures to db/seeds. E.g. `test/fixtures/users.yml` becomes `db/seeds/test/users.rb` and so on. + +#### Accessing Oaken seeds from fixtures + +Call `grant_fixture_access` to make Oaken seeds accessible from fixtures: + +```ruby +# db/seeds/setup.rb +grant_fixture_access accounts, users +``` + +```ruby +# db/seeds/test/accounts.rb +accounts.create :kaspers_donuts, name: "Kasper's Donuts" +accounts.create name: "Demo" # Inaccessible! Pass a label ala `:kaspers_donuts` above to access this record from fixtures. + +# You can also use `accounts.label` to expose records in fixtures. +accounts.label demo: Account.find_by!(name: "Demo") +``` + +Now you can access the created record from fixtures: + +```yaml +# test/fixtures/menus.yml +basic_kasper_menu: + name: "Kasper's Basic Menu" + account: kaspers_donuts + +basic_demo_menu: + name: "Demo's Basic Menu" + account: demo +``` + +> [!WARNING] +> Fixtures truncate data per table before inserting any anything. This means if you have say `test/fixtures/menus.yml`, you can't have any menus in Oaken seeds. So you have to port by file and then every fixture in a file. So convert every fixture in e.g. `test/fixtures/menus.yml` at once. #### Disable fixtures diff --git a/lib/oaken/seeds.rb b/lib/oaken/seeds.rb index 068c1a7..e428fab 100644 --- a/lib/oaken/seeds.rb +++ b/lib/oaken/seeds.rb @@ -48,4 +48,29 @@ def self.respond_to_missing?(meth, ...) = loader.locate(meth) || super # end # end def self.section(*, **) = block_given? && yield + + # Make any labelled records accessible in Rails' fixtures. Useful when porting from fixtures to Oaken. + # + # grant_fixture_access accounts + # + # # Now passing the label `:kaspers_donuts` here makes this record accessible: + # accounts.create :kaspers_donuts, name: "Kasper's Donuts" + # + # # test/fixtures/menus.yml + # basic: + # name: "Basic Menu" + # account: kaspers_donuts # We're accessing the named record by the label here. + def self.grant_fixture_access(*registers) + registers.each { _1.extend FixtureAccess } + end + + module FixtureAccess + def create(label = nil, id: identify(label), **) = super + def upsert(label = nil, id: identify(label), **) = super + + private + def identify(label) + ActiveRecord::FixtureSet.identify(label) if label + end + end end diff --git a/test/dummy/app/models/account/fixture_access.rb b/test/dummy/app/models/account/fixture_access.rb new file mode 100644 index 0000000..9afb249 --- /dev/null +++ b/test/dummy/app/models/account/fixture_access.rb @@ -0,0 +1,3 @@ +class Account::FixtureAccess < ApplicationRecord + belongs_to :account +end diff --git a/test/dummy/db/migrate/20251102174456_create_account_fixture_accesses.rb b/test/dummy/db/migrate/20251102174456_create_account_fixture_accesses.rb new file mode 100644 index 0000000..cdac0f5 --- /dev/null +++ b/test/dummy/db/migrate/20251102174456_create_account_fixture_accesses.rb @@ -0,0 +1,10 @@ +class CreateAccountFixtureAccesses < ActiveRecord::Migration[8.0] + def change + create_table :account_fixture_accesses do |t| + t.references :account, null: false, index: true + t.string :name + + t.timestamps + end + end +end diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index 01cf1ec..6017143 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -10,7 +10,15 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_10_24_125945) do +ActiveRecord::Schema[8.0].define(version: 2025_11_02_174456) do + create_table "account_fixture_accesses", force: :cascade do |t| + t.integer "account_id", null: false + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_account_fixture_accesses_on_account_id" + end + create_table "accounts", id: :string, default: -> { "ULID()" }, force: :cascade do |t| t.string "name" t.datetime "created_at", null: false diff --git a/test/dummy/db/seeds/setup.rb b/test/dummy/db/seeds/setup.rb index 2eb502b..59f3a60 100644 --- a/test/dummy/db/seeds/setup.rb +++ b/test/dummy/db/seeds/setup.rb @@ -1,5 +1,7 @@ loader.defaults name: -> { "Shouldn't be used for users.name" }, title: -> { "Global Default Title" } +grant_fixture_access accounts + section users do user_count, email_address_count = 0.step, 0.step users.defaults name: -> { "Customer #{user_count.next}" }, diff --git a/test/dummy/test/fixtures/account/fixture_accesses.yml b/test/dummy/test/fixtures/account/fixture_accesses.yml new file mode 100644 index 0000000..edcf8ac --- /dev/null +++ b/test/dummy/test/fixtures/account/fixture_accesses.yml @@ -0,0 +1,3 @@ +basic: + name: "Basic Access" + account: kaspers_donuts diff --git a/test/dummy/test/models/oaken_test.rb b/test/dummy/test/models/oaken_test.rb index 570e8f0..2be780a 100644 --- a/test/dummy/test/models/oaken_test.rb +++ b/test/dummy/test/models/oaken_test.rb @@ -23,6 +23,11 @@ class OakenTest < ActiveSupport::TestCase assert_equal [users.kasper, users.coworker], accounts.kaspers_donuts.users end + test "accessing fixture from within rails fixtures" do + assert_equal 1, Account::FixtureAccess.count + assert_equal accounts.kaspers_donuts, Account::FixtureAccess.first.account + end + test "accessing fixture from test env" do assert plans.test_premium end diff --git a/test/dummy/test/test_helper.rb b/test/dummy/test/test_helper.rb index a1e3715..393d0a2 100644 --- a/test/dummy/test/test_helper.rb +++ b/test/dummy/test/test_helper.rb @@ -8,4 +8,5 @@ class ActiveSupport::TestCase parallelize workers: :number_of_processors, threshold: ENV.fetch("PARALLEL_TEST_THRESHOLD", 5).to_i include Oaken.test_setup + fixtures "account/fixture_accesses" end From 7857755806bda595924d757033ef8c4298ec147f Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Mon, 3 Nov 2025 19:37:26 +0100 Subject: [PATCH 2/4] Move this setup to just the test env --- README.md | 5 ++++- test/dummy/db/seeds/setup.rb | 2 -- test/dummy/db/seeds/test/setup.rb | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 test/dummy/db/seeds/test/setup.rb diff --git a/README.md b/README.md index 06adb98..74e10e7 100644 --- a/README.md +++ b/README.md @@ -574,10 +574,13 @@ This will convert anything in test/fixtures to db/seeds. E.g. `test/fixtures/use Call `grant_fixture_access` to make Oaken seeds accessible from fixtures: ```ruby -# db/seeds/setup.rb +# db/seeds/test/setup.rb grant_fixture_access accounts, users ``` +> [!TIP] +> Since fixtures are a test environment thing, place this in the test-environment dedicated setup in `db/seeds/test/setup.rb`. + ```ruby # db/seeds/test/accounts.rb accounts.create :kaspers_donuts, name: "Kasper's Donuts" diff --git a/test/dummy/db/seeds/setup.rb b/test/dummy/db/seeds/setup.rb index 59f3a60..2eb502b 100644 --- a/test/dummy/db/seeds/setup.rb +++ b/test/dummy/db/seeds/setup.rb @@ -1,7 +1,5 @@ loader.defaults name: -> { "Shouldn't be used for users.name" }, title: -> { "Global Default Title" } -grant_fixture_access accounts - section users do user_count, email_address_count = 0.step, 0.step users.defaults name: -> { "Customer #{user_count.next}" }, diff --git a/test/dummy/db/seeds/test/setup.rb b/test/dummy/db/seeds/test/setup.rb new file mode 100644 index 0000000..fc101e0 --- /dev/null +++ b/test/dummy/db/seeds/test/setup.rb @@ -0,0 +1 @@ +grant_fixture_access accounts From 2031f93509d3b1beb25c9cd4a817963dffe4495a Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Mon, 3 Nov 2025 19:42:21 +0100 Subject: [PATCH 3/4] Let commands set RAILS_ENV --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd53bd3..55c3445 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,6 @@ jobs: - rails: "7.2" ruby: "3.1" env: - RAILS_ENV: test RAILS_VERSION: ${{ matrix.rails }} name: ${{ format('rails-{0} ruby-{1}', matrix.rails, matrix.ruby) }} From 9687d078f10083ccabf56360b3a75dde4b931c79 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Mon, 3 Nov 2025 19:51:53 +0100 Subject: [PATCH 4/4] We have to setup the db in dev/test --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 55c3445..7ee5e76 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,6 +38,8 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler-cache: true + - run: bin/rails db:prepare + - name: Parallel Tests run: bin/rails test