joestelmach.com

Persisting Object Graphs With Active Record

If given a choice between simple and complex, Rails will always lean towards the simple (actually, it crashes into the simple side of things.) Most of the time this is a nice, refreshing change from the J2EE world, whose committee seems to get off on adding complexity into every nook and cranny of the J2EE spec. However, sometimes there are real complexities in the problems we are trying to solve. Consider the following domain model:

example domain model

Now, let's suppose you would like to display the rendered html version of this entire object graph to the user on a single page with only one form. If you've done any rails development in the past, you're probably thinking "It can't be that bad". After all, Rails almost enforces that you follow domain-driven design, so how hard can it be to satisfy this requirement? update_attributes to the rescue! Take a look at the RDoc:

Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will fail and false will be returned.

Sounds great right? But wait - what if the method fails? Can we still access the error messages as if i called the save or valid? method directly on each object? Will the framework know which properties have errors associated with them to insert the proper 'fieldWithError' class names while rendering the response? Unfortunately, the answer is No. The update_attributes method is therefore worthless to me, and exists as nothing more than an absolute tease. However, I'm sure the Rails core developers will argue that they were just 'keeping it simple'. I applaude that. But I still have to satisfy this requirement that goes against the seemingly conventional Rails wisdom of a single object per page.

So what options are we left with here? I could refactor my interface to break the object graph into multiple screens, but that would add lots of complexity to my user interface, which I believe should remain simple at all costs (even if it means adding complexity to the back-end.) So refactoring the interface is out. I suppose I could just walk the object graph and populate and save each object directly, collecting any error messages that arise as we make our way to the bottom of the graph. Populate and save you say? That's what update_attributes is for. So maybe we can validate each object on our way down, and then use the update_attributes on each object. Sounds good right? Wrong again. update_attributes maintains its own transaction, which means that once an object is updated, it cannot be rolled back if another object's validation or save fails. The entire group of saves needs to be wrapped in a transaction. It looks like the optimal solution in this case is to use the attributes= method to populate the attributes from the parameters hash and the save method to do the persisting, all within one transaction.

This solution will work (and does work for a couple apps I have written.) However, the complexity of the save operation increases for each entity you add to your domain model (luckily, this seems to be only a linear increase.) But seriously, I can't help but to think there's a better way to do this...

Comments
blog comments powered by Disqus