Make Sortable

This plugin/gem provides an easy way to implement a simple database record sorting solution.

Installation

gem sources -a http://gems.github.com
sudo gem install meskyanichi-make_sortable

config/environment.rb

config.gem 'meskyanichi-make_sortable', :lib => 'make_sortable'

Simple Setup

Add the “make_sortable” method to your controller.

class PostsController < ApplicationController
	make_sortable
end

The above will assume that:

  • You want to make “posts” sortable

  • You are using the “posts” controller

  • You are using the “post” model

  • You have a “position” attribute in your “post” model

  • You have a “position” attribute in your “posts” table

Now, this single method, without any options, is already enough to start sorting! It will make 4 methods available to you in the view. Check the example below, it should make sense.

RAILS_ROOT/app/views/posts/index.html.erb

<table>
	<tr>
		<th>Up</th>
		<th>Down</th>
		<th>Name</th>
	</tr>

	<% @posts.each_with_index do |post, index| %>
	<tr>
		<td><%= button_up_for_posts_position(:object => @posts, :index => index) %></td>
		<td><%= button_down_for_posts_position(:object => @posts, :index => index) %></td>
		<td><%= post.name %></td>
	</tr>
	<% end %>
</table>

Above you see a simple table, with posts being looped out using the “each_with_index” method. It is important that you use the “each_with_index” method, rather than just the “each” method since you will need the index to determine the position of each record.

That’s it! Just click on the up or down button and it works.

What I always like to do is go into my model, in this case, post.rb and add a default_scope:

class Post < ActiveRecord::Base
	default_scope :order => :position
end

This will make sure that by default, all records are sorted by “position”.

Slightly more advanced setup

So, the above is quite easy to utilize if you follow the conventions, which in my option, are fine. Adding 1 method in a controller and providing you with helper methods to generate the buttons for the view. Quite nice.

Now now, there are a couple of settings you can adjust through the make_sortable method. Settings you can adjust are:

  • the controller (:controller) that should be used (default: controller that it’s invoked on)

  • the model (:model) that should be used (default: singular form of the controller name that the method is invoked on)

  • the attribute (:attribute) inside the model that should be used (default: position)

  • the redirect path (:redirect_path) where the user should be redirected to after the process (default: index action of specified controller)

  • the filter (:filter) which you can use for associations. (default: nil)

Specifying the :controller and :model options with make_sortable()

The only time you would want to do this is when you for example have a bunch of posts displayed and wish to have their comments displayed below them on the same screen. Also, at the same time, you wish for these comments to ALSO be “sortable” from this location.

Requirement

class PostsController < ApplicationController
	make_sortable # for posts
	make_sortable	:controller => :comments,
					:model => :comment
end

We need to invoke a second “make_sortable” with the :controller => :comments and :model => :comment to let the view know it needs to generate the methods for comments as well for any PostController action.

NOTE: Do not add any other options to the make_sortable for the comments. These settings need to be specified inside the CommentsController where they belong.

So be sure to add the “make_sortable” method inside the CommentsController

class CommentsController < ApplicationController
	make_sortable
end

And add any additional options to the “make_sortable” method here, NOT inside the PostsController.

Using a different model/database attribute

So you have your database set up with the attribute “order” instead of “position” and wish to use it instead of the default “position”.

Apply the following option to the “make_sortable()” method

class PostsController < ApplicationController
	make_sortable :attribute => :order
end

Changing the “Redirect Path”

If you do not wish to have your users be redirected to the index action after clicking on a “up” or “down” button to update the records “position”, you can change this very easily.

Redirecting to a different action

class PostsController < ApplicationController
	make_sortable :redirect_path => {:action => "other_action"}
end

If you simply wish to return to the page on which you were when clicking on the “up” or “down” button, apply the following option.

Redirecting Back

class PostsController < ApplicationController
	make_sortable :redirect_path => :back
end

Filtering position updates

So you are building a web application which has mulitple users, and each user has it’s own posts. Obviously you do not want “User 1” to update “User 2”‘s posts position. By default, “User 1” will have the ability to update “ALL” posts when updating his. Of course, that’d mean “User 1” should be able to see other users’ posts. Regardless, it’s best to just filter out that kind of behavior on the back-end to prevent it from ever happening.

For example, we have a logged in user, and we store all it’s data inside the “current_user” method/variable. It’s an AR Object. Now, when this user clicks on the “up” or “down” button of a post item, we want to make sure that regardless of what he sees on the front-end, can’t be adjusted unless he is the owner of the post.

You can accomplish this by adding the following option

class PostsController < ApplicationController
	make_sortable :filter => current_user
end

If you need to chain in a way (maybe for the users’ -> post’ -> comments), you can just do the following

class CommentsController < ApplicationController
	make_sortable :filter => current_user.post
end

View Helpers

When the “make_sortable” method is invoked on a controller, it will dynamically generate 4 view helpers for you to use.

Let’s say we’ve written the following

class PostsController < ApplicationController
	make_sortable
end

The above would provide you with 4 methods. These methods will be available to all “PostController” views.

  • button_up_for_posts_position :object, :index, :html

  • button_down_for_posts_position :object, :index, :html

  • link_up_for_posts_position :object, :index, :html

  • link_down_for_posts_position :object, :index, :html

Note that it’s highly recommended to use the button_up and button_down methods. The link_up and link_down methods rely on Javascript.

If you for any reason need these methods available to a different controllers’ views, see the (above) topic: “Specifying the :controller and :model options with make_sortable()”

All 4 methods take the same arguments. Let me explain.

Argument :object

This is a required argument. It expects you to provide the object/(array) on which you are performing the .each_with_index method.

button_up_for_posts_position(:object => @posts)

Argument :index

This is a required argument. It expects you to provide the index of the currently looping object(record). You can get this index when calling .each_with_index on the @posts object/(array)

@posts.each_with_index do |post, index|
	button_up_for_posts_position(:object => @posts, :index => index)
end

The example above is basically all that’s required to get the sorting gem working. The :html argument is optional. Basically, it’s like the “link_to()” and “button_to()” methods in the Rails-Core. With it you can provide any additional HTML attributes to the HTML tag.

Argument :html

button_up_for_posts_position(:object => @posts, :index => index, :html => {
	:id		=> "post_id_#{post.id}",
	:class	=> "post_class",
	:etc	=> "etc_etc_etc"
})

So you are free to add whatever additional html attributes you like to all 4 methods.

Copyright © 2009, Michael van Rooijen / Final Creation. See LICENSE for details.