Positionable¶ ↑
<img src=“https://secure.travis-ci.org/pguegan/positionable.png” />
Positionable is a library which provides contiguous positionning capabilities to your ActiveRecord models.
For more functionalities, you could also have a look at acts_as_list.
Installation¶ ↑
Edit your Gemfile, and simply add the following line:
gem 'positionable'
Getting Started¶ ↑
Let’s say you want to make the model Item
positionable.
Create a migration¶ ↑
First, create a migration to add the column position
to the table items
:
rails generate migration add_position_to_items position:integer
Then, run the migration:
rake db:migrate
Setup your model¶ ↑
Simply add the is_positionable
method in your ActiveRecord model:
class Item < ActiveRecord::Base is_positionable end
Grouping records¶ ↑
Maybe your items are grouped (typically with a belongs_to
association). In this case, you’ll want to restrict the position in each group by declaring the :scope
option:
class Item < ActiveRecord::Base belongs_to :folder is_positionable :scope => :folder attr_accessible :folder_id, :position end
Note that it is the model responsibility to give the white-list of attributes that can be updated via mass-assignement. In this case, you must add the position
attribute in the attr_accessible
clause.
Start position¶ ↑
By default, position starts by zero. But you may want to change this at the model level, for instance by starting at one (which seems more natural for some people):
class Item < ActiveRecord::Base is_positionable :start => 1 end
Ordering¶ ↑
When a new record is created, it is inserted by default at the last (highest) position of its group. Thus, when record are listed, the newly created record will appear at the bottom.
It is possible to change this behaviour by setting the order
option as follows:
class Item < ActiveRecord::Base is_positionable :order => :desc end
This way, records are always listed by descending positions order. Record that have the highest position will appears at the top.
Caution! The semantic of next
or previous
methods remains unchanged. More precisely, even if the highest position is the position of the first returned record, it is still considered as the last one (i.e.: Item.first.last?
returns true
). I know, this is odd. The semantic of these methods will certainly change in a further version.
Mixing options¶ ↑
Obviously, these options are not exclusive. You are free to mix them like, for example:
class Item < ActiveRecord::Base belongs_to :folder is_positionable :scope => :folder, :order => :desc, :start => 1 end
Usage¶ ↑
Querying¶ ↑
To get the previous or next sibling items:
previous = item.previous all_previous = item.all_previous next = item.next all_next = item.all_next
Both first and last items can be caracterized:
item.first? # True if item.previous is nil item.last? # True if item.next is nil
Given a positionable item, its position is always included in a range which can be determined as follows:
item.range
If this item is aimed at being moved to another different scope, then you can pass this new scope as a parameter:
item.range(folder)
Moving¶ ↑
Rather than directly assign position attribute, you can move your items with these provided methods:
item = Item.create(...) # The newly created item is put at the last position (by default). item.up! # Item's position is swaped with the previous item. item.down! # Item's position is swaped with the next item. item.move_to new_position # Moves this record to the given position, and updates sibling items positions accordingly.
Yet, it is possible to update the item’s position via mass-assignement:
item.update_attributes { :position => new_position, ... }
This will trigger some ActiveRecord callbacks in order to maintain positions contiguity across all other concerned items, even if the item is moved from a scope to another.