Reputation: 5467
Is there any built-in way to attach an instance variable to a record? For example, suppose I have a User
class with a foo
attr_accessor
:
class User < ActiveRecord::Base
...
attr_accessor :foo
end
If I do
u = User.first
u.foo = "bar"
u.foo # -> "bar"
User.find(1).foo # => nil
This is the correct behavior and I understand why: the instance variable exists for the instance (in memory) and not for the record. What I want is something like a record variable, so that in the above example u.foo
and User.find(1).foo
return the same value within the same instance of my application. I don't want persistence: it's not appropriate for foo
to be a column in the table, it's just appropriate for foo
to return the same value for the same record during the life cycle of e.g., a controller action, console session, etc. Nor do I want a class variable via cattr_accessor
, because there's no reason that User.find(1).foo
should be the same as User.find(2).foo
.
The best bet I can come up with is to fake it with a class variable array and instance methods to get/set the appropriate element of the array:
class User < ActiveRecord::Base
cattr_accessor :all_the_foos
self.all_the_foos = Array.new
def foo
self.all_the_foos[id]
end
def foo= new_foo
self.all_the_foos[id] = new_foo
end
end
This ALMOST works, but it doesn't work with un-saved records, e.g. User.new.foo = "bar"
fails.
Upvotes: 2
Views: 1335
Reputation: 64363
You can use Thread.current
to set variables that are active within the context of a controller action invocation. Your current implementation doesn't guarantee context reset across calls.
class User < ActiveRecord::Base
after_create :set_thread_var
def foo
new_record? ? @foo : Thread.current["User-foo-#{id}"]
end
def foo=(val)
new_record? ? (@foo = val) : (Thread.current["User-foo-#{id}"] = val)
end
private
def set_thread_var
Thread.current["User-foo-#{id}"] = @foo if defined?(@foo)
end
end
Upvotes: 2
Reputation: 106027
I can't think of a better idea than your class variable solution. To solve your "new
" problem I'd use after_initialize
.
class User < ActiveRecord::Base
after_initialize :init_foos
cattr_accessor :all_the_foos
def foo
self.all_the_foos[id]
end
def foo= new_foo
self.all_the_foos[id] = new_foo
end
private
def init_foos
@@all_the_foos ||= []
end
end
Upvotes: 1