Ruby Bang (!) Methods
Published: 2023-05-09 19:16:36

---
title: Ruby Bang (!) Methods
published: true
date: 2023-05-04 12:54:17 UTC
tags: 
canonical_url: https://blog.unathichonco.com/ruby-bang-methods
---

Ruby is a powerful and elegant programming language, known for its readability and expressiveness. [Optimised for developer happiness](https://rubyonrails.org/doctrine#optimize-for-programmer-happiness). One of the more unique aspects of Ruby is its "bang"(!) methods. As a newcomer to Ruby, you might find yourself wondering about the purpose of these methods and how to use them. In this article, we will explore Ruby's bang methods, the naming convention, and their use in a Ruby on Rails project.

## **What are Bang Methods?**

In Ruby, bang methods are simply methods that have an exclamation mark (!) at the end of their names. The bang is a naming convention to signify that the method has some potentially surprising or dangerous behaviour compared to its non-bang counterpart.

## Mutating bang methods

Generally, bang methods mutate/modify the object they are called upon, while their non-bang counterparts return a new object with the desired changes.

Here are a few examples of bang and non-bang method pairs in Ruby:

1. `String#upcase` and `String#upcase!`

2. `Array#sort` and `Array#sort!`

3. `Array#uniq` and `Array#uniq!`

Let's take a closer look at these examples to understand the differences between bang and non-bang methods.

### **Example 1: String#upcase and String#upcase!**

`String#upcase` returns a new string with all characters in uppercase, leaving the original string unchanged. In contrast, `String#upcase!` modifies the original string in-place, converting all characters to uppercase.

```ruby
text = "hello, world!"

uppercase_text = text.upcase
puts uppercase_text # => "HELLO, WORLD!"
puts text # => "hello, world!"

text.upcase!
puts text # => "HELLO, WORLD!"

```

### **Example 2: Array#sort and Array#sort!**

`Array#sort` returns a new array with its elements sorted, while `Array#sort!` sorts the original array in-place.

```ruby
numbers = [5, 3, 1, 4, 2]

sorted_numbers = numbers.sort
puts sorted_numbers.inspect # => [1, 2, 3, 4, 5]
puts numbers.inspect # => [5, 3, 1, 4, 2]

numbers.sort!
puts numbers.inspect # => [1, 2, 3, 4, 5]

```

### **Example 3: Array#uniq and Array#uniq!**

`Array#uniq` returns a new array with duplicate elements removed, while `Array#uniq!` removes duplicate elements from the original array in-place.

```ruby
repeated_numbers = [1, 2, 2, 3, 3, 3]

unique_numbers = repeated_numbers.uniq
puts unique_numbers.inspect # => [1, 2, 3]
puts repeated_numbers.inspect # => [1, 2, 2, 3, 3, 3]

repeated_numbers.uniq!
puts repeated_numbers.inspect # => [1, 2, 3]

```

## Error-raising bang methods

In a Ruby application, bang methods are also prevalent for denoting error-raising methods. You can find common use cases in a library such as ActiveRecord, the built-in Object-Relational Mapping (ORM) framework for Ruby on Rails that handles database operations. Some of the most common ActiveRecord bang methods include:

1. `ActiveRecord::Base#save` and `ActiveRecord::Base#save!`

2. `ActiveRecord::Base#create` and `ActiveRecord::Base#create!`

3. `ActiveRecord::Base#update` and `ActiveRecord::Base#update!`

4. `ActiveRecord::Base#destroy` and `ActiveRecord::Base#destroy!`

Let's examine these ActiveRecord examples to understand their usage and differences in a Ruby on Rails application.

### Example 1: ActiveRecord::Base#save and ActiveRecord::Base#save!

`save` and `save!` are instance methods used to persist an ActiveRecord object to the database. The non-bang method, `save`, returns a boolean value indicating whether the record was saved successfully or not. If there are any validation errors, it returns false.

On the other hand, `save!` raises an `ActiveRecord::RecordInvalid` exception if the record is invalid, halting the execution flow. This method is useful when you want to ensure the record is valid and saved; otherwise, an exception should be raised.

```ruby
class User < ActiveRecord::Base
  validates :email, presence: true
end

user = User.new(email: "")

# Using save
if user.save
  puts "User saved!"
else
  puts "User not saved. Errors: #{user.errors.full_messages.join(', ')}"
end

# Using save!
begin
  user.save!
  puts "User saved!"
rescue ActiveRecord::RecordInvalid => e
  puts "User not saved. Errors: #{e.record.errors.full_messages.join(', ')}"
end

```

### Example 2: ActiveRecord::Base#create and ActiveRecord::Base#create!

`create` and `create!` are class methods used to create and save a new ActiveRecord object to the database in a single step. Similar to `save`, `create` returns the newly created object, whether it's valid or not. If the object is invalid, it won't be persisted to the database.

In contrast, `create!` raises an `ActiveRecord::RecordInvalid` exception if the object is invalid, making it suitable for situations where you want to ensure the record is valid and created; otherwise, an exception should be raised.

```ruby
# Using create
user = User.create(email: "")
if user.persisted?
  puts "User created!"
else
  puts "User not created. Errors: #{user.errors.full_messages.join(', ')}"
end

# Using create!
begin
  user = User.create!(email: "")
  puts "User created!"
rescue ActiveRecord::RecordInvalid => e
  puts "User not created. Errors: #{e.record.errors.full_messages.join(', ')}"
end

```

### Example 3: ActiveRecord::Base#update and ActiveRecord::Base#update!

`update` and `update!` are instance methods used to update the attributes of an ActiveRecord object and save it to the database. `update` returns a boolean value, indicating whether the record was updated successfully or not. If there are any validation errors, it returns false.

`update!`, like `save!` and `create!`, raises an `ActiveRecord::RecordInvalid` exception if the record is invalid, making it suitable for situations where you want to ensure the record is valid and updated; otherwise, an exception should be raised.

### Example 4: ActiveRecord::Base#destroy and ActiveRecord::Base#destroy!

`destroy` and `destroy!` are instance methods used to delete an ActiveRecord object from the database. The non-bang method, `destroy`, returns the destroyed object if the deletion is successful. If the object has any `before_destroy` callbacks that halt the destruction process, it returns false.

In contrast, `destroy!` raises an `ActiveRecord::RecordNotDestroyed` exception if the object cannot be destroyed due to any `before_destroy` callbacks. This method is useful when you want to ensure the record is deleted; otherwise, an exception should be raised.

```ruby
class User < ApplicationRecord
  before_destroy :check_admin

  private

  def check_admin
    throw :abort if email == "admin@example.com"
  end
end

user = User.create(email: "admin@example.com")

# Using destroy
if user.destroy
  puts "User destroyed!"
else
  puts "User not destroyed."
end

# Using destroy!
begin
  user.destroy!
  puts "User destroyed!"
rescue ActiveRecord::RecordNotDestroyed => e
  puts "User not destroyed."
end

```

In this example, the `User` model has a `before_destroy` callback that prevents the deletion of a user with the email "[admin@example.com](mailto:admin@example.com)". When using `destroy`, the method returns false if the user cannot be deleted. However, when using `destroy!`, an `ActiveRecord::RecordNotDestroyed` exception is raised if the user cannot be deleted.

### Conclusion

Understanding the differences between bang and non-bang methods in Ruby can empower you to write more efficient and maintainable code. Bang methods are more aggressive, often modifying objects in place or raising exceptions when operations fail. Non-bang methods are more lenient, returning modified copies of objects or allowing for flexible error handling. Knowing when to use each type of method can be useful in creating robust and reliable applications.

So, the next time you come across a bang method in Ruby, don't be afraid to use it but also be aware of its potential side effects. Happy coding!

🧑🏾🚀🚀