Blog

Handling deletes with Null Object Pattern in Ruby

Placeholder Avatar
Raluca Pintilii
April 7, 2019

I recently worked on a project for an Australian company who wanted to ensure they were GDPR compliant in how they handled personal data through their website and requests to delete user accounts.

If we want the user and all the associated data (blog posts, comments, etc) deleted, it is easy, we just need to add dependent: :destroy to the relations and Rails takes care of that for us.

But in this scenario, the client wanted to keep the user-related data after a user account was deleted.

General Data Protection Regulation (GDPR) background:

By way of background, since the GDPR was approved for the European Union in May 2018, it is more and more common for users to ask for their accounts to be deleted. The EU General Data Protection Regulation (GDPR) generally applies to the data processing activities of data processors or controllers where:

  • an establishment of the controller or processor is in the EU
  • the controller or processor is outside the EU, and the processing activities are related to:

Offering goods or services to individuals in the EU (irrespective of whether a payment is required)
Monitoring the behaviour of individuals in the EU, where that behaviour takes place in the EU (see Article 3).

So a US or Australian business of any size may need to comply if they have an establishment in the EU, if they offer goods and services in the EU, or if they monitor the behaviour of individuals in the EU. And in this day and age this encompasses a lot of companies.

Australian privacy law doesn’t include the “right to be forgotten” which is covered in the GDPR. So I had to ensure this was addressed in our client’s code.

Deleting Accounts:

Let’s say you have an awesome blog made in Ruby on Rails, where people can create accounts and add posts. What happens if someone who had posted articles on your blog, now asks for their account to be deleted?

There are several ways to deal with this, but in this article, I’d like to cover the null object way.

For this, let’s imagine we have a normal Ruby on Rails blog, with posts and users, and we need to set it up so users can be deleted. Let’s see what the code could look like, and what changes we need to add in order to be able to delete users and have the application still working properly.

On the model User, generically, we have something like this:

# app/models/user.rb

Class User < ApplicationRecord
  has_many :posts

  def full_name
    "#{first_name} #{last_name}"
  end
end

And the Post model should look like this:

# app/models/post.rb

Class Post < ApplicationRecord
  belongs_to :user

  def user_name
    user.full_name
  end
end

The issue to address is when users ask to delete their accounts, their posts become orphans, and unless we want to delete them, we have to deal with this situation.

If, on the page where we display the posts, we also display the author name, picture, or any other user attribute, then after we delete an account, these pages will be broken, since calling all these methods on nil will cause a method missing error.

So, how do we fix this using the Null Object Pattern?

We should start by adding a nulls folder under the app directory. From there add the following file:

# app/nulls/null_user.rb

Class NullUser
    
end

Then, on the Post model, change the relation to read the following:

belongs_to :user, optional: true

This will allow us to safely delete users with posts without getting any constraint errors.

If you have any custom validation on the user_id presence, please remove them as well.

Ok, so now that we can remove users with posts, we need to make sure the post pages (index, show) won’t break when we visit them due to calling methods on nil.

Let’s continue our example using the user_name method.

On the Post model file, add the following changes:

# app/models/post.rb

Class Post < ApplicationRecord
  belongs_to :user, optional: true

  def user_name
    user&.full_name || NullUser.new.user_name
  end
end

Now let’s implement the user_name method on the NullUser class:

# app/nulls/null_user.rb

Class NullUser
  def user_name
    'User deleted'
  end
end

Now when calling user_name on a post without user, it will return ‘User deleted’ instead of breaking. Feel free to customise the message you want to return.

But what if we have multiple methods on the Post model for retrieving its author details (eg. age, avatar, posts count, and so on)? To keep the code DRY, we can edit the NullUser class like this:

# app/nulls/null_user.rb

Class NullUser
  def method_missing(*args, &block)
    'User deleted'
  end
end

Then edit the Post model to the following:

# app/models/post.rb

Class Post < ApplicationRecord
  belongs_to :user, optional: true


  def user
    super || NullUser.new
  end
end

And that’s how you can use the null object pattern in Ruby on Rails to DRY your code and preventing method missing errors.