Is there a way to use acts_as_list with a single parent but many different children?

75 Views Asked by At

I've stumbled upon this Rails - acts_as_list with multiple Models. I'm having a hard time understanding how to fit the solution into my current use case.

In my app, I have a section. The section can hold many places, checklists, and notes.

class Section < ApplicationRecord
  has_many :places, -> { order(position: :asc) }, dependent: :destroy
  has_many :notes, -> { order(position: :asc) }, dependent: :destroy
  has_many :checklists, -> { order(position: :asc) }, dependent: :destroy
end

class Note < ApplicationRecord
  acts_as_list scope: :section

  belongs_to :section
end

class Place < ApplicationRecord
  acts_as_list scope: :section

  belongs_to :section
end

class Checklist < ApplicationRecord
  acts_as_list scope: :section

  belongs_to :section
end

What I'm aiming to do, is allow the user to drag around each place, checklist, or note in the section. I've opted to use acts_as_list to manage positioning.

The problem I'm experiencing is, the scope on each model isn't global to the section. If I query data as section.places The position of the items may be 1, 2, and 3. If I query data as section.notes The position could also be 1, 2, and 3.

What I'm ultimately trying to do is tell acts_as_list to order based on parent. Is this possible? Should I be looking more into polymorphic relationships to solve this problem?

2

There are 2 best solutions below

2
Brendon Muir On BEST ANSWER

An alternative might be to use STI (Single Table Inheritance). That way your Note, Place, and Checklist would share the same table and you could declare acts_as_list on the abstract inherited model. STI has its drawbacks though.

You could also have a concrete model that has a polymorphic relationship to each of Note, Place, and Checklist (1:1). You'd declare acts_as_list on that model. I do this in my content management system:

class ComponentInstance < ApplicationRecord
  belongs_to :section
  belongs_to :instance, polymorphic: true
  acts_as_list scope: :section
end

class Note < ApplicationRecord
  has_one :component_instance, as: :instance, inverse_of: :instance
end

class Place < ApplicationRecord
  has_one :component_instance, as: :instance, inverse_of: :instance
end

class Checklist < ApplicationRecord
  has_one :component_instance, as: :instance, inverse_of: :instance
end

I've adapted it to your setup but you'd probably want to change the name of the ComponentInstance class. Hope that helps :)

1
smathy On

Right, the scope doesn't solve the "meaningful position across multiple models" issue. For that you can't use acts_as_list, you need to craft it yourself using a separate Position model.

In your case you might find the queries nicer if you stored the section_id in the Position model.