Missing attribute during initialization

Hello.

In one of my ActiveRecord models I provide an after initialization
callback
after_initialize :foobar

In that callback I check some attribute
def foobar
if my_attr.blank? …
end

Sometimes when I check the validation of an instance of that model
my_model.valid?
this foobar method (together with all my other after_initialize
methods) are called (why that? … the instance already exist.). The
main problem is that this leads to “missing attribute” errors.
It seems that the valid? method fetches and initializes objects from
the database without loading all attributes … is that true? How to
avoid those missing attribute errors? Simply ignore them?

Regards,
Kai

On 5 December 2010 15:35, Kai S. [email protected]
wrote:

Sometimes when I check the validation of an instance of that model
my_model.valid?
this foobar method (together with all my other after_initialize
methods) are called (why that? … the instance already exist.). The
main problem is that this leads to “missing attribute” errors.
It seems that the valid? method fetches and initializes objects from
the database without loading all attributes … is that true? How to
avoid those missing attribute errors? Simply ignore them?

That seems very strange. Are you absolutely sure that this what is
happening?
valid? cannot be fetching the object from the db as it must exist
before you call valid? Or are you talking about fetching associated
objects that are checked within valid?
Do you have a repeatable situation where you see it?
If so what happens for example if you use ruby-debug to break in just
before the call of valid? and then inspect the object and call valid?
from the console.

Colin

Colin

Sorry for the late answer … I had to simplyfy my model somehow to
really test that is not an other interaction that causes that problem.

That seems very strange. Are you absolutely sure that this what is happening?
valid? cannot be fetching the object from the db as it must exist
before you call valid? Or are you talking about fetching associated
objects that are checked within valid?
Do you have a repeatable situation where you see it?

Yes, I guess so.

My Model:

class MyModel < ActiveRecord::Base

after_initialize :init_token

validates :token, :uniqueness => true

def init_token
puts “init token”
if self.token.blank?
self.token = ActiveSupport::SecureRandom.hex(10)
end
end
end

And a simple case where it happens:
m1 = MyModel.create
m1.token.should_not be_empty
m2 = MyModel.new
m2.token.should_not be_empty
m2.token = m1.token
m2.valid? # where the exception is thrown (also “init token” is
called)

Full trace below.

If so what happens for example if you use ruby-debug to break in just
before the call of valid? and then inspect the object and call valid?
from the console.

I never used the debugger yet, but I will try to. But maybe you
already know of the above info what is going on here?

Kai

Error trace:
ActiveModel::MissingAttributeError: missing attribute: token
from /home/zeus/projects/myproject/app/models/my_model.rb:9:in
init_token' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activesupport-3.0.3/lib/active_support/callbacks.rb:415:in_run_initialize_callbacks’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activerecord-3.0.3/lib/active_record/base.rb:1453:in init_with' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activerecord-3.0.3/lib/active_record/base.rb:909:ininstantiate’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activerecord-3.0.3/lib/active_record/base.rb:467:in find_by_sql' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activerecord-3.0.3/lib/active_record/base.rb:467:incollect!’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activerecord-3.0.3/lib/active_record/base.rb:467:in find_by_sql' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activerecord-3.0.3/lib/active_record/relation.rb:64:into_a’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activerecord-3.0.3/lib/active_record/relation/finder_methods.rb:333:in
find_first' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activerecord-3.0.3/lib/active_record/relation/finder_methods.rb:122:infirst’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activerecord-3.0.3/lib/active_record/relation/finder_methods.rb:180:in
exists?' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activerecord-3.0.3/lib/active_record/validations/uniqueness.rb:39:invalidate_each’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activemodel-3.0.3/lib/active_model/validator.rb:154:in validate' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activemodel-3.0.3/lib/active_model/validator.rb:151:ineach’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activemodel-3.0.3/lib/active_model/validator.rb:151:in validate' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activesupport-3.0.3/lib/active_support/callbacks.rb:314:insend’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activesupport-3.0.3/lib/active_support/callbacks.rb:314:in
_callback_before_1265' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activesupport-3.0.3/lib/active_support/callbacks.rb:414:in_run_validate_callbacks’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activemodel-3.0.3/lib/active_model/validations.rb:212:in
run_validations!' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activemodel-3.0.3/lib/active_model/validations/callbacks.rb:67:inrun_validations!’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activesupport-3.0.3/lib/active_support/callbacks.rb:413:in
_run_validation_callbacks' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activemodel-3.0.3/lib/active_model/validations/callbacks.rb:67:inrun_validations!’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activemodel-3.0.3/lib/active_model/validations.rb:179:in valid?' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activerecord-3.0.3/lib/active_record/validations.rb:55:invalid?’
from (irb):103

