Positionable tables
A model keeping an integer position is one a reader puts in order by hand. The column is the whole
declaration; a column named otherwise is named with def self.recourse_position = :ordering. The routes
say where a drop is reported:
recourses :tasks, positionable: true
A grip opens each row, dragging it moves the row, and the place it lands in is written to
PATCH /tasks/5/position. The table is ordered by the column, the column is never drawn, and the search
box and the sorted headings stand down while the grips are drawn, since a shortened page would report a wrong
place. A listing of every row across every parent, where positions repeat, is not positioned.
Two callbacks keep the numbers running 1, 2, 3: a new row lands last among its own, and the gap closes behind one that goes. A model pointing two ways says which rows its place is counted among:
class Task < ApplicationRecord
belongs_to :project
belongs_to :user
def recourse_siblings = project.tasks
end
A host keeping the order itself writes def self.recourse_position = nil, which takes the grips and
both callbacks away.
A second listing
A host listing the same rows in a second order — tasks under a user, ranked — names that column on both the listing controller and the positions controller, since a drop would otherwise write the model’s own column:
module Ranking
extend ActiveSupport::Concern
private
def recourse_position = 'ranking'
end
class Users::TasksController < RecoursesController
include Ranking
end
class Users::Tasks::PositionsController < Recourse::PositionsController
include Ranking
end
Recourse::Positioning.new(relation, column).close is what such a host calls to keep its own column
contiguous when a row goes.