Когда я изменяю объект ActiveRecord во время сеанса, я не могу получить этот измененный экземпляр в вызове метода. Ниже приведен упрощенный пример:
Предположим, у нас есть модель только с двумя объектами: Project
и Task
, связанными отношением 1-n. Оба объекта могут быть активными, но задачи требуют, чтобы их родительский проект был активным перед активацией. Существует два способа активации: глобально через Project
(который активирует все задачи) или индивидуально через Task
.
При следующей простой реализации возникает ошибка:
class Project < ActiveRecord::Base
# Relations
has_many :tasks
def activate
self.transaction do
self.active = true
tasks.each {|task| task.activate}
end
end
end
class Task < ActiveRecord::Base
# Relations
belongs_to :project
def activate
raise ArgumentError, "Cannot activate a task of an inactive project" unless project.active?
self.active = true
end
end
Действительно, консоль выдаст сообщение
>> project = Project.first
=> #<Project id: 1, name: "Test project", active: false>
>> project.activate
ArgumentError: Cannot activate a task of an inactive project
from /Rails/cache_issue/app/models/task.rb:7:in `activate'
from /Rails/cache_issue/app/models/project.rb:9:in `activate'
Проблема заключается в том, что экземпляр объекта Project
, измененный в методе Project#activate
, не совпадает с тем, что ActiveRecord загружает при доступе к отношению Task#project
в методе Task#activate
. При отладке оба объекта представляют собой «одну и ту же» запись ActiveRecord, но не один и тот же экземпляр объекта Ruby.
>> project = Project.first
=> #<Project id: 1, name: "Test project", active: false>
>> project.activate
"Project#activate: self.id = 1, self.object_id = 2176477060"
" Task#activate: project.id = 1, project.object_id = 2176246440"
ArgumentError: Cannot activate a task of an inactive project
from /Rails/cache_issue/app/models/task.rb:8:in `activate'
from /Rails/cache_issue/app/models/project.rb:10:in `activate'
В других системах ORM выборка экземпляра модели по идентификатору базы данных всегда выглядит в «кэше», по крайней мере, во время транзакции и даже во время сеанса. Я пытался загрузить отношения, но это не меняет проблему, поскольку я все еще мог использовать другой экземпляр Project, чем тот, который ActiveRecord решил связать с объектом Task.
Есть ли какая-либо техника (или гем, или сторонний разработчик), чтобы заставить этот простой процесс работать? То есть каждая ссылка на одну и ту же запись ActiveRecord во время сеанса / потока всегда ссылается на один и тот же экземпляр объекта Ruby?
Спасибо
1028 * Джейсон *