I've been playing with will_paginate, and thought I would mix it in
with active_scaffold a bit. I've created a patch to replace the AS
finder logic with will_paginate, moved a few of the controller methods
to the model, and updated the controller actions/views. I've run a
couple of simple tests, and haven't seen any difference in the
generated SQL, but, since I've already written it, I thought I'd put
this out there in case someone else wanted to through some more stuff
at it.
You'll have to specify load order so that will_paginate loads before
active_scaffold, since the default is alphabetical.
I haven't used any of the will_paginate fanciness for searches like
"paginate_by_filed_name" yet, I've just replaced most of the
functionality of the finder class. Also, some of the logic moved to
the model might have been done better. I have a awful lot of calls to
"active_scaffold_config.model..." in my controllers.
Anyways, here it is:
Index: frontends/default/views/_list.rhtml
--- frontends/default/views/_list.rhtml (revision 731)
+++ frontends/default/views/_list.rhtml (working copy)
@@ -10,7 +10,7 @@
<div id="<%= active_scaffold_messages_id -%>">
<%= render :partial => 'messages' %>
</div>
- <p id="<%= empty_message_id %>" class="empty-message" <%= '
style="display:none;" ' unless @page.items.empty? %>>
+ <p id="<%= empty_message_id %>" class="empty-message" <%= '
style="display:none;" ' unless @records.empty? %>>
<%= as_('No Entries') %>
</p>
</td>
@@ -18,7 +18,7 @@
</tbody>
<tbody class="records" id="<%= active_scaffold_tbody_id %>">
<% if !...@records.empty? -%>
- <%= render :partial => 'list_record', :collection =>
@page.items, :locals => { :hidden => false } %>
+ <%= render :partial => 'list_record', :collection =>
@records, :locals => { :hidden => false } %>
<% if active_scaffold_config.list.columns.any? {|c|
c.calculation?} -%>
<%= render :partial => 'list_calculations' %>
<% end -%>
@@ -26,9 +26,10 @@
</tbody>
</table>
<div class="active-scaffold-footer">
- <div class="active-scaffold-found"><span class="active-scaffold-
records"><%= @page.pager.count -%></span> <%=as_('Found') %></div>
+ <div class="active-scaffold-found"><span class="active-scaffold-
records"><%= @records.total_entries -%></span> <%=as_('Found') %></
div>
<div class="active-scaffold-pagination">
- <%= render :partial => 'list_pagination_links', :locals =>
{ :current_page => @page } unless @page.pager.number_of_pages < 2 %>
+ <%= loading_indicator_tag :action => :pagination %>
+ <%= will_paginate @records, :renderer =>
'ActiveScaffoldPaginationLinkRende
rer' %>
</div>
<br clear="both" /><%# a hack for the Rico Corner problem %>
</div>
\ No newline at end of file
Index: lib/actions/core.rb
--- lib/actions/core.rb Base (BASE)
+++ lib/actions/core.rb Locally Modified (Based On LOCAL)
@@ -76,9 +76,9 @@
params.reject {|key, value| [:controller, :action, :id].include?
(key.to_sym)}.each do |key, value|
next unless active_scaffold_config.model.column_names.include?
(key)
if value.is_a?(Array)
- conditions = merge_conditions(conditions,
["#{active_scaffold_config.model.table_name}.#{key.to_s} in (?)",
value])
+ conditions =
active_scaffold_config.model.merge_conditions(conditions,
["#{active_scaffold_config.model.table_name}.#{key.to_s} in (?)",
value])
else
- conditions = merge_conditions(conditions,
["#{active_scaffold_config.model.table_name}.#{key.to_s} = ?", value])
\ No newline at end of file
+ conditions =
active_scaffold_config.model.merge_conditions(conditions,
["#{active_scaffold_config.model.table_name}.#{key.to_s} = ?", value])
\ No newline at end of file
end
end
conditions
Index: lib/actions/field_search.rb
--- lib/actions/field_search.rb Base (BASE)
+++ lib/actions/field_search.rb Locally Modified (Based On LOCAL)
@@ -30,7 +30,7 @@
params[:search].each do |key, value|
next unless
active_scaffold_config.field_search.columns.include?(key)
column = active_scaffold_config.columns[key]
- conditions = merge_conditions(conditions,
ActiveScaffold::Finder.condition_for_column(column, value,
like_pattern))
+ conditions =
active_scaffold_config.model.merge_conditions(conditions,
ActiveScaffold::Finder.condition_for_column(column, value,
like_pattern))
end
self.active_scaffold_conditions = conditions
Index: lib/actions/list.rb
--- lib/actions/list.rb Base (BASE)
+++ lib/actions/list.rb Locally Modified (Based On LOCAL)
@@ -49,7 +49,13 @@
includes_for_list_columns =
active_scaffold_config.list.columns.collect{ |c|
c.includes }.flatten.uniq.compact
self.active_scaffold_joins.concat includes_for_list_columns
- options = {:sorting =>
active_scaffold_config.list.user.sorting,}
+ options = {
+ :order => active_scaffold_config.model.build_order_clause(
+ active_scaffold_config.list.user.sorting),
+ :conditions => all_conditions,
+ :joins => joins_for_collection,
+ :include => active_scaffold_joins.empty? ? nil :
active_scaffold_joins
+ }
paginate = (params[:format].nil?) ? (accepts? :html, :js) :
[:html, :js].include?(params[:format])
if paginate
options.merge!({
@@ -58,12 +64,10 @@
})
end
- page = find_page(options);
- if page.items.empty?
- page = page.pager.first
+ @records = active_scaffold_config.model.find_page(options);
+ if @records.empty?
active_scaffold_config.list.user.page = 1
end
- @page, @records = page, page.items
\ No newline at end of file
end
# The default security delegates to ActiveRecordPermissions.
Index: lib/actions/live_search.rb
--- lib/actions/live_search.rb Base (BASE)
+++ lib/actions/live_search.rb Locally Modified (Based On LOCAL)
@@ -26,7 +26,7 @@
unless @query.empty?
columns = active_scaffold_config.live_search.columns
like_pattern =
active_scaffold_config.live_search.full_text_search? ? '%?%' : '?%'
- self.active_scaffold_conditions =
merge_conditions(self.active_scaffold_conditions,
ActiveScaffold::Finder.create_conditions_for_columns(@query.split('
'), columns, like_pattern))
+ self.active_scaffold_conditions =
active_scaffold_config.model.merge_conditions(self.active_scaffold_conditio ns,
ActiveScaffold::Finder.create_conditions_for_columns(@query.split('
'), columns, like_pattern))
includes_for_search_columns = columns.collect{ |column|
column.includes}.flatten.uniq.compact
self.active_scaffold_joins.concat includes_for_search_columns
Index: lib/actions/search.rb
--- lib/actions/search.rb Base (BASE)
+++ lib/actions/search.rb Locally Modified (Based On LOCAL)
@@ -20,7 +20,7 @@
unless @query.empty?
columns = active_scaffold_config.search.columns
like_pattern =
active_scaffold_config.search.full_text_search? ? '%?%' : '?%'
- self.active_scaffold_conditions =
merge_conditions(self.active_scaffold_conditions,
ActiveScaffold::Finder.create_conditions_for_columns(@query.split('
'), columns, like_pattern))
+ self.active_scaffold_conditions =
active_scaffold_config.model.merge_conditions(self.active_scaffold_conditio ns,
ActiveScaffold::Finder.create_conditions_for_columns(@query.split('
'), columns, like_pattern))
includes_for_search_columns = columns.collect{ |column|
column.includes}.flatten.uniq.compact
self.active_scaffold_joins.concat includes_for_search_columns
Index: lib/constraints.rb
--- lib/constraints.rb Base (BASE)
+++ lib/constraints.rb Locally Modified (Based On LOCAL)
@@ -71,7 +71,7 @@
raise ActiveScaffold::MalformedConstraint,
constraint_error(active_scaffold_config.model, k), caller
end
- conditions = merge_conditions(conditions,
constraint_condition)
+ conditions =
active_scaffold_config.model.merge_conditions(conditions,
constraint_condition)
end
conditions
@@ -107,7 +107,7 @@
condition = constraint_condition_for("#{table}.#{field}",
value)
if association.options[:polymorphic]
- condition = merge_conditions(
+ condition = active_scaffold_config.model.merge_conditions(
condition,
constraint_condition_for("#{table}.#{association.name}_type",
params[:parent_model].to_s)
)
Index: lib/extensions/finder_methods.rb
--- lib/extensions/finder_methods.rb Locally New
+++ lib/extensions/finder_methods.rb Locally New
@@ -0,0 +1,60 @@
+class ActiveRecord::Base
+
+ # returns a Paginator::Page (not from ActiveRecord::Paginator)
for the given parameters
+ # options may include:
+ # * :sorting - a Sorting DataStructure (basically an array of
hashes of field => direction, e.g. [{:field1 => 'asc'}, {:field2 =>
'desc'}]). please note that multi-column sorting has some limitations:
if any column in a multi-field sort uses method-based sorting, it will
be ignored. method sorting only works for single-column sorting.
+ # * :per_page
+ # * :page
+ def self.find_page(options = {})
+
options.assert_valid_keys :order, :per_page, :page, :joins, :include, :conditions
+ options[:per_page] ||= 999999999
+ options[:page] ||= 1
+
+ self.paginate options
+#
+# # NOTE: we must use :include in the count query, because some
conditions may reference other tables
+# count = klass.count(finder_options.reject{|k,v|
[:order].include? k})
+#
+# # we build the paginator differently for method- and sql-based
sorting
+# if options[:sorting] and options[:sorting].sorts_by_method?
+# pager = ::Paginator.new(count, options[:per_page]) do |
offset, per_page|
+# sorted_collection =
sort_collection_by_column(klass.find(:all, finder_options),
*options[:sorting].first)
+# sorted_collection.slice(offset, per_page)
+# end
+# else
+# pager = ::Paginator.new(count, options[:per_page]) do |
offset, per_page|
+# klass.find(:all, finder_options.merge(:offset =>
offset, :limit => per_page))
+# end
+# end
+#
+# pager.page(options[:page])
+ end
+
+ def self.merge_conditions(*conditions)
+ c = conditions.find_all {|c| not c.nil? and not c.empty? }
+ c.empty? ? nil : c.collect{|c| self.send(:sanitize_sql,
c)}.join(' AND ')
+ end
+
+ protected
+
+ # accepts a DataStructure::Sorting object and builds an order-by
clause
+ def self.build_order_clause(sorting)
+ return nil if sorting.nil? or sorting.sorts_by_method?
+
+ # unless the sorting is by method, create the sql string
+ order = []
+ sorting.each do |clause|
+ sort_column, sort_direction = clause
+ sql = sort_column.sort[:sql]
+ next if sql.nil? or sql.empty?
+
+ order << "#{sql} #{sort_direction}"
+ end
+
+ order = order.join(', ')
+ order = nil if order.empty?
+
+ order
+ end
+
+end
\ No newline at end of file
Index: lib/finder.rb
--- lib/finder.rb Base (BASE)
+++ lib/finder.rb Locally Modified (Based On LOCAL)
@@ -51,7 +51,7 @@
end
def all_conditions
- merge_conditions(
+ active_scaffold_config.model.merge_conditions(
active_scaffold_conditions, # from the
search modules
conditions_for_collection, # from the dev
conditions_from_params, # from the
parameters (e.g. /users/list?first_name=Fred)
@@ -70,70 +70,6 @@
return record
end
- # returns a Paginator::Page (not from ActiveRecord::Paginator)
for the given parameters
- # options may include:
- # * :sorting - a Sorting DataStructure (basically an array of
hashes of field => direction, e.g. [{:field1 => 'asc'}, {:field2 =>
'desc'}]). please note that multi-column sorting has some limitations:
if any column in a multi-field sort uses method-based sorting, it will
be ignored. method sorting only works for single-column sorting.
- # * :per_page
- # * :page
- # TODO: this should reside on the model, not the controller
- def find_page(options = {})
- options.assert_valid_keys :sorting, :per_page, :page
- options[:per_page] ||= 999999999
- options[:page] ||= 1
-
- klass = active_scaffold_config.model
-
- # create a general-use options array that's compatible with
Rails finders
- finder_options = { :order =>
build_order_clause(options[:sorting]),
- :conditions => all_conditions,
- :joins => joins_for_collection,
- :include => active_scaffold_joins.empty? ?
nil : active_scaffold_joins}
-
- # NOTE: we must use :include in the count query, because some
conditions may reference other tables
- count = klass.count(finder_options.reject{|k,v|
[:order].include? k})
-
- # we build the paginator differently for method- and sql-based
sorting
- if options[:sorting] and options[:sorting].sorts_by_method?
- pager = ::Paginator.new(count, options[:per_page]) do |
offset, per_page|
- sorted_collection =
sort_collection_by_column(klass.find(:all, finder_options),
*options[:sorting].first)
- sorted_collection.slice(offset, per_page)
- end
- else
- pager = ::Paginator.new(count, options[:per_page]) do |
offset, per_page|
- klass.find(:all, finder_options.merge(:offset =>
offset, :limit => per_page))
- end
- end
-
- pager.page(options[:page])
- end
-
- # TODO: this should reside on the model, not the controller
- def merge_conditions(*conditions)
- c = conditions.find_all {|c| not c.nil? and not c.empty? }
- c.empty? ? nil : c.collect{|c|
active_scaffold_config.model.send(:sanitize_sql, c)}.join(' AND ')
- end
-
- # accepts a DataStructure::Sorting object and builds an order-by
clause
- # TODO: this should reside on the model, not the controller
- def build_order_clause(sorting)
- return nil if sorting.nil? or sorting.sorts_by_method?
-
- # unless the sorting is by method, create the sql string
- order = []
- sorting.each do |clause|
- sort_column, sort_direction = clause
- sql = sort_column.sort[:sql]
- next if sql.nil? or sql.empty?
-
- order << "#{sql} #{sort_direction}"
- end
-
- order = order.join(', ')
- order = nil if order.empty?
-
- order
- end
-
\ No newline at end of file
# TODO: this should reside on the column, not the controller
def sort_collection_by_column(collection, column, order)
sorter = column.sort[:method]
Index: lib/helpers/active_scaffold_pagination_link_renderer.rb
--- lib/helpers/active_scaffold_pagination_link_renderer.rb Locally
New
+++ lib/helpers/active_scaffold_pagination_link_renderer.rb Locally
New
@@ -0,0 +1,21 @@
+# app/helpers/remote_link_renderer.rb
+
+class ActiveScaffoldPaginationLinkRenderer <
WillPaginate::LinkRenderer
+ def page_link_or_span(page, span_class = 'current', text = nil)
+ text ||= page.to_s
+ pagination_params = @template.params_for(:action =>
'update_table')
+ if page and page != current_page
+ @template.link_to_remote text, {
+ :url => pagination_params.merge(:page => page),
+ :method => :get,
+ :after => "$('...@template.loading_indicator_id(:action
=> :pagination)}').style.visibility = 'visible';",
+ :complete => "$('...@template.loading_indicator_id(:action
=> :pagination)}').style.visibility = 'hidden';",
+ :failure =>
"ActiveScaffold.report_500_response('...@template.active_scaffold_id}')",
+ :update => @template.active_scaffold_content_id,
+ },
+ :class => "previous"
+ else
+ @template.content_tag :span, text, :class => span_class
+ end
+ end
+end
\ No newline at end of file
Index: lib/helpers/association_helpers.rb
--- lib/helpers/association_helpers.rb Base (BASE)
+++ lib/helpers/association_helpers.rb Locally Modified (Based On
LOCAL)
@@ -3,11 +3,11 @@
module Associations
# Provides a way to honor the :conditions on an association
while searching the association's klass
def association_options_find(association, conditions = nil)
- association.klass.find(:all, :conditions =>
controller.send(:merge_conditions, conditions,
association.options[:conditions]))
+ association.klass.find(:all, :conditions =>
association.klass.send(:merge_conditions, conditions,
association.options[:conditions]))
end
def association_options_count(association, conditions = nil)
- association.klass.count(:all, :conditions =>
controller.send(:merge_conditions, conditions,
association.options[:conditions]))
+ association.klass.count(:all, :conditions =>
association.klass.send(:merge_conditions, conditions,
association.options[:conditions]))
end
# returns options for the given association as a collection of
[id, label] pairs intended for the +options_for_select+ helper.