0

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 Answers 2

1

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 :)

2
  • Thanks for the help! This helped me get on the right track. I guess ultimately my final solution was very similar to the thread I linked in my question. I just needed to work through every piece to understand what was going on. Commented Jul 9, 2023 at 0:07
  • Glad to hear you worked through it yourself step by step. Definitely a great way to learn and have confidence in your code. I see your linked answer (which I didn't look at previously sorry!) calls the table positions. I'd suggest you'd be better off being more specific in your naming of this table and describe these things you're positioning instead. In my scenario I also use this table as a place to cache things like the names of the items so that future index lookups of the list don't need to do additional queries to load the items themselves just to fetch basic stats. Commented Jul 9, 2023 at 2:36
1

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.

1
  • 1
    Thank you! My final result ended with having a polymorphic model that has the position stored on it, similar to the original link I posted as well as your recommendation. Commented Jul 9, 2023 at 0:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.