Lately we have been working on some drag & drop interfaces for some client work that needed model sorting. As you might know, on the front-end layer there are plenty of solutions for that (being jQuery draggable/sortable a really good one). But what to use on a model level?
We first thought of some
acts_as_list-ish kind of plugins, and after thoroughly
reviewing them all, we came up with an observation.
Most of sorting plugins work with an absolute
position attribute that sets
the weight of a given element within a tree. This field has no semantic sense,
since "84" by itself gives you absolutely no information about an element's
position or its relations with other elements of the tree. After you just read
this, just like us, you won't be able to sleep. Think about it. All these
arbitrary numbers in your database columns. Flowers aren't pretty anymore. Life
Of course it doesn't! Computer science brings us beautiful data structures to
satiate our need for beauty. Look at the structure of a linked list
for example, and you will think: enough
:order => "position" already! We
don't need an arbitrary
position field. We just need every model to reference
next and a
previous. Yeah, it smells like those old C tutorials, but it
is worth it :)
So, unlike most Rails sorting plugins (acts_as_list, etc), Resort is based on linked lists rather than absolute position fields.
TL;DR - How can I use it?
$ gem install resort
Or in your Gemfile:
As you just learned, you must add two fields (
first) to your
model's table, let's say,
class AddResortFieldsToProducts < ActiveRecord::Migration def self.up add_column :products, :next_id, :integer add_column :products, :first, :boolean add_index :products, :next_id add_index :products, :first end def self.down remove_column :products, :next_id remove_column :products, :first end end
Then in your
class Product < ActiveRecord::Base resort! end
By default, Resort will treat all products as a single big tree. But what if
you have different
ProductLines, and want to sort products separately within
each product line? As a sortable product, you have to tell Resort who your
siblings are. To do that, just override the
class Product < ActiveRecord::Base resort! def siblings \# Tree contains only products from my own product line self.product_line.products end end
Now your products are enhanced with two methods doing the following:
first?— Returns true if the element is the first of the tree.
append_to(other_element)— Appends the element after another element.
And the class Product has a new scope named
ordered that returns the
products in order. Easy huh?
UPDATE: We did some benchmarks comparing the performance of Resort against RankedModel and ActsAsList.