I am not fully confident, but here is a suggestion. The call of valid?
must check for the uniqueness of token. To do that I think it may be
doing a find with a condition of the token value of the object being
validated, so that is the find_by_sql in the stack. Since the token
is not unique this does actually find a record. Possibly then the
found record is being instantiated, hence the call to init_token.

Yes, that is what I also guessed and the debugger seems to bring up.
But in my opinion it is not good behavior to instantiate a record in
that case.
I wonder why that can’t be avoided by simply checking the data without
creating the record (but I am no Rails pro).

But why it should fail I do not know.

The last SQL statement is:
SELECT grids.id FROM grids WHERE (grids.token = BINARY
‘mkp8ae2qbq’) LIMIT 1

And thats the cause of the problem as token is not selected here.
I am not sure if this is good behavior, that why opened a ticket to
discuss it further:
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6127-after_initialize-may-lead-to-missing-attribute-when-used-with-uniqueness-validation

Kai

On 7 December 2010 22:19, Kai S. [email protected]
wrote:

that case.
I am not sure if this is good behavior, that why opened a ticket to
discuss it further:

https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6127-after_initialize-may-lead-to-missing-attribute-when-used-with-uniqueness-validation

There is nothing wrong with rails running that query to check for an
existing token. It must do something similar. Note that it is only
picking up grids.id (not sure why it is grids as that does not seem
to bear any relationship to your model names but presumably you
understand that). The question is whether it goes on to attempt to
instantiate an object from that, which the query does not prove. You
could go a bit further by debugging into the failing method and seeing
what is there and how it got there.

I don’t think you answered my question as to whether the failure is
only in the tests and whether the app itself basically works.

Colin

On 7 December 2010 19:24, Kai S. [email protected]
wrote:

if self.token.blank?
m2.token = m1.token
already know of the above info what is going on here?
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activerecord-3.0.3/lib/active_record/relation.rb:64:in to_a' activerecord-3.0.3/lib/active_record/validations/uniqueness.rb:39:in activesupport-3.0.3/lib/active_support/callbacks.rb:314:in from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activesupport-3.0.3/lib/active_support/callbacks.rb:413:in _run_validation_callbacks’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activemodel-3.0.3/lib/active_model/validations/callbacks.rb:67:in
run_validations!' from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/ activemodel-3.0.3/lib/active_model/validations.rb:179:in valid?’
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/
activerecord-3.0.3/lib/active_record/validations.rb:55:in `valid?’
from (irb):103

I am not fully confident, but here is a suggestion. The call of valid?
must check for the uniqueness of token. To do that I think it may be
doing a find with a condition of the token value of the object being
validated, so that is the find_by_sql in the stack. Since the token
is not unique this does actually find a record. Possibly then the
found record is being instantiated, hence the call to init_token. But
why it should fail I do not know. I presume the table has got a field
called token? As I said I am not convinced. You may learn more by
studying the log to see what the query being run is. Also try the
debugger and you can find out what is being instantiated. Is it only
in tests that you have the problem or is the test throwing up a real
error in your app (such as the missing field in the table for
example).

Colin

I’m experiencing this error and it is happening during normal site
operation, not just in tests. Haven’t debugged it yet to see if it’s
attempting to instantiate an object. Will try…

In fact, it appears that activerecord/lib/active_record/base.rb (rails
2.3.8) is calling after_initialize on line 1687 on a record that only
has an
id set.