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.
(: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|
(: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
(: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
Copyright © 2009, Michael van Rooijen / Final Creation. See LICENSE for details.