70

I'm trying to figure out when to use the bang! versions for saving and updating records? I've read and heard that you don't need them if you're just saving one record or updating a single attribute, if you're confident nothing should go wrong, or to always use them outside of a controller. I guess I'm paranoid about having multiple things getting saved then something fails then there is incomplete data in the DB. The current Rails project I'm working on is over 50% complete and currently doesn't contain any bangs. I have some custom methods I'm calling in models that update or create multiple records and worry if they should be in some sort of transaction.

Sorry if this seems scattered but I'm just trying to figure how to use the saving capabilities in ActiveRecord correctly and make my life easier and little more stress free in the end. Thanks for your time.

CalebHC
  • 4,885
  • 3
  • 34
  • 40
  • Possible duplicate of [Why are exclamation marks used in Ruby methods?](https://stackoverflow.com/q/612189/608639) – jww Sep 17 '18 at 18:12

3 Answers3

88

The main difference is how failed saves are handled. When updating an ActiveRecord class the ! version will raise an exception if the record is invalid.

I recommend reading the docs here - http://api.rubyonrails.org/classes/ActiveRecord/Base.html

Using transactions might also be something worth looking into - http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html

Andy Gaskell
  • 30,100
  • 5
  • 70
  • 74
  • 21
    Great for pointing the key difference between the two, unlike the selected answer. – ifightcrime Mar 06 '13 at 19:40
  • Thanks for pointing to the use of transactions, they often seem overlooked. – ki4jnq Aug 12 '14 at 13:29
  • 4
    +1. Might also be worth noting that the non-bang methods will silently fail in transactions, so always use `save!`, `update_attributes!`, etc. when your code is wrapped in a transaction. – NM Pennypacker Nov 29 '16 at 13:11
  • @NickM So that the transaction is automatically rolled back, I presume? Whilst the silent failure will make it go through? – Magne Oct 19 '17 at 14:34
  • 2
    @Magne Yep, if the exception isn't raised the transaction will still complete – NM Pennypacker Oct 19 '17 at 14:46
57

Generally you want to use the non-bang versions in your controllers. This allows logic like this:

def update
  @model = Model.find params[:id]
  if @model.update_attributes params[:model] #returns true of false
     # handle success
  else
     # handle failure
  end
end

I find myself using the bang versions a lot in tests when I want to make sure I know if something doesn't validate, and isn't saved. I've definitely wasted time debugging tests that were failing because of changed model validations, which would be obvious if I used the bang versions.

e.g.

it "should do something" do
   m = Model.create! :foo => 'bar' # will raise an error on validation failure             
   m.should do_something
end

In terms of not having invalid data in the database, you should be handling this with ActiveRecord validations (e.g. validates_presence_of :user_id), or defining your own validate method in the model. (http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html) This should prevent saves from occurring if your data isn't valid. If you're really paranoid you can add some constraints to your database. Check the ActiveRecord::Migration docs for how to set up unique indexes and other database constraints in your migrations.

Also in my experience you want to avoid using any custom save or create method whenever possible. If you re-implement functionality included in ActiveRecord you end up paying a price down the road. http://matthewpaulmoore.com/post/5190436725/ruby-on-rails-code-quality-checklist has more to say on this.

evanrmurphy
  • 1,726
  • 1
  • 16
  • 18
samg
  • 3,340
  • 1
  • 22
  • 25
7

What ! (bang) means for update_attributes and save is:

"Raise an exception upon failure" rather than "Return false upon failure"

https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update-21 https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-save

What ! (bang) means for create is:

"Raise an exception upon failure" rather than "Return the resulting object upon failure" https://api.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-create-21

Tim Heilman
  • 151
  • 1
  • 6