has_many :through, scoped to a value on the other side of the :through relationship


Here goes another round of wrapping my head around what rails associations are looking for.

This time, I’m trying to scope a collection on the other side of a has_many :through to an attribute on that model.

Contrived exhibit A:

class Gym < ActiveRecord::Base
  # gym has a boolean "open"
end

class TrainerGym < ActiveRecord::Base
  has_one :trainer
  has_one :gym
end

class Trainer < ActiveRecord::Base 
  has_many :trainer_gyms
  has_many :gyms, through: :trainer_gyms
end

I want to create an association of `open_gyms` that my `Trainer` is connected to.

The end result looks like this for the `Trainer` model:

class Trainer < ActiveRecord::Base 
  has_many :trainer_gyms
  has_many :gyms, through: :trainer_gyms

  has_many :open_trainer_gyms ->{ includes(:gym).where(gyms: {open: true}) }, class_name: TrainerGym
  has_many :open_gyms, through: :open_trainer_gyms, source: :gym
end

The scope:

->{ includes(:gym).where(gyms:{open: true}) }

`includes` the association `gym` from `TrainerGym`. The `where` clause specifies the table `gyms` and the nested hash is the condition on `gyms` to match (`{open: true}`).

The `class_name: TrainerGym` tells the association `open_trainer_gyms` that it is a collection of the model `TrainerGym`, since that cannot be determined by reflection/inflection magic.

The `open_gyms` association piggy-backs the `open_trainer_gyms` association, but the `:through` relationship needs to be told that the `gym` association on `TrainerGym` is how it gets to the other side, since `open_gyms` can't be converted to `gym` automatically by the reflection and inflection magic.


%d bloggers like this: