I'm trying to figure out how to handle pagination when I have a very
complex way of building up a collection of objects. I've used the old
classic pagination before, and I've used will_paginate, but I don't
think either will work in my case.
Briefly, I have a Location model that defines a physical location. I
then build up a list of @locations by a variety of "filters"... for
example, a text-based search query plus a geolocation radial search,
plus one or more category selections. Underneath the hood, these are
each separate queries that build a list of location objects. I then
merge the results into a single instance variable: @locations, which
is the array of matched Location objects.
Now, I wanna paginate that.
I suppose one option might be for me to try and build the thing into a
single, monster SQL query that I can then add some LIMIT fields to...
but for now, let's assume that's not an option. (I shiver at the
complexity of it. oy!)
So... is there anything I can do with an existing set? Can
will_paginate support that? (I'm thinking no, but I figured I'd ask).
I think the classic_pagination plugin can, but I'm not really sure
how.
I was gonna just delete this post as I figured it out, but I'll leave
it and post my solution in case it helps anyone else.
Turns out will_paginate can handle this. You just wrap your Array with
a WillPaginate::Collection and split it manually into the "windows"
needed/desired for pagination.
Here's my code snippit. I start with @locations being the full array
of matched locations. Then...
[code]
page = (params[:page] ||= 1).to_i
PAGE_SIZE = 10 # could be defined somewhere global
offset = (page - 1) * PAGE_SIZE
@locations = WillPaginate::Collection.new(page, PAGE_SIZE,
"#{@locations.length}").concat(@locations[offset..(offset + PAGE_SIZE
- 1)])
[/code]
Then, I just use the normal: <%= will_paginate @locations %> code in
my view and voila! Instant pagination! Whee!
"Dang, that was easy." <-- me, about so many things of the Rails
persuasion.
Getting the final @locations variable looks complicated to me, is this something you had to code for the solution to work, or is this how you’ve been using will_paginate from the beginning?
If the @locations collection is that complicated to assemble you might
also want to introduce some caching on the results. Unless I
misunderstand what you're doing it appears that you are going to have
to calculate that collection on every page request and then select the
appropriate results to return.
It was coded that way because of the application needs. Ultimately,
it's that I have a potentially large set of Location objects and the
end-user can choose one or more methods of "whittling" that set down
(search, geolocation, categories, etc). So I have mulitple, complex
finds that AND or OR merge together for a final results set. And that
final set is what I need to paginate.
It may be worth a refactor attempt, but I think I might just have to
live with it as-is and just do as much performance enhancements as I
can... i.e. like caching as Andy suggested.
I was gonna just delete this post as I figured it out, but I'll leave
it and post my solution in case it helps anyone else.
Turns out will_paginate can handle this. You just wrap your Array with
a WillPaginate::Collection and split it manually into the "windows"
needed/desired for pagination.
Here's my code snippit. I start with @locations being the full array
of matched locations. Then...
[code]
page = (params[:page] ||= 1).to_i
PAGE_SIZE = 10 # could be defined somewhere global
offset = (page - 1) * PAGE_SIZE
@locations = WillPaginate::Collection.new(page, PAGE_SIZE,
"#{@locations.length}").concat(@locations[offset..(offset + PAGE_SIZE
- 1)])
[/code]
Then, I just use the normal: <%= will_paginate @locations %> code in
my view and voila! Instant pagination! Whee!
Here is an even simpler way:
[code]
@locations = WillPaginate::Collection.create(page, PAGE_SIZE,
locations.size) do |pager|
pager.replace(locations[pager.offset,
[pager.per_page, locations.size].min])
end
[/code]
locations is the full array of locations, @locations is the WillPaginate
object.
HTH,
Jeffrey
P.S. not tested or guaranteed to work. I copy and pasted it from working code
with editing of variables to match your code.