will_paginate array?

July 23rd, 2007 by comment

Today I started putting pagination in the app that I have been working on. Based on recommendations from Obie I decided to use “will_paginate”, a rails plugin for pagination put out by the err the blog guys. It worked amazingly and the view helper was great! I really like the fact that I can apply the same look and feel to all page pagination throughout the app… well umm.. until I wanted to add pagination to a collection not generated from a finder or association. Since I really wanted everything to look the same and behave the same I did the following little trick so that you can call paginate on a plain old array.

class Array
  def paginate(page=1, per_page=15)
    pagination_array = WillPaginate::Collection.new(page, per_page, self.size)
    start_index = pagination_array.offset
    end_index = start_index + (per_page - 1)
    array_to_concat = self[start_index..end_index]
    array_to_concat.nil? ? [] : pagination_array.concat(array_to_concat)
  end
end

Before folks say anything about the above code.. yes I know it could be more concise if I didn’t use all the local variables but I wanted it to be really clear what I was doing here so.. leave it alone.

Now basically you can say

myarray.paginate(params[:page], per_page)

If you want to see it work yourself feel free to run this spec.


require File.dirname(__FILE__) + '/../spec_helper'

describe 'Given we call paginate on an array' do
  it 'should return an array containing the first 3 elements of the org array when page = 1 and per_page_count = 3' do
    array = ["a","b","c","d","e"]
    current_page = 1
    show_per_page = 3
    expected_array = ["a", "b", "c"]
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it 'should return an array containing the last 2 elements of the org array when page = 2 and per_page_count = 3' do
    array = ["a","b","c","d","e"]
    current_page = 2
    show_per_page = 3
    expected_array = ["d", "e"]
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it 'should return an array containing all the elements of the org array when page = 1 and per_page_count = 5' do
    array = ["a","b","c","d","e"]
    current_page = 1
    show_per_page = 5
    expected_array = ["a","b","c","d","e"]
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it 'should return an array containing all the elements of the org array when page = 1 and per_page_count greater than org number of elements i.e = 6' do
    array = ["a","b","c","d","e"]
    current_page = 1
    show_per_page = 6
    expected_array = ["a","b","c","d","e"]
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it 'should return an empty array if you ask for a page that does not exist' do
    array = ["a","b","c","d","e"]
    current_page = 3
    show_per_page = 5
    expected_array = []
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it 'should return an empty array if you ask for a negative page number' do
    array = ["a","b","c","d","e"]
    current_page = -1
    show_per_page = 5
    expected_array = []
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it 'should return an empty array if you ask for a negative per_page number' do
    array = ["a","b","c","d","e"]
    current_page = 1
    show_per_page = -5
    expected_array = []
    (array.paginate(current_page, show_per_page)).should == expected_array
  end
end

Comments

