Composing SQL queries with Arel

I believe you agree that SQL queries as a long strings are not very readable. Fortunately, in the Ruby on Rails world there is an Arel. For context, let’s say we have the following models:

class Resource < ActiveRecord::Base
  belongs_to :environment
  belongs_to :permission
end

class Environment < ActiveRecord::Base
  has_many :resources
  has_and_belongs_to_many :permissions
end

class Permission < ActiveRecord::Base
  has_many :resources
  has_and_belongs_to_many :environments
end

As we see, the Resource belongs to the Environment and to the Permission. There is also a many-to-many relationship between Permission and Environment. The Permission defines which environments are allowed. So, the Resource is only available when its Environment is assigned to its Permission.

class Resource < ActiveRecord::Base
  #  (...)
  def available?
    permission.environment_ids.include?(environment_id)
  end
end

Our task is to get a list of all available resources. Arel comes in handy here.

Resource.joins(permission: :environments)
        .where(
          Environment.arel_table[:id]
                     .eq(Resource.arel_table[:environment_id])
        )

By using joins we got all the resources that have at least one allowed environment. Next, we had to filter these resources where the environment_id matches the id of the allowed environment. This gives us the following SQL query:

SELECT "resources".* 
FROM "resources" 
  INNER JOIN "permissions" 
    ON "permissions"."id" = "resources"."permission_id"
  INNER JOIN "environments_permissions" 
    ON "environments_permissions"."permission_id" = "permissions"."id" 
  INNER JOIN "environments" 
    ON "environments"."id" = "environments_permissions"."environment_id" 
WHERE "environments"."id" = "resources"."environment_id"

Arel version looks simpler, right? You can use Scuttle to translate your SQL to Arel.

#ruby #rails #arel #sql