Words and Code

One writer’s journey from words to code.

Embrace Your Inner Paranoia: Using Acts_as_paranoid

#technicaltuesdays, rails

Yesterday, I started my very first job as a software engineer! It’s been super exciting, slightly terrifying, and sometimes overwhelming. I think one of the trickiest parts of starting as a new engineer at a company is the onboarding process.

It might seem kind of scary, but if you think about it, it’s actually pretty fun. You get to dive down a rabbit hole and look at production code that you didn’t write. It’s getting a new puzzle that you haven’t solved yet: you try to figure out how one thing connects to another, where modules and methods exist, how things are namespaced, not to mention learning about new frameworks and gems. I feel like I’m entering into new dimensions and travelling through a space-time continuum or something. This also might be attributed to the fact that I’ve been listening exclusively to the Interstellar soundtrack for the past two days, but whatever – you get the point.

My favorite part of the onboarding process is how much I’ve been learning. Every new class or module definition brings a new piece of the puzzle that I’ve never seen before, but can’t wait to learn about. It’s kind of crazy that I get paid to read and learn all day, every day – that’s the dream, right?

Anyways, all of this is to say that I’ve found and learned about some cool stuff! For example this little ditty: acts_as_paranoid. I saw this in a class definition and my first thought was literally: Damn, that’s a great name for a validation! But as it turns out, it’s not actually a validation – it’s Rails magic!

Start acting paranoid!

Okay, I lied: acts_as_paranoid not actually magic – it’s a Rails ActiveRecord plugin. But it’s still pretty magical, you guys! So, what does it do? Well, it helps you be less paranoid about deleting stuff by accident (hence the name). Essentially, acts_as_paranoid allows you to make soft deletes in Rails. That means that it gives you the flexibility to delete an object without actually deleting it from the database.


Does this sound like black magic yet? Just wait, you’re about to see some real magic.

So…how do I starting acting paranoid?

In my example, I’ll be implementing acts_as_paranoid on some Book objects in my eCommerce bookstore app. Using acts_as_paranoid is relatively simple. You can break it down into two simple steps:

First, add acts_as_paranoid to your class definition:

1
2
3
class Book < ActiveRecord::Base
  acts_as_paranoid
end

Then, add a column to the database for that class called deleted_at, which is set to a datetime format:

1
2
3
4
5
class AddDatetimeToBooks < ActiveRecord::Migration
  def change
    add_column :books, :deleted_at, :datetime
  end
end

Ok, I’m getting paranoid now – how does this work?

So, we have another column in our Books table that has a deleted_at column with a type of :datetime. Now, this is where the magic happens: the acts_as_paranoid plugin actually overrides ActiveRecord’s find, count, and destroy methods. So now when we call the destroy method on a Book object, instead of actually deleting the object, the object’s deleted_at field will be set to the current date and time.

And, if we call the find method on all of our Book objects, the one we just “deleted” won’t show up! Instead, only the objects that don’t have a value in their deleted_at column will render.

So, calling @book.destroy doesn’t delete a row from the database; it actually just updates the row by giving a datetime to the object’s deleted_at field. If you’re into SQL queries, this is what’s going on:

UPDATE books SET deleted_at = '2015-01-27 19:36:16' WHERE (id = 50)

The Book object with an id of 50 isn’t actually deleted from the database, even though it will appear so in all of our views, and to our users/admins.

But who needs soft deletes, anyways?

I actually didn’t realize the use case for soft deletes at first. But it turns out that they are incredibly helpful when building out large, more complicated Rails applications.

It’s important to remember that Ruby is an object-oriented programming language. Any object that has an association with another object inherently relies upon it. In my bookstore app example, a Book object would belong to a Order object, and also be associated with a User object of some sort. If you think about the appliation on a broader, less granular level, you might realize that deleting any given Book object could actually have serious repercussions.

For example, you might want to see a Book object that was ordered in the past, even if that Book has since been deleted from a store. Perhaps you want to view the details of a Shipment object, even if that shipment was cancelled. Or, you might want to see an order that was placed by a User who may have deactivated their account months ago.

The acts_as_paranoid plugin helps you access all of this information, without keeping you up at night, wondering whether you deleted the wrong row from the database. Because honestly, who has time for that? Not this kitty, for sure:

tl;dr?

  • The acts_as_paranoid plugin modifies ActiveRecord methods and allows you to implement soft deletes on your Ruby objects. Just remember to include it in your class definition and add a deleted_at column to your migration, with a type of datetime.
  • Want to see another example of disabling records using acts_as_paranoid? Check out this blog post.
  • To read more about acts_as_paranoid and its many caveats, check out the easy-to-follow documentation.