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 installIn the following instructions:
-
Recordrefers to the model your users are allowed to reorder -
Containerrefers to the model that holds orderedRecords (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_recordsIn 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
endNew 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
endModels
Record
Replace the belongs_to :container relation in your Record model with:
sortable_belongs_to :container, inverse_of: :records,
scope: :container_id # , on: :sort_positionYou can also specify a custom association scope:
sortable_belongs_to :container, -> { with_deleted },
inverse_of: :records, scope: :container_id # , on: :sort_positionIt 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_numberContainer
Simply replace the has_many :records relation in your Container model with:
sortable_has_many :records, inverse_of: :container # , on: :sort_positionYou 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_positionOnce 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
recordsin order directly from the relation in thecontainer(or fromRecord.allif therecordsare globally sorted). -
Get all peers of a
record(recordsin the samecontainer) by using thesort_position_peersmethod. -
Create a new
containerwith severalrecordswith one call tocontainer.saveand have all the records receive validsort_positions. -
Add a new
recordto an existingcontainerand have it automatically appended at the end of the list. -
Set the
sort_positionfor arecordand have otherrecordsin the samecontainerbe automatically updated to create a gap when thatrecordis saved. -
Change a
record'scontainerand have otherrecordsin the newcontaineralso be automatically updated to create a gap for thatrecord. -
Close all gaps in the
sort_positionfor the peers of arecordby calling thecompact_sort_position_peers!method. -
Get the next or previous record by using the
next_by_sort_positionorprevious_by_sort_positionmethods.
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:loadOr if the above fails:
$ bundle exec rake db:schema:loadTesting
To run all existing tests for Sortability, simply execute the following from the main folder:
$ rakeOr if the above fails:
$ bundle exec rakeLicense
This gem is distributed under the terms of the MIT license. See the MIT-LICENSE file for details.