Constructors in Ruby are not guaranteed to be called

Today, Caius made a discovery that shocked me.

He had a class, descending from ActiveRecord::Base, with a custom constructor (initialize method). To debug it, he had the constructor raise an exception. In the console, Thingy.new(params) raised the exception as expected. But wotsit.thingies.find_by_field(value) did not. Even though it was instantiating an instance of Thingy and returning it.

“It must not be calling the constructor” he said.
“Rubbish” said I, “it’s a constructor. Constructors are always called. That’s the point of them”.

But as he dug deeper it certainly looked like the constructor wasn’t being called.

And then he found an article explaining that you should never rely on things being set up on in an Active Record constructor. Mainly because Active Record uses allocate to instantiate associated objects. And what is this mysterious allocate? Why explains it all.

To be honest, I’ve got mixed feelings about this. I can see the use of “allocate” – why’s example of marshalling an object makes sense (I’m slightly less sure about the way that Active Record uses it to load associations). But, to my mind, the definition of a constructor is “the code that is always called when an object is created”. So maybe I should just stop thinking of initialize as a constructor and more as an initialiser.

Tags: , ,

This entry was posted on Wednesday, June 3rd, 2009 at 9:28 pm and is filed under General, Ruby on Rails and Software Development. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

3 Responses to “Constructors in Ruby are not guaranteed to be called”

  1. Paul Horsfall Says:

    I was aware of, or at the very least not surprised by this. And for whatever reason, I don’t think I’ve ever thought of Ruby’s initialize as a constructor. If you were to override YourClass.new and not call super you’d see something similar I suppose, although you wouldn’t get a new instance either in that case would you?

    It would be interesting to know why ActiveRecord calls allocate directly though, it seems as though you’d only do so if you specifically wanted an uninitialised instance, but maybe that’s the point.

  2. Rahoul Baruah Says:

    It looks to me like #allocate is for situations where an object’s state is about to be specified by some other means (such as when deserialising from an external source) – so whatever happens in #initialise would be overridden.

    Which kind of makes sense, as far as ActiveRecord is concerned, IF you hold that an ActiveRecord model is merely a thin wrapper around the state in the database.

    Unfortunately, because we also use ActiveRecord models as a holder for business logic (which may require _temporary_ state) the assumption that all a model’s state is in the database is invalid. Another flaw in the “mixing-business-logic-and-persistence” model that ActiveRecord follows.

  3. 3hv » Blog Archive » Default values in your models Says:

    [...] default value code into your initialize method, it turns out that, under certain circumstances, it may not be called. Instead, move it to [...]