Rails 2.1 gotcha with has_one

9 Jun 2008

I just updated the SaaS Rails Kit for Rails 2.1, and added a couple new payment gateways, and I thought I’d mention a behavior change in Rails 2.1 that might catch you unawares.

The new behavior in Rails 2.1 for has_one is that Rails will now automatically validate the association for you, which can cause you some problems if you already were validating the association yourself, as I do with the SaaS Rails Kit. In my case, I validate the subscription associated with a new account, but only if everything is valid with the account first, as I don’t need to hit the gateway with your credit card info if your account info is invalid. When I started to upgrade the Kit to Rails 2.1, my spec that checks that behavior started failing, as it got back the generic “is invalid” error message, rather than the error message I was generating inside the Subscription model.

So, I thought I’d put this blog post out there to hopefully save some people some time wondering what’s going on, and also to give you a good example of why thoroughly testing your code is a good idea. :)


Actions

Informations

4 responses to “Rails 2.1 gotcha with has_one”

Luke Redpath (12:20:21) :

Hi Ben, just to let you and others know, I encountered this bug (yes, I consider it a bug) whilst upgrading Reevoo.com to Rails 2.1 (yes, I caught it with failing tests too…never underestimate their value – I wouldn’t have dared attempt an upgrade without them).

Fortunately, I found a patch which should hopefully be committed to core which adds a :validate option to has_one, allowing you to disable this behaviour with :validate => false.

As to why I consider this a bug; I consider this a bug because it is an undocumented (from what I can see) change of behaviour that seems quite unnecessary given the presence of validates_associated. If you want to validate your associated models on save, validates_associated gives you the ability to do this so I don’t see why it should be done automatically, potentially breaking people’s apps. It’s an element of business logic that should not be assumed by the framework.

I would personally like to see this either a) removed or b) enabled by the above :validate option (and turned off by default. Either way, this should be documented and made very clear, which is why I have submitted a ticket for it: http://rubyurl.com/sBc5 – I’d appreciate some +1s.

Incidentally, this change was applied to belongs_to as well but was subsequently rolled back, seemingly because it broke one of the core team’s apps.

Ben (12:43:53) :

I agree that this change shouldn’t have been made in this way, changing the default behavior without (at least) an announcement of the change. Encountering it made me wonder what else is lurking in the 2.1 release.

I’d be fine either with a :validate option which defaults to false, or just reverting this and going back to validates_associated.

aotianlong (09:56:43) :

this problem let me crazy.

thank you very much.

Jean-Baptiste (02:12:51) :

People are talking about this “bug” for has_one in some places on the web.

But what about has_many?

I realize that associated objects (associated with build on a new record) are validated two times (strange?) when the parent object is saved. The first time they are validated, the parent is not saved yet. Hence, if I want to validate the presence of parent in associated objects, I got validation errors. This broke a important part of my business logic.

Today, the only thing I have to do is appending ”:validate=>false” in every “has_many” of my projects…

Any idea?