[In your opinion] What's the best way to deal with nested resources when polymorphic associations are in play?

I have a polymorphic association between messages and read_receipts. ReadReceipt has a polymorphic association on it called readable. On the model side, I have it so that I can have read receipt functionality by only including the module:

Models and routes

# Readable Concern for Models

module Readable
  extend ActiveSupport::Concern

  included do
    scope :read, -> { joins(:read_receipts) }
    scope :unread, -> { left_outer_joins(:read_receipts).where(read_receipts: { id: nil }) }
    
    has_many :read_receipts, as: :readable
  end

  .......

end
# Models

class Message < ApplicationRecord
  include Readable
end

class OtherModel < ApplicationRecord
  include Readable
end
# Routes

resources :messages do
  resources :read_receipts
end

resources :other_models do
  resources :read_receipts
end

Ugly-ass controller

class ReadReceiptsController < ApplicationController
  before_action :set_readable

  ...........

  def set_readable
    # Ugly, terribly sinful code. How can we clean this up to accept all 'Readable' models?
    @readable = params[:message_id] && Message.find(params[:message_id])
    @readable = params[:other_model_id] && OtherModel.find(params[:other_model_id])
  end
end

How would you DRY this up when you have nested resources that are polymorphic associations? I run into the problem where each nested resource passes a parameter in the style of model_name_id.

I had same issue in one of my projects. There were multiple models that could have comments, so I ended up with this:

def find_commentable
  params.each do |name, value|
    if name =~ /(.+)_id$/
      return $1.classify.constantize.find(value)
    end
  end
  nil
end

Not very pretty, but seems to work fine. Hope that helps.

1 Like