14 Responses to “will_paginate array?”

  1. Nola says:


    cool, thanks for that. I was about ready to starting using that plugin and nice to see a different use of it.. yay for rspec!

  2. carlivar says:


    You can also take advantage of the existing Paginator to do this. Although now that I think about it, I’m too much of a Rails n00b still to know how to make the Paginator model available while extending Array like this…


    class Array
    def paginate(page=1, per_page=15)
    pager = Paginator.new(self, self.size, per_page, page)
    returning WillPaginate::Collection.new(page, per_page, self.size) do |p|
    p.replace self[pager.current.offset, pager.items_per_page]
    end
    end
    end

  3. Desi says:


    Hi Carlivar,
    At first glance I thought you were referring to the Paginator gem that was released by Bruce Williams so I was all sorts of confused but .. once I figured out you were talking about Paginator from ActionController class I gave it a try.

    You are right you could do that as well but there was something funny feeling about doing an include ActionController::Pagination inside Array. Also the Paginator class in ActionController talks about controllers and such and that felt weird.

    I ran it against my spec and it ran fine except that I typically return an empty array on neg page and per_page where Paginator returns either the array you gave it or an exception. Basically your solution works as well all you have to do is include the Paginator class in Array.

  4. Larry says:


    Hi,

    The good news is that I think this is exactly what I need. (Thank you!) The bad news is that I think I am doing something stupid.

    I copied the code you gave for adding the ‘paginate’ method to the ‘Array’ class and pasted it inside my controller class. I have an array called ‘activities’, however, when I make the ‘activities.paginate(params[:page], 5) call I get the following error message: undefined method `paginate’ for #

    Can you please tell me what I am doing wrong? Thanks.

  5. Larry says:


    Hello again,

    My previous post got chopped. The error message said “undefined method ‘paginate’ for Array”.

  6. Larry says:


    A solution… but then a problem.

    I looked around the web and found an alternative way to open up a class and add a method. Instead of simply opening up the Array class by defining it, I did this:

    Array.class_eval do
    def paginate(~~~)
    ~~~~~~~~
    end
    end

    The article said you could also simply define the class the way it was illustrated on this site, but that did not work for me and I don’t know why. Any explanation would be appreciated.

    At that point I thought I was home free, but the paginator did not work. I have a simple 5 element array. When I display those elements without the paginator they all appear correctly.

    However, when I display the exact same array using the paginator the elements are wrong. I list 3 elements per page just to test the paginator. The first 3 elements are correct. But when I click the ’2′ page I get a repeat of the last 2 elements that were displayed on the first page. So I get the right number (5) but I get 3 elements and then 2 duplicates.

    To make matters worse (or perhaps to aid in discovering what is going wrong, if we are to look at the glass as being half full), the ‘Next’ button does not work. I notice that on other pages where the paginator does work, i.e. where I’m using it *not* on an array, there is a ‘?page=2′ tacked onto the end of the ‘Next’ URL. There is no such argument when using the paginator on an array, which is obviously why the ‘Next’ button does nothing.

    Has anyone else experienced these problems? I’d love to get this thing working with arrays!

  7. desi says:


    Hi Larry,
    Sorry I didn’t respond sooner. I hope that this response is not too late and that I can help you figure out what it going on.

    What version of the will_paginate plugin are you using? The reason I ask is because I think the errtheblog guys added this functionality to the plugin recently.

    In your view do you have something that looks like this < %= will_paginate your_array %>?

    As for the troubles with Array I usually have a core_ext directory under my lib directory which is where I put core classes that I open up. When you put it here it should be available to arrays anywhere in your rails app.

    Can you cut the code snippits and put it to here or some where where I can see all the code view, array, controller etc.

  8. Larry says:


    Hi Desi,

    “Sorry for not responding sooner”? Are you kidding? Your response timing was GREAT! (Thanks.) I will try the things that you mentioned and get back to you with the outcome, as it might serve to help others as well.

    Not sure if I will get a chance to do this today, though. Got a case of the ol’ “they want a whole bunch of stuff done by tomorrow” blues.

    Thanks again, Desi.

  9. Larry says:


    Hi Desi,

    I got a chance to update my plugin. My original files had July dates, the new ones September, so I guess it was updated. (I could not tell from the doc or the site.)

    Anyway, I tried it and got the following error:
    undefined method `to_i’ for {:page=>nil, :per_page=>3}:Hash

    I googled that and found out someone else had the same problem. Hopefully the errtheblog guys are working on this. (I posted on their site too, but have not gotten a response.)

    Here is the URL describing the problem:
    http://err.lighthouseapp.com/projects/466/tickets/85-failures-linked-to-association-extensions

    I figured I’d throw this info your way in case others are monitoring this discussion.

    BTW, thanks for the heads up about ‘core_ext’. That kind of Ruby/Rails folklore that’s not in the books (at least I haven’t seen it) is invaluable.

    Thanks for everything,
    Larry

  10. Zeta says:


    Larry,

    Using the latest will_paginate plugin I bumped in the same problem as yours, and the thing is that for an array you should call the paginate method not with a hash with page and per_page keys, but with two parameters (page, per_count) as in Desi extension:

    ss = ["s1","s2","s3","s4"]
    ss.paginate(1,2)

    this returns page 1 of per_page=2

    ["s1","s2"]

    Cheers,
    Z

  11. Zeta says:


    Sorry,

    I’ve noticed that my will_paginate plugin is also out of date, and in the new release you can call the paginate method:

    ss.paginate params

    where params is a hash with the page and per_page keys…

    Z

  12. devpuppy says:


    thanks, worked like a charm!

  13. Scott says:


    I’m sure this issue doesn’t have much to do with the original entry… but I had a `require’: no such file to load — readline (LoadError), after migrating to a new server.

    The solution was to add ‘require “will_paginate”‘ to the environment.rb at the _very_ bottom. Something to do with versions and using the will_paginate plugin.

  14. Nicholas Johnson says:


    As Zeta mentions, will_paginate on an array is called slightly differently now and accepts a hash of values so, assuming @people is an array, you can do:

    @people.paginate params

    or if you need more control:

    @people.paginate :page => params[:page], :per_page => 28

Got something to say?


cheap research papers