Save vs. Save!
When learning about ActiveRecord objects, two methods usually taught are ActiveRecord::Base#save and ActiveRecord::Base#create. For beginners–especially those new to Ruby as a language–these methods are fine and unobtrusive. I'm finding myself using their counterparts, ActiveRecord::Base#save! and ActiveRecord::Base#create!, respectively as time goes on, for a few reasons.
First, a bit of an explanation of what the exclamation mark at the end of a method tells you, since it's generally different in Ruby and in ActiveRecord. In general, when talking about built-in Ruby methods, the exclamation mark tells you, "This method will return the object it was passed, edited in place." Take this example:
>> str = ' A phrase with spaces ' => " A phrase with spaces " >> str.strip => "A phrase with spaces" >> str => " A phrase with spaces " >> str.strip! => "A phrase with spaces" >> str => "A phrase with spaces"
The String#strip method removes leading and trailing spaces from a string and returns the edited string, without editing the string object itself. Conversely, the String#strip! method also removes leading and trailing spaces from a string, but it also edits the string object itself, then returns it. You can see that subsequently displaying the str object after the call to str.strip! returns the stripped string; the string object has been edited and saved, as opposed to just edited and returned, with str.strip.
ActiveRecord handles the exclamation mark differently than Ruby, generally speaking. When we think of methods with an exclamation mark at the end in methods such as ActiveRecord::Base#save! and ActiveRecord::Base#create!, what we are told is, "Try to save or create this ActiveRecord object, but if you can't for some reason (usually validation), raise an exception." In contrast, the ActiveRecord::Base#save and ActiveRecord::Base#create methods will return true if the object was successfully saved or created, and false if not.
Why would you choose to have ActiveRecord raise an exception when trying to create an object? Here are a couple reasons:
- Using a Rails plugin like exception_notification, you can be alerted by email if an object was not saved or created for some reason. If you have code that's silently returning false when trying to save an object, you might not notice an issue with your code. This can lead to database inconsistencies and unhappy users.
- When testing, sometimes you have a method handle a lengthy process, such as importing a record from an external source, massaging the data, and creating various objects in the database to correspond with the incoming data. If any one of these things fails, your encapsulating method might not let you know, but if you're raising exceptions, you can do something like this:
assert_nothing_raised do
Person.import_from_external_source
end
If anything fails, you'll get a test failure and you can delve deeper into your code to figure out what's going wrong.
I use ActiveRecord::Base#save! and ActiveRecord::Base#create! when I want to make sure an object will be created, and their non-exclaiming counterparts when it's not critical that the records be updated or created (e.g., if we're importing data for an existing person and an "Email" record already exists for the person for the specified email address, we can just test for failure based on our validations and move on, without re-importing the record).