Validating a has_and_belongs_to_many collection/association BEFORE it gets saved

Hi all,

I'm wondering if there is any way to add validation to a "has_and_belongs_to_many collection attribute" (Room.people in my example) so that it limits the number of objects in the collection to some maximum?

I'm running into two problems here:

1. When I do the assignment to my collection (room.people = whatever), it IMMEDIATELY saves it in my join table (people_rooms) rather than waiting until I call room.save. (Shouldn't there be some way to explicitly defer the save?)

2. I thought maybe I could get around this by using habtm's :before_add option ... but it seems that any errors added there end up being ignored/lost. Plus, the only way to abort the save seems to be to raise an exception ... (which I don't really want to do).

Hopefully an example will help illustrate the problem. Here's the simplest test case I could come up with...

I believe what may be throwing you off is the fact the (3rd) people
instance added to the room object is cancelled due to the thrown
exception in before_adding_person(person) method.

In other words, you execute room.people << people(:person3) which
causes the Room#before_adding_person method to execute prior to adding
this to the association collection. In the process of the code
executing it decides too many people are in the room and therefore
throws an exception cancelling the 3rd person added to the person
collection.

You can verify the above by executing assert_equal 2,
room.people.size

As for why you can save the Room object (via room.save) I believe
that's due to the fact that well, you still only have 2 people in the
Room#people collection.

The last thing you are pointing out is the room.errors.on(:people) is
empty. This is a guess - I assume this is due to the fact the
successful completion of the room.save method clears out the error
collection. That would make sense to me as I would not want an error
hanging around until I have to force it to be cleared. It should exist
only to reflect the success/failure of the prior operation.

Thanks, your explanation makes a lot of sense...

And I think you're right about the save() method clearing out the errors collection each time. (I see a call to errors.clear in the source for ActiveRecord::Validations#valid?() ... I'm *assuming* valid?() gets called each time save() is called.)