Skip to content

Commit

Permalink
cleaner tests, adding documentation for all the overrride options
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Croak committed Jan 2, 2009
1 parent c1fc242 commit 8f86f51
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 78 deletions.
124 changes: 89 additions & 35 deletions README.textile
Original file line number Diff line number Diff line change
Expand Up @@ -18,61 +18,115 @@ In app/helpers/application_helper.rb:
include SortableTable::App::Helpers::ApplicationHelper
end

h2. Conventions
h2. Testing

params[:sort] and params[:order]
context "enough Users to sort" do
setup do
5.times { Factory :user }
end

h2. Test with shoulda macros:
should_sort_by_attributes :name, :email, :age, :group => "groups.name"

context "enough Users to sort" do
setup { 5.times { Factory :user } }
should_sort_by_attributes :name, :email, :age
context "GET to #index" do
setup { get :index }
should_display_sortable_table_header_for :name, :email, :age
end
end

If you need to test an action other than a simple "get :index," you can pass a
block to #should_sort_by_attributes:

context 'GET to show with id, sort, and order params' do
setup do
5.times { Factory :user }
@search = Factory(:search, :name => 'name')
end
should_sort_by_attributes :name, :email do |sort, order|
get :show, :id => @search.to_param, :sort => sort, :order => order
should_display_sortable_table_header_for :name, :email, :age, :group
end
end

h2. Controller
This is the common case for a RESTful UsersController.

Use the sortable_attributes macro to list the... sortable attributes.
* should_sort_by_attributes tests that the controller's index action can sort by the attributes.
* should_display_sortable_header_for tests that a sortable header displays for the attributes.

Then, in your index action, pass the sort_order method to your usual
order parameter for will paginate or named scope call.
h2. Controller

class UsersController < Admin::BaseController
sortable_attributes :name, :email
sortable_attributes :name, :email, :age, :group => "groups.name"

def index
@users = User.paginate :page => params[:page], :order => sort_order
end
end

sortable_attributes defines a sort_order method that gets called in your action.

If the index action is rendered without a params[:sort] option, @users will be sorted by :name, the first option in the list of sortable_attributes.

h2. View

Add some sugar for your views:

%h1 Users
%table
%tr
= sortable_table_header :name => 'Name', :sort => 'name'
= sortable_table_header :name => 'E-mail', :sort => 'email'
- @users.each do |each|
%tr
%td= each.name
%td= each.email
<h1>Users</h1>

<table>
<tr>
<%= sortable_table_header :name => "Name", :sort => "name" %>
<%= sortable_table_header :name => "Email", :sort => "email" %>
<%= sortable_table_header :name => "Age", :sort => "age" %>
<%= sortable_table_header :name => "Group", :sort => "group" %>
</tr>

<% @users.each do |user| %>
<tr>
<td><%= html_escape(user.name) %></td>
<td><%= html_escape(user.email) %></td>
<td><%= html_escape(user.age) %></td>
<td><%= html_escape(user.group.name) %></td>
</tr>
<% end %>
</table>

sortable_table_header creates a table header containing a link with the correct :sort and :order params. It also has a class of "ascending" or "descending" so you can add styles with arrows.

h2. Example styles

th.ascending a {
background: url(/images/sort-ascending-arrow.gif) 0% 50% no-repeat;
padding-left: 15px;
}

th.descending a {
background: url(/images/sort-descending-arrow.gif) 0% 50% no-repeat;
padding-left: 15px;
}

h2. Overriding defaults

h3. should_sort_by_attributes

Opinionated defaults:

