Sometimes having a custom method on an association would be handy. Well good news, you can do that with Rails!
I recently used this to reorganize and simplify some deeply nested has_many through
associations with complex self-referential joins. It was something like this:
class User < ApplicationRecord
has_many :memberships
has_many :teams, through: :memberships
has_many :tasks
end
class Team < ApplicationRecord
has_many :memberships
has_many :users, through: :memberships
end
class Membership < ApplicationRecord
enum :role, %w[peon manager].index_by(&:itself)
belongs_to :user
belongs_to :team
end
class Task < ApplicationRecord
belongs_to :user
end
Accessing my tasks is done easily via User#tasks
, but if you wanted to get all tasks for all members of the teams you are a manager of, the query could be a bit complicated. However, another option is to add it the association itself using an extension:
class User < ApplicationRecord
has_many :teams, through: :memberships do
def managed_users
team_ids = where(membership: { role: :manager}) .pluck(:id)
User
.joins(:memberships)
.where(memberships: { team_id: team_ids})
end
def managed_user_ids
managed_users.pluck(:id)
end
def managed_tasks
Task.where(user_id: managed_user_ids)
end
end
end
# usage
User.find(123).teams.managed_tasks
This is a good way of adding functionality closer to the responsible entity, users instead of teams or tasks in this example.