annotate app/helpers/sort_helper.rb @ 8:0c83d98252d9 yuya

* Add custom repo prefix and proper auth realm, remove auth cache (seems like an unwise feature), pass DB handle around, various other bits of tidying
author Chris Cannam
date Thu, 12 Aug 2010 15:31:37 +0100
parents 513646585e45
children cbce1fd3b1b7
rev   line source
Chris@0 1 # Helpers to sort tables using clickable column headers.
Chris@0 2 #
Chris@0 3 # Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
Chris@0 4 # Jean-Philippe Lang, 2009
Chris@0 5 # License: This source code is released under the MIT license.
Chris@0 6 #
Chris@0 7 # - Consecutive clicks toggle the column's sort order.
Chris@0 8 # - Sort state is maintained by a session hash entry.
Chris@0 9 # - CSS classes identify sort column and state.
Chris@0 10 # - Typically used in conjunction with the Pagination module.
Chris@0 11 #
Chris@0 12 # Example code snippets:
Chris@0 13 #
Chris@0 14 # Controller:
Chris@0 15 #
Chris@0 16 # helper :sort
Chris@0 17 # include SortHelper
Chris@0 18 #
Chris@0 19 # def list
Chris@0 20 # sort_init 'last_name'
Chris@0 21 # sort_update %w(first_name last_name)
Chris@0 22 # @items = Contact.find_all nil, sort_clause
Chris@0 23 # end
Chris@0 24 #
Chris@0 25 # Controller (using Pagination module):
Chris@0 26 #
Chris@0 27 # helper :sort
Chris@0 28 # include SortHelper
Chris@0 29 #
Chris@0 30 # def list
Chris@0 31 # sort_init 'last_name'
Chris@0 32 # sort_update %w(first_name last_name)
Chris@0 33 # @contact_pages, @items = paginate :contacts,
Chris@0 34 # :order_by => sort_clause,
Chris@0 35 # :per_page => 10
Chris@0 36 # end
Chris@0 37 #
Chris@0 38 # View (table header in list.rhtml):
Chris@0 39 #
Chris@0 40 # <thead>
Chris@0 41 # <tr>
Chris@0 42 # <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
Chris@0 43 # <%= sort_header_tag('last_name', :caption => 'Name') %>
Chris@0 44 # <%= sort_header_tag('phone') %>
Chris@0 45 # <%= sort_header_tag('address', :width => 200) %>
Chris@0 46 # </tr>
Chris@0 47 # </thead>
Chris@0 48 #
Chris@0 49 # - Introduces instance variables: @sort_default, @sort_criteria
Chris@0 50 # - Introduces param :sort
Chris@0 51 #
Chris@0 52
Chris@0 53 module SortHelper
Chris@0 54 class SortCriteria
Chris@0 55
Chris@0 56 def initialize
Chris@0 57 @criteria = []
Chris@0 58 end
Chris@0 59
Chris@0 60 def available_criteria=(criteria)
Chris@0 61 unless criteria.is_a?(Hash)
Chris@0 62 criteria = criteria.inject({}) {|h,k| h[k] = k; h}
Chris@0 63 end
Chris@0 64 @available_criteria = criteria
Chris@0 65 end
Chris@0 66
Chris@0 67 def from_param(param)
Chris@0 68 @criteria = param.to_s.split(',').collect {|s| s.split(':')[0..1]}
Chris@0 69 normalize!
Chris@0 70 end
Chris@0 71
Chris@0 72 def criteria=(arg)
Chris@0 73 @criteria = arg
Chris@0 74 normalize!
Chris@0 75 end
Chris@0 76
Chris@0 77 def to_param
Chris@0 78 @criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
Chris@0 79 end
Chris@0 80
Chris@0 81 def to_sql
Chris@0 82 sql = @criteria.collect do |k,o|
Chris@0 83 if s = @available_criteria[k]
Chris@0 84 (o ? s.to_a : s.to_a.collect {|c| append_desc(c)}).join(', ')
Chris@0 85 end
Chris@0 86 end.compact.join(', ')
Chris@0 87 sql.blank? ? nil : sql
Chris@0 88 end
Chris@0 89
Chris@0 90 def add!(key, asc)
Chris@0 91 @criteria.delete_if {|k,o| k == key}
Chris@0 92 @criteria = [[key, asc]] + @criteria
Chris@0 93 normalize!
Chris@0 94 end
Chris@0 95
Chris@0 96 def add(*args)
Chris@0 97 r = self.class.new.from_param(to_param)
Chris@0 98 r.add!(*args)
Chris@0 99 r
Chris@0 100 end
Chris@0 101
Chris@0 102 def first_key
Chris@0 103 @criteria.first && @criteria.first.first
Chris@0 104 end
Chris@0 105
Chris@0 106 def first_asc?
Chris@0 107 @criteria.first && @criteria.first.last
Chris@0 108 end
Chris@0 109
Chris@0 110 def empty?
Chris@0 111 @criteria.empty?
Chris@0 112 end
Chris@0 113
Chris@0 114 private
Chris@0 115
Chris@0 116 def normalize!
Chris@0 117 @criteria ||= []
Chris@0 118 @criteria = @criteria.collect {|s| s = s.to_a; [s.first, (s.last == false || s.last == 'desc') ? false : true]}
Chris@0 119 @criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria
Chris@0 120 @criteria.slice!(3)
Chris@0 121 self
Chris@0 122 end
Chris@0 123
Chris@0 124 # Appends DESC to the sort criterion unless it has a fixed order
Chris@0 125 def append_desc(criterion)
Chris@0 126 if criterion =~ / (asc|desc)$/i
Chris@0 127 criterion
Chris@0 128 else
Chris@0 129 "#{criterion} DESC"
Chris@0 130 end
Chris@0 131 end
Chris@0 132 end
Chris@0 133
Chris@0 134 def sort_name
Chris@0 135 controller_name + '_' + action_name + '_sort'
Chris@0 136 end
Chris@0 137
Chris@0 138 # Initializes the default sort.
Chris@0 139 # Examples:
Chris@0 140 #
Chris@0 141 # sort_init 'name'
Chris@0 142 # sort_init 'id', 'desc'
Chris@0 143 # sort_init ['name', ['id', 'desc']]
Chris@0 144 # sort_init [['name', 'desc'], ['id', 'desc']]
Chris@0 145 #
Chris@0 146 def sort_init(*args)
Chris@0 147 case args.size
Chris@0 148 when 1
Chris@0 149 @sort_default = args.first.is_a?(Array) ? args.first : [[args.first]]
Chris@0 150 when 2
Chris@0 151 @sort_default = [[args.first, args.last]]
Chris@0 152 else
Chris@0 153 raise ArgumentError
Chris@0 154 end
Chris@0 155 end
Chris@0 156
Chris@0 157 # Updates the sort state. Call this in the controller prior to calling
Chris@0 158 # sort_clause.
Chris@0 159 # - criteria can be either an array or a hash of allowed keys
Chris@0 160 #
Chris@0 161 def sort_update(criteria)
Chris@0 162 @sort_criteria = SortCriteria.new
Chris@0 163 @sort_criteria.available_criteria = criteria
Chris@0 164 @sort_criteria.from_param(params[:sort] || session[sort_name])
Chris@0 165 @sort_criteria.criteria = @sort_default if @sort_criteria.empty?
Chris@0 166 session[sort_name] = @sort_criteria.to_param
Chris@0 167 end
Chris@0 168
Chris@0 169 # Clears the sort criteria session data
Chris@0 170 #
Chris@0 171 def sort_clear
Chris@0 172 session[sort_name] = nil
Chris@0 173 end
Chris@0 174
Chris@0 175 # Returns an SQL sort clause corresponding to the current sort state.
Chris@0 176 # Use this to sort the controller's table items collection.
Chris@0 177 #
Chris@0 178 def sort_clause()
Chris@0 179 @sort_criteria.to_sql
Chris@0 180 end
Chris@0 181
Chris@0 182 # Returns a link which sorts by the named column.
Chris@0 183 #
Chris@0 184 # - column is the name of an attribute in the sorted record collection.
Chris@0 185 # - the optional caption explicitly specifies the displayed link text.
Chris@0 186 # - 2 CSS classes reflect the state of the link: sort and asc or desc
Chris@0 187 #
Chris@0 188 def sort_link(column, caption, default_order)
Chris@0 189 css, order = nil, default_order
Chris@0 190
Chris@0 191 if column.to_s == @sort_criteria.first_key
Chris@0 192 if @sort_criteria.first_asc?
Chris@0 193 css = 'sort asc'
Chris@0 194 order = 'desc'
Chris@0 195 else
Chris@0 196 css = 'sort desc'
Chris@0 197 order = 'asc'
Chris@0 198 end
Chris@0 199 end
Chris@0 200 caption = column.to_s.humanize unless caption
Chris@0 201
Chris@0 202 sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param }
Chris@0 203 # don't reuse params if filters are present
Chris@0 204 url_options = params.has_key?(:set_filter) ? sort_options : params.merge(sort_options)
Chris@0 205
Chris@0 206 # Add project_id to url_options
Chris@0 207 url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
Chris@0 208
Chris@0 209 link_to_remote(caption,
Chris@0 210 {:update => "content", :url => url_options, :method => :get},
Chris@0 211 {:href => url_for(url_options),
Chris@0 212 :class => css})
Chris@0 213 end
Chris@0 214
Chris@0 215 # Returns a table header <th> tag with a sort link for the named column
Chris@0 216 # attribute.
Chris@0 217 #
Chris@0 218 # Options:
Chris@0 219 # :caption The displayed link name (defaults to titleized column name).
Chris@0 220 # :title The tag's 'title' attribute (defaults to 'Sort by :caption').
Chris@0 221 #
Chris@0 222 # Other options hash entries generate additional table header tag attributes.
Chris@0 223 #
Chris@0 224 # Example:
Chris@0 225 #
Chris@0 226 # <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
Chris@0 227 #
Chris@0 228 def sort_header_tag(column, options = {})
Chris@0 229 caption = options.delete(:caption) || column.to_s.humanize
Chris@0 230 default_order = options.delete(:default_order) || 'asc'
Chris@0 231 options[:title] = l(:label_sort_by, "\"#{caption}\"") unless options[:title]
Chris@0 232 content_tag('th', sort_link(column, caption, default_order), options)
Chris@0 233 end
Chris@0 234 end
Chris@0 235