* GET to :index
* collection same name as controller (@users for UsersController)
* model name same name as controller (User for UsersController

If you need to test another action (or a nested controller), pass a block:

should_sort_by_attributes :age do |sort, order|
get :show, :sort => sort, :order => order, :group_id => @group.id
end

If you need to test another collection or model name, use should_sort_by.

h3. should_sort_by

The :collection, :model_name, and :action options of should_sort_by.

context "with a non-standard collection name" do
action = lambda { |sort, order| get :members, :sort => sort, :order => order }
should_sort_by :name, { :collection => "members",
:model_name => "user",
:action => action } do |user|
user.name
end
end

h3. sort_order

The default sort order is descending. This applies to the first time you click on a table header. You can override this to be ascending:

def index
@users = User.find :all, :order => sort_order("ascending")
end

Authors
-------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def join_array_and_hash_values(array, hash)
def define_sort_order(acceptable_columns, mappings)
define_method(:sort_order) do |*default|
direction = params[:order] == 'ascending' ? 'asc' : 'desc'
column = params[:sort] || 'created_on'
column = params[:sort] || acceptable_columns.first
if params[:sort] && acceptable_columns.include?(column)
column = mappings[column.to_sym] || column
handle_compound_sorting(column, direction)
Expand Down
13 changes: 7 additions & 6 deletions lib/sortable_table/app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ def sortable_table_header(opts = {})
link_to(opts[:name],
sortable_url(opts) + anchor,
:title => opts[:title]),
:class => full_class_name_for_sortable_table_header(opts)
:class => sortable_table_header_classes(opts)
end

def full_class_name_for_sortable_table_header(opts)
class_name_array = []
class_name_array << class_name_for_sortable_table_header_tag(opts) << opts[:class]
class_name_array.compact.blank? ? nil : class_name_array.compact.join(" ")
def sortable_table_header_classes(opts)
class_names = []
class_names << sortable_table_header_class(opts)
class_names << opts[:class]
class_names.compact.blank? ? nil : class_names.compact.join(" ")
end

def class_name_for_sortable_table_header_tag(opts)
def sortable_table_header_class(opts)
if default_sort_to_most_recent? opts
'descending'
elsif re_sort? opts
Expand Down
3 changes: 0 additions & 3 deletions shoulda_macros/sortable_table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@ def should_sort_by(attribute, options = {}, &block)
%w(ascending descending).each do |direction|
should "sort by #{attribute.to_s} #{direction}" do
assert_db_records_exist_for(model_under_test) # sanity check

action.bind(self).call(attribute.to_s, direction) # controller action

assert_collection_can_be_tested_for_sorting(collection) # sanity check

assert_collection_is_sorted(collection, direction, &block) # test
end
end
Expand Down
16 changes: 10 additions & 6 deletions test/rails_root/app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
class UsersController < ApplicationController

sortable_attributes :name,
:age,
:email,
:group => "groups.name",
sortable_attributes :name, :age, :email, :group => "groups.name",
:age_and_name => ["age", "users.name"]

def index
@users = User.find :all, :order => sort_order("ascending")
@users_dupe = @users
@users = User.find :all, :order => sort_order("ascending")
end

def show
@users = User.find :all, :order => sort_order
end

def members
@members = User.find :all, :order => sort_order
end

end
24 changes: 12 additions & 12 deletions test/rails_root/app/views/users/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

<table>
<tr>
<%= sortable_table_header :name => 'Name', :sort => 'name' %>
<%= sortable_table_header :name => 'Email', :sort => 'email' %>
<%= sortable_table_header :name => 'Age', :sort => 'age' %>
<th>Created On</th>
<%= sortable_table_header :name => "Name", :sort => "name" %>
<%= sortable_table_header :name => "Email", :sort => "email" %>
<%= sortable_table_header :name => "Age", :sort => "age" %>
<%= sortable_table_header :name => "Group", :sort => "group" %>
</tr>

<% @users.each do |user| %>
<tr>
<td><%=h user.name %></td>
<td><%=h user.email %></td>
<td><%=h user.age %></td>
<td><%=h user.created_at.to_s %></td>
</tr>
<% end %>
<% @users.each do |user| %>
<tr>
<td><%= html_escape(user.name) %></td>
<td><%= html_escape(user.email) %></td>
<td><%= html_escape(user.age) %></td>
<td><%= html_escape(user.group.name) %></td>
</tr>
<% end %>
</table>
Empty file.
Empty file.
25 changes: 10 additions & 15 deletions test/rails_root/test/functional/users_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,31 @@ class UsersControllerTest < ActionController::TestCase

context "enough Users to sort" do
setup do
Factory(:user, :name => "a")
Factory(:user, :name => "b")
Factory(:user, :email => "a@a.com")
Factory(:user, :email => "b@b.com")
Factory(:user, :age => 20)
Factory(:user, :age => 30)
5.times { Factory :user }
end

should_sort_by_attributes :name, :email
should_sort_by_attributes :name, :email, :group => "groups.name"

should_sort_by "groups.name"
should_sort_by :group => "groups.name"

# block form
# block form to test an action other than :index
should_sort_by_attributes :age do |sort, order|
get :index, :sort => sort, :order => order
get :show, :sort => sort, :order => order
end

# TODO:
# should_sort_by :age_and_name => ["age", "users.name"]

context "with a non-standard instance variable name" do
should_sort_by :name, { :collection => "users_dupe", :model_name => "user" } do |user|
context "with a non-standard collection name" do
action = lambda { |sort, order| get :members, :sort => sort, :order => order }
should_sort_by :name, { :collection => "members",
:model_name => "user",
:action => action } do |user|
user.name
end
end

context "GET to #index" do
setup { get :index }
should_display_sortable_table_header_for :name, :email, :age
should_display_sortable_table_header_for :name, :email, :age, :group
end
end

Expand Down

0 comments on commit 8f86f51

Please sign in to comment.