Here’s a little chunk of code I’ve been using recently. It gives you .add
and .add! methods on your has_many relationships that work similar to the << method. The difference is that .add sacrifices chain-ability in order to return the object being added to the collection.
Adding instead of concatenating
Using book.authors << Author.new(:name => ‘Geoff’) returns true or false forcing you to do something like book.authors << (geoff = Author.new(:name => ‘Geoff’)) to maintain a reference to the new object. Using .add you can do geoff = book.authors.add Author.new(:name => 'Geoff')
No really…add!ing
Perhaps even more helpful (especially in specs) is the add! method. add! acts just like add except it raises a RecordInvalid exception on failure. So you can do geoff = book.authors.add! Author.new(:name => 'Geoff') and have your execution (or your transaction) halt if Geoff messed up and wasn’t valid.
Blowing up your specs
This comes in handy a lot in your specs as exceptions will be displayed as the reason for failure. So if you were to add a validation to your Author class that caused all the Author’s you were adding to Book’s all over your specs to be invalid, you would see it (along with your validation’s helpful error message) when you ran your specs.
The goods
Just drop this in a file in your /lib folder and require it in an initializer.
module ActiveRecord
module Associations
class AssociationCollection < AssociationProxy #:nodoc:
# works just like << except returns the added records instead of self, so
# it's not chainable but allows you to do something like:
# @jim = Book.authors.add Author.new(:name => 'jim')
# (often saving a whole line of code!)
def add(*records)
self.<<(*records)
unarray_if_lonely(records)
end
# works just like add except raises an exception on error
def add!(*records)
raise RecordInvalid.new(*records) unless self.<<(*records)
unarray_if_lonely(records)
end
# if the given object is an array of size 1 then it returns the only
# element itself (w/ no array), otherwise it just gives back object
def unarray_if_lonely(object)
(object.is_a?(Array) && object.length == 1) ? object.first : object
end
end
end
end

{ 0 comments… add one now }