I seem to run into a some kind of circular relationships that the two solutions in the gem's documentation won't solve for me. See the example below. Is this meant to be done differently?
One would argue that because one object could not really be persisted without the other they ought to just be one model. I think it's better to extract all the logic regarding authentication to it's seperate model in order not to bloat the user. Most of the time credential stuff is only used when creating sessions, whereas the user is used all the time.
create_table "credentials", force: :cascade do |t|
t.bigint "user_id", null: false
...
t.index ["user_id"], name: "index_credentials_on_user_id"
end
add_foreign_key "credentials", "users"
class Credential < ApplicationRecord
belongs_to :user, inverse_of: :credential
end
class User < ApplicationRecord
has_one :credential, inverse_of: :user
validates :credential, presence: true
end
Fabricator(:user_base, class_name: :user)
Fabricator(:user, from: :user_base) do
credential
end
Fabricator(:credential) do
user(fabricator: :user_base)
end
irb(main):001:0> Fabricate(:user)
TRANSACTION (0.1ms) BEGIN
TRANSACTION (0.1ms) ROLLBACK
Traceback (most recent call last):
1: from (irb):1:in `<main>'
ActiveRecord::RecordInvalid (Validation failed: Credential can't be blank)
irb(main):002:0> Fabricate(:credential)
Traceback (most recent call last):
2: from (irb):1:in `<main>'
1: from (irb):2:in `rescue in <main>'
ActiveRecord::RecordInvalid (Validation failed: Credential can't be blank)
irb(main):003:0> Fabricate.build(:user).save
TRANSACTION (0.2ms) BEGIN
User Create (0.8ms) INSERT INTO "users" ("email", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["email", "fake@mail.com"], ["created_at", "2021-05-29 18:19:09.312429"], ["updated_at", "2021-05-29 18:19:09.312429"]]
Credential Create (0.9ms) INSERT INTO "credentials" ("user_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["user_id", 19], ["created_at", "2021-05-29 18:19:09.319411"], ["updated_at", "2021-05-29 18:19:09.319411"]]
TRANSACTION (41.2ms) COMMIT
=> true
The way you're solving this would certainly work. The way I normally recommend people solve this is by overriding the inverse relationships. ActiveRecord will do the right thing in this case.
Fabricator(:user) do
credential { Fabricate.build(:credential, user: nil) }
end
Fabricator(:credential) do
user { Fabricate.build(:user, credential: nil) }
end