Named scopes outside of their own model

Named scopes are a great way to generate complex SQL queries in a DRY and simple fashion. There is a great writeup on them in Rails AntiPatterns, as well as in the Rails Guide.

I’ve found it is not always easy to get scopes to work when there are lots of complicated joins, and I want to filter on one of the middle, joined models, as opposed to the model that I will eventually be actually pulling out of the database. Here is one of those cases:

# app/models/mission.rb
class Mission < ActiveRecord::Base
  scope :upcoming, -> { ## a lot of super complicated joins and filters ## }
end

# app/models/company.rb
class Company < ActiveRecord::Base
  has_many :missions, through: :supplier_assignments
  has_many :clients, through: :missions, source: :client
end

What I would have liked to do is do something like this:

# app/models/company.rb
  def upcoming_clients
    missions.upcoming.clients.distinct
  end

But none of the variations I tried (all sorts of fancy joins, squeel sifters, etc) worked.

So I hacked a bit on the `Mission` model:

# app/models/mission.rb
# Get the raw sql from the 'upcoming' named scope, and keep only the 'where' clause
UPCOMING_MISSION_SQL_WHERE_CLAUSE = self.upcoming.to_sql.partition(' WHERE ').last

And now in the `Company` model I can do this:

# app/models/company.rb
  def upcoming_clients
    clients.where(Mission::UPCOMING_MISSION_SQL_WHERE_CLAUSE).distinct
  end

Anyone know a better way of doing this??? I would love to hear it if so.

NOTE: There IS a better way of doing this, using #merge, as such:

# app/models/company.rb
  def upcoming_clients
    clients.merge(Mission.upcoming).distinct
  end
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s