A from-scratch fork of simply_couch's model layer that runs on Couchbase natively: `include SimplyBased::Model` instead of `include SimplyCouch::Model`. - SimplyBased::Store replaces the CouchDB layer: KV documents, N1QL/SQL++ finders + lazily-created GSIs, CAS mapped onto _rev. No design documents or JavaScript map/reduce — the hand-written association views become N1QL (belongs_to/has_many/has_one scalar FK; habtm via ANY ... SATISFIES + array index, or KV multi-get on the storing side). - Redis replacements on the same cluster: Counter (atomic increment/decrement), Cache (ActiveSupport::Cache::Store over KV+TTL, drop-in Rails.cache/ActiveJob), Lock (insert-if-absent + TTL + CAS-checked release). - Runs the simply_couch test suite against live Couchbase: 378/432 applicable examples green (CouchDB-mechanic specs tagged :couchdb_only and excluded). - Remaining: ancestry (tree) + embedded_in reverse-lookups (recursive/embedded N1QL) and a few pagination/lifecycle edges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
140 lines
4.2 KiB
Ruby
140 lines
4.2 KiB
Ruby
require 'spec_helper'
|
|
|
|
describe 'has_one' do
|
|
it 'adds a getter method' do
|
|
expect(Instance.new).to respond_to(:identity)
|
|
end
|
|
|
|
it 'raises an error if another property with the same name already exists' do
|
|
expect {
|
|
class ::DoubleHasOneUser
|
|
include SimplyBased::Model
|
|
property :user
|
|
has_one :user
|
|
end
|
|
}.to raise_error RuntimeError
|
|
end
|
|
|
|
it 'fetches the object when invoking the getter' do
|
|
instance = Instance.create
|
|
identity = Identity.create(instance: instance)
|
|
expect(instance.identity).to eq identity
|
|
end
|
|
|
|
it 'sets the parent object on the clients cache' do
|
|
expect(Instance).not_to receive(:find)
|
|
instance = Instance.create
|
|
identity = Identity.create(instance: instance)
|
|
expect(instance.identity.instance).to eq instance
|
|
end
|
|
|
|
it 'uses the correct view when handling inheritance' do
|
|
problem = Problem.create
|
|
big_problem = BigProblem.create
|
|
issue = Issue.create(name: 'Thing', problem: problem)
|
|
expect(problem.issue).to eq issue
|
|
issue.update_attributes(problem_id: nil, big_problem_id: big_problem.id)
|
|
expect(big_problem.issue).to eq issue
|
|
end
|
|
|
|
it 'verifies the given options for the accessor method' do
|
|
instance = Instance.create
|
|
expect { instance.identity(foo: :var) }.to raise_error ArgumentError
|
|
end
|
|
|
|
it 'verifies the given options for the association definition' do
|
|
expect {
|
|
User.instance_eval do
|
|
has_one :foo, bar: :do
|
|
end
|
|
}.to raise_error ArgumentError
|
|
end
|
|
|
|
it 'stores the fetched object into the cache' do
|
|
instance = Instance.create
|
|
identity = Identity.create(instance: instance)
|
|
instance.identity
|
|
expect(instance.instance_variable_get('@identity')).to eq identity
|
|
end
|
|
|
|
it 'does not fetch from the database when object is in cache' do
|
|
instance = Instance.create
|
|
identity = Identity.create(instance: instance)
|
|
instance.identity
|
|
expect(SimplyBased.database).not_to receive(:view)
|
|
instance.identity
|
|
end
|
|
|
|
it 'updates the foreign object to have the owners id in the foreign key' do
|
|
instance = Instance.create
|
|
identity = Identity.create
|
|
instance.identity = identity
|
|
identity.reload
|
|
expect(identity.instance_id).to eq instance.id
|
|
end
|
|
|
|
it 'updates the cache when setting' do
|
|
instance = Instance.create
|
|
identity = Identity.create
|
|
instance.identity = identity
|
|
expect(SimplyBased).not_to receive(:database)
|
|
expect(instance.identity).to eq identity
|
|
end
|
|
|
|
it 'sets the foreign key value to nil when assigning nil' do
|
|
instance = Instance.create
|
|
identity = Identity.create(instance: instance)
|
|
instance.identity = nil
|
|
identity = Identity.find(identity.id)
|
|
expect(identity.instance_id).to be_nil
|
|
end
|
|
|
|
it 'checks the class' do
|
|
instance = Instance.create
|
|
expect { instance.identity = 'foo' }.to raise_error ArgumentError, /expected Identity got String/
|
|
end
|
|
|
|
it 'deletes the dependent objects when dependent is set to destroy' do
|
|
identity = Identity.create
|
|
mag = Magazine.create
|
|
mag.identity = identity
|
|
mag.identity = nil
|
|
expect(Identity.find_by_id(identity.id)).to be_nil
|
|
end
|
|
|
|
it 'unsets the id on the foreign object when a new object is set' do
|
|
instance = Instance.create
|
|
identity = Identity.create(instance: instance)
|
|
identity2 = Identity.create
|
|
instance.identity = identity2
|
|
identity = Identity.find(identity.id)
|
|
expect(identity.instance_id).to be_nil
|
|
end
|
|
|
|
it 'deletes the foreign object when a new object is set and dependent is set to destroy' do
|
|
identity = Identity.create
|
|
identity2 = Identity.create
|
|
mag = Magazine.create
|
|
mag.identity = identity
|
|
mag.identity = identity2
|
|
expect(Identity.find_by_id(identity.id)).to be_nil
|
|
end
|
|
|
|
it 'deletes the foreign object when parent is destroyed and dependent is set to destroy' do
|
|
identity = Identity.create
|
|
mag = Magazine.create
|
|
mag.identity = identity
|
|
mag.destroy
|
|
expect(Identity.find_by_id(identity.id)).to be_nil
|
|
end
|
|
|
|
it 'nullifies the foreign objects foreign key when parent is destroyed' do
|
|
identity = Identity.create
|
|
instance = Instance.create
|
|
instance.identity = identity
|
|
instance.destroy
|
|
identity = Identity.find(identity.id)
|
|
expect(identity.instance_id).to be_nil
|
|
end
|
|
end
|