When I modify an ActiveRecord object during a session, I cannot seem to retrieve this modified instance in a method call. A simplified example follows:
Assume we have a model with only two objects: Project
and Task
linked with a 1-n relationship. Both objects can be active, but tasks require their parent project to be active before activating. There are two ways to activate: globally through the Project
(which activates all tasks) or individually through a Task
.
With the following straightforward implementation, an error occurs:
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
Indeed, the console will report
>> 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'
The problem is that the Project
object instance modified in the Project#activate
method is not the same that ActiveRecord loads when accessing the Task#project
relationship in the Task#activate
method. When debugging, both objects are the "same" ActiveRecord record, but not the same Ruby object instance.
>> 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'
In other ORM systems, fetching a model instance by database identifier always looks in "cache", at least during a transaction and even during a session. I have tried to eager load the relations, but that does not change the issue since I could still be using another Project instance than the one ActiveRecord decided to link to the Task object.
Is there any technique (or gem or third-party) to get this simple process to work? That is that every reference to the same ActiveRecord record during a session/thread always refers to the same Ruby object instance?
Thanks,
-Jason