I’m running into a bit of a conundrum when it comes to designing objects in a Ruby on Rails application, and thought I’d toss it up here for discussion. I have an application with two persistent objects,
Address. Address is a child of user, and user holds a pointer to their current address (so that a history of addresses can be maintained. So users has an ID column and a current address ID coumn, and addresses has an ID column and a user ID column. As you might imagine, this leads to a bit of a chicken and egg problem.
I have one “new user” form that enables someone to create a user. If everything goes well, when the user submits the form, a new User object will be created and a new Address object will be created. The question is, how do I design the method that saves the two objects? Here’s why this question is a little bit complicated:
- I don’t want to save anything if there are problems with either of the objects. So if the address is invalid, I don’t want to save the user.
- User ID is a required field for the address object, so in order to save the address, I have to save the user object first.
- One purpose of this process is to save the objects to the database if they are valid. The other is to record the problems with the objects if they are not valid, so that they can be presented to the user for correction. One option would be to validate both objects before trying to save either of them and then save only if both are valid. Unfortunately, the address will always be treated as invalid if the user has not been saved.
- Once the user and address have been saved, you have to go back and save the user again to populate he user’s current address ID with the newly assigned ID fof the address.
Currently the most straightforward to make this work is to instantiate and save the user object and then instantiate and save the address object. If you try to instantiate the address object and link it back to the user object, you get a stack error because ActiveRecord recurses through the circular relationships between the user and the address. The problem here is that once you’ve created the user object, if there’s an error in the address object, the user has already been saved. That breaks the expected experience in that the data will be partially saved.
So the question is, what’s the easiest and most idiomatic Rails way to handle this situation? Here are the options as I see them:
- Wrap all of the database operations in a transaction. If the save of the address fails, roll back the transaction and wipe out the newly created user.
- Muck around with the validation so that you can validate the address before you save the user. Then validate both the user and the address without attempting to save them.
- Split the form into two forms, one which creates the user and another which allows them to submit their address.
- If the address validation fails but the user validation succeeds, report to the user that the user object was saved but ask them to correct their address.
There are probably other options as well. I’m still trying to determine which makes the most sense. Any ideas?
Update: I’ve posted how I solved this problem.