Faking a Will Paginate Collection on an Active Resource model

October 28th, 2008 by

To follow up on my other blog post about Paginating an ActiveResource model and to_xml I figured I should include the client-side code so you can see how to actually use will paginate helpers out of the box to paginate an ActiveResource request.

This is example code from my model:

  def self.paginate(*args, &block)
    options = args.pop
    page, per_page, total_entries = wp_parse_options(options)
 
    WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
      count_options = options.except :page, :per_page, :total_entries, :finder
      find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page) 
 
      args << find_options
      # @options_from_last_find = nil
      find_results = self.find(*args, &block)
      pager.replace find_results
      # magic counting for user convenience:
      pager.total_entries = find_results.total_entries unless find_results.blank?
    end
  end

Example View code:

<%= render :partial => 'list' %>
<%= will_paginate(@model)%>

Making WillPaginate and Rails to_xml play nice with ActiveResource

October 10th, 2008 by

We are currently working on a project that involves Flex and active resource + will_paginate and we needed to be able to paginate the xml transactions easily. Unfortunately, will_paginate and to_xml don’t play nicely when it comes to adding the current_page, total_pages, and page attributes to the xml. After many failed attempts I went looking around github and found in a few forks of will paginate that some people had solved this problem but, I didn’t want to install another version of the gem to risk breaking other apps on the server so I did it the rails way!

I started by creating a module that opens up the will_paginate collection class and includes ActiveResource and alias method chain the to_xml method to include these values. Example code below.

#enviroment.rb
...
require 'to_xml_extensions'
#lib/to_xml_extensions.rb
module WillPaginateHelpers
  include ActiveSupport::CoreExtensions::Array::Conversions
  def to_xml_with_collection_type(options = {})
        serializeable_collection.to_xml_without_collection_type(options) do |xml|
          xml.tag!(:current_page, {:type => ActiveSupport::CoreExtensions::Hash::Conversions::XML_TYPE_NAMES[current_page.class.name]}, current_page)
          xml.tag!(:per_page, {:type => ActiveSupport::CoreExtensions::Hash::Conversions::XML_TYPE_NAMES[per_page.class.name]}, per_page)
          xml.tag!(:total_entries, {:type => ActiveSupport::CoreExtensions::Hash::Conversions::XML_TYPE_NAMES[total_entries.class.name]}, total_entries)
        end.sub(%{type="array"}, %{type="collection"})
      end
      alias_method_chain :to_xml, :collection_type
 
      def serializeable_collection #:nodoc:
        # Ugly hack because to_xml will not yield the XML Builder object when empty?
        empty? ? returning(self.clone) { |c| c.instance_eval {|i| def empty?; false; end } } : self
      end
end
 
WillPaginate::Collection.send(:include, WillPaginateHelpers)

This now gives me the proper xml when I call to_xml

<?xml version="1.0" encoding="UTF-8"?>
<time-cards type="collection">
  <current_page type="integer">1</current_page>
  <per_page type="integer">25</per_page>
  <total_entries type="integer">108</total_entries>
  <time_card>
    <approved type="boolean">false</approved>
    <billable type="boolean">false</billable>
    <created_at type="datetime">2008-10-10T14:04:13-04:00</created_at>
    <date type="datetime">2008-10-10T14:04:13-04:00</date>
    <has_been_billed type="boolean">false</has_been_billed>
    <has_been_paid type="boolean">true</has_been_paid>
    <hours type="float">2.0</hours>
    <id type="integer">98</id>
    <is_overtime type="boolean">false</is_overtime>
    <task_id type="integer">6</task_id>
    <updated_at type="datetime">2008-10-10T14:04:13-04:00</updated_at>
    <user_id type="integer">1</user_id>
  </time_card>
  ...
</timecards>