Two really useful gems for a Rails project are acts_as_list and permanent_records (or the somewhat more common acts_as_paranoid). The first one, acts_as_list
, adds a position
column to your model to provide natural ordering. Usually you don’t want global ordering, but only within an owning model, e.g. the Page
objects inside a Book
have position 1
, 2
, 3
, and the Page
objects inside a different Book
also go 1
, 2
, 3
. You can use the scope
option to achieve that:
class Page
belongs_to :book
acts_as_list scope: :book
end
The problem is that if you are soft-deleting pages using a deleted_at
column, acts_as_list
doesn’t know to exclude deleted pages from the list. This gets annoying if you want to move pages up and down: Imagine you have page 1, then 5 deleted pages, and now you add a new page p
to the end. You want to move your new page to the beginning. You have a button that calls p.move_higher
, but the page won’t appear to move until the user presses the button five times. That is pretty confusing!
Fortunately acts_as_list
lets you pass a string to scope
that it will evaluate when necessary, and you can even embed Ruby code in it. Look at this, and notice the single quotes!:
class Page
belongs_to :book
acts_as_list scope: 'book_id = #{book_id} AND deleted_at IS NULL'
end
With that code, acts_as_list
will only consider non-deleted pages, so moving a page works as it should.