Sortability
Sortability is a gem that makes it easy to manage records that can be sorted and reordered by users of your Rails app.
Installation
Add this line to your application's Gemfile:
gem 'sortability'
And then execute:
$ bundle install
In the following instructions:
-
Record
refers to the model your users are allowed to reorder -
Container
refers to the model that holds orderedRecord
s (e.g. a list)
Migrations
Sortability uses a non-null integer column in your records
table
to store the sort order.
It is also a good idea to have a unique index that covers the sort column
and any container_id
or container_type
columns.
By default, the sort column is named sort_position
, but that name can be
changed by passing the on
option to the methods provided by this gem.
This will also change the names of the methods created on the Record
model.
The scope
option in the following methods specifies the container foreign
key column(s). You can ommit it if the records
should be sorted globally.
Existing Tables
If you don't already have this column, you will need to add it
to the records
table using a migration:
$ rails g migration add_sort_position_to_records
In this migration, you will want something similar to this:
class AddSortPositionToRecords < ActiveRecord::Migration[4.2]
def change
add_sortable_column :records # , on: :sort_position
add_sortable_index :records, scope: :container_id # , on: :sort_position
end
end
New Tables
If you haven't created the records
table yet, you can use the sortable
method to create the appropriate column in the new table,
but you should still create the index using add_sortable_index
to ensure that the index covers the appropriate column(s):
class CreateRecords < ActiveRecord::Migration[4.2]
def change
create_table :records do |t|
t.sortable # on: :sort_position
end
add_sortable_index :records, scope: :container_id # , on: :sort_position
end
end
Models
Record
Replace the belongs_to :container
relation in your Record
model with:
sortable_belongs_to :container, inverse_of: :records,
scope: :container_id # , on: :sort_position
You can also specify a custom association scope:
sortable_belongs_to :container, -> { with_deleted },
inverse_of: :records, scope: :container_id # , on: :sort_position
It is highly recommended that you specify the inverse_of
and scope
options.
If records
are sorted globally, without a container
, use the sortable_class
method instead:
sortable_class # on: :sort_position, scope: :sort_group_number
Container
Simply replace the has_many :records
relation in your Container
model with:
sortable_has_many :records, inverse_of: :container # , on: :sort_position
You can also specify a custom association scope, but in that case you have to order the records yourself:
sortable_has_many :records, ->{ with_deleted.order(:sort_position) },
inverse_of: :container # , on: :sort_position
Once again, it is highly recommended that you specify the inverse_of
option.
Usage
Once you have run the migrations and modified your models according to the installation instructions, you are ready to start sorting the records
.
Here are some things that you can do:
-
Get all the
records
in order directly from the relation in thecontainer
(or fromRecord.all
if therecords
are globally sorted). -
Get all peers of a
record
(records
in the samecontainer
) by using thesort_position_peers
method. -
Create a new
container
with severalrecords
with one call tocontainer.save
and have all the records receive validsort_position
s. -
Add a new
record
to an existingcontainer
and have it automatically appended at the end of the list. -
Set the
sort_position
for arecord
and have otherrecords
in the samecontainer
be automatically updated to create a gap when thatrecord
is saved. -
Change a
record
'scontainer
and have otherrecords
in the newcontainer
also be automatically updated to create a gap for thatrecord
. -
Close all gaps in the
sort_position
for the peers of arecord
by calling thecompact_sort_position_peers!
method. -
Get the next or previous record by using the
next_by_sort_position
orprevious_by_sort_position
methods.
The sort_position
is not guaranteed to contain consecutive numbers.
When listing the records
, you should compute their positions
in application code as you iterate through the list.
If you need the position for a single record
, call
compact_sort_position_peers!
first to close any gaps
in its peers, then read sort_position
directly.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Write specs for your feature
- Implement your new feature
- Test your feature (
rake
) - Commit your changes (
git commit -am 'Added some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
Development Environment Setup
- Use bundler to install all dependencies:
$ bundle install
- Load the schema:
$ rake db:schema:load
Or if the above fails:
$ bundle exec rake db:schema:load
Testing
To run all existing tests for Sortability, simply execute the following from the main folder:
$ rake
Or if the above fails:
$ bundle exec rake
License
This gem is distributed under the terms of the MIT license. See the MIT-LICENSE file for details.