Read more

Merging two arbitrary ActiveRecord scopes

Henning Koch
June 08, 2011Software engineer at makandra GmbH

(Rails has a method ActiveRecord::Relation#merge that can merge ActiveRecord scopes. However, its behavior has never been clear, and in Rails 7 it still discards conditions on the same column by the last condition. We discourage using #merge!)

Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more Show archive.org snapshot

The best way to merge ActiveRecord scopes is using a subquery:

scope_a.where(id: scope_b)

It is a little less concise than #merge, but unambiguous.

Example

Assume a model where a deal has many documents:

class Deal < ApplicationRecord
  has_many :documents
end

class Document < ApplicationRecord
  belongs_to :deal
end

You also have a Consul Show archive.org snapshot power that specifies which deals and documents are accessible by a user:

class Power
  include Consul::Power
  
  power :deals do
    Deal.where(author_id: user.id)
  end

  power :documents do
    admin? ? Document.all : Document.where(visibility: 'public')
  end
end

Now there should be a screen to list documents pertaining to a given deal. When we implement the controller, we must filter the document list by two conditions:

  • Only show documents pertaining to the given deal (Deal#documents)
  • Only show documents that the user is allowed to see (Power#documents)

Combine both scopes with scope_a.where(id: scope_b):

class DealDocumentsController < ApplicationController

  def index
    @deal = current_power.deals.find(params[:deal_id])
    @documents = current_power.documents.where(id: @deal.documents) # <-- Here
  end

end

You can also merge scopes for different models: old blog post, card

Posted by Henning Koch to makandra dev (2011-06-08 13:50)