Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

merging scopes with STI models


On a client project recently, we ran into a domain problem that didn’t fit into the ActiveRecord standard conventions. The following is the thought process taken to get to our solution, so it gets detailed in some areas.

ActiveRecord has a great feature called Single Table Inheritance. It allows a model to have multiple types while using a single database table for the storage. Those type abstractions can each have their own validations, override base functionality, and specific abstraction functionality.

If your model has ever been littered with case statements checking if a User is a guest, admin, etc., you should take a look at STI.


The project had a based model that had many types and each abstraction had a scope of .active that defined what it meant to be active for that type.

class Person < ActiveRecord::Base; end

class FireFighter < Person
    where(has_helmet: true)

class PoliceOfficer < Person
    where(has_squad_car: true)


We needed to create an API endpoint that returned all active Person instances. This would require us to iterate through each child of Person and get all its current active members. Since we have Person model let’s give it a concept of .active that incorporates every active member of society in our domain.


We can extend Person to return an array of each active FireFighter and PoliceOfficer.

class Person < ActiveRecord::Base
  def +

One problem we have with this implementation is every time we add a new abstraction of Person we have to add to .active. Luckily, ActiveRecord STI comes with support for looking up a parent’s .descendants.

class Person < ActiveRecord::Base
    active_people = do |descendant|

This is pretty powerful. We can add Astronaut and any active astronauts will automatically be in array. This implementation will help satisfy our API endpoint requirements, but it does break useful ActiveRecord patterns.

Advance Solution

WARNING: Continue at your own risk. If you are content with the solution above stop, but if you want to see what can be done with Arel continue.

What if we want to chain scopes or extend the .active with pagination for our API? We cannot do this because easily because we are currently returning a Ruby array instead of an ActiveRecord::Relation. How can we modify .active to be an actual scope?

You might be thinking, ActiveRecord comes with the ability to merge scopes between models. Unfortunately, it does not work very well when merging scopes with STI models.

We ended using Arel (known for not being well documented) within our model. Each ActiveRecord::Relation is actually just an object with holding on to Arel values for different parts of an SQL statement — joins, froms, selects, etc. We are able to get the conditions for WHERE clause by looking at the ActiveRecord::Relation where_values.

class Person < ActiveRecord::Base
    conditions = do |d|


Our implementation takes the where_values from the .active scope from each descendant and does an SQL OR on them. ActiveRecord::Relation can take

# somewhere in a Rails console
=> SELECT "people".* FROM "people"  WHERE (
      ("people"."has_helmet" = 't' AND "people"."type" = "FireFighter")
      ("people"."has_squad_car" = 't' AND "people"."type" = "PoliceOfficer")

What does give us? We can now use as a normal scope, which allows us to append any conditions on to it.

=> []
=> []

  1. javio says:

    I want to confirm that, your post is so interesting. It contains a lot of important and useful information. I got a lot of great things. Thank you so much!
    is it just me

  2. javio says:

    Hi, this is a very interesting article and I have enjoyed read­ing many of the arti­cles and posts con­tained on the web­site, keep up the good work and hope to read some more inter­est­ing con­tent in the future. I got a lot of useful and significant information. Thank you so much.
    down or just me

  3. Harald says:

    Splendid idea and nice implementation. Thank you so far!

    But I run now into this problem: may work, but I have problem on a has_many-relation.

    Say, House has_many :persons will crash deep inside AR query_method’s build_arel method…

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *