Forum: Ruby on Rails How to pass a collection to paginate?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B45eab4f18aa1bb2a44d6e657531a642?d=identicon&s=25 Alain Ravet (aravet)
on 2005-12-16 01:19
There must be a better way to write this code:

     @project_pages, @projects= paginate :project,
	     :per_page => 10,
	     :conditions => ["account_id = ?", account]

?!

If only I could pass the sub-collection
    account = ...
    @projects = account.project

to paginate, instead of letting it extract it with a find :all + sql
conditions


Alain.
D8cb8c8cd40ddf0cd05241443a591868?d=identicon&s=25 Duane Johnson (Guest)
on 2005-12-16 04:45
(Received via mailing list)
On Dec 15, 2005, at 5:19 PM, Alain Ravet wrote:

>     account = ...
>     @projects = account.project
>
> to paginate, instead of letting it extract it with a find :all + sql
> conditions
>
>
I've been wishing for this for a while as well.  I'm not sure if all
the pieces are in place yet, as I haven't made a complete prototype,
but here's one piece that might be useful if you're thinking of
fixing this problem:

http://dev.rubyonrails.org/ticket/2466

The idea is that you should be able to set a constraint where the
code within the block will have certain conditions (like yours)
enforced on it.  In that way, the pagination methods (or some
derivatives) will fetch the correct result set instead of :all.

Duane Johnson
(canadaduane)
B45eab4f18aa1bb2a44d6e657531a642?d=identicon&s=25 Alain Ravet (aravet)
on 2005-12-16 10:23
Duane
   > http://dev.rubyonrails.org/ticket/2466
   > The idea is that you should be able to set a constraint where
..

It would be much simpler/natural to simply feed the prevously fetched
collection to paginate.
=> we could write code like this:

     @project_pages, @projects= paginate :collection =>
@john.pending_projects,
	     :per_page => 10

Without a feature like this, we are forced to turn model querying
methods - m.pending_projects() - back into sql code for the condition.
It's really unlike Rais.

Alain
Dce47278b4de2597c378881b482d9cb6?d=identicon&s=25 Izidor Jerebic (Guest)
on 2005-12-16 11:28
(Received via mailing list)
On Dec 16, 2005, at 10:23 AM, Alain Ravet wrote:

> @john.pending_projects,
> 	     :per_page => 10

Well, adapted from API docs <http://api.rubyonrails.org/classes/
ActionController/Pagination.html> :

     @projects = someperson.projects
     @project_pages = Paginator.new self, @projects.length, 10, params
['page']

Is this what you are looking for?


izidor
455f4a636fdd56e8277921dc307100bd?d=identicon&s=25 Izidor Jerebic (Guest)
on 2005-12-16 11:37
(Received via mailing list)
On Dec 16, 2005, at 11:26 AM, Izidor Jerebic wrote:

>> => we could write code like this:
> params['page']
>
> Is this what you are looking for?

No, this is not what you are looking for :-)

One needs to also limit the @projects, something like this:

page = params['page'].to_i
page = 1 if page <= 0
offset = (page - 1) * 10
@projects = someperson.projects[ offset .. offset+10-1]

@project_pages = Paginator.new self, @projects.length, 10, params
['page']

izidor
Dce47278b4de2597c378881b482d9cb6?d=identicon&s=25 Izidor Jerebic (Guest)
on 2005-12-16 11:43
(Received via mailing list)
On Dec 16, 2005, at 11:34 AM, Izidor Jerebic wrote:

>>>
>> ActionController/Pagination.html> :
>
> page = params['page'].to_i
> page = 1 if page <= 0
> offset = (page - 1) * 10
> @projects = someperson.projects[ offset .. offset+10-1]
>
> @project_pages = Paginator.new self, @projects.length, 10, params
> ['page']
>

Heh, third time's a charm...

Object count must be from the full collection.

@project_pages = Paginator.new self, someperson.projects.length, 10,
params['page']


All this could be generalized and wrapped in a nice small helper,
e.g. paginate_collection or something....

izidor
B45eab4f18aa1bb2a44d6e657531a642?d=identicon&s=25 Alain Ravet (aravet)
on 2005-12-16 14:45
Izidor

Nice but...

There's still a problem: how to have the paginator add parameters in the
url?

For example, I want to list all the members for a given project, instead
of just all the members in the db (scaffold's default) :

part 1:
*******
=> in the view, I add the 'project' id as a parameter :

      <%= link_to  project.name,
           {:action => :list, :project => project}  %>

=> when clicked, it translates into

    http://localhost:3001/members/list?project=1

(Note the "project=1" at the end of the url)


part 2 :
********

In the list action, I filter the members by project

  def list

     @project = Project.find(params[:project])  <<- use the filter here

     records_per_page = 10
     page = params['page'].to_i
     page = 1 if page <= 0
     offset   = (page - 1) * records_per_page
     @members = @project.members[offset .. offset+records_per_page-1]
     @member_pages = Paginator.new self,
          @project.members.length,
          records_per_page,
          params['page']
  end


problem: I cannot have the paginator add/keep "project=1" in the params,
so it generates  this html

  <a href="/members/list?page=2">Next page</a>

(Note: no more "project=1" in the url)
=> when I click on "Next page", I get an error because the project id is
missing.


Any idea?
B7aa6f0356566cc801f769c6fc14ba1a?d=identicon&s=25 Francois GORET (Guest)
on 2005-12-16 14:59
(Received via mailing list)
On Friday 16 December 2005 20:45, Alain Ravet wrote:
> part 1:
> (Note the "project=1" at the end of the url)
>
>
>
> Any idea?

Keep the project_id in the session, and have it initialized when the
list is
called with an explicit project_id.

   def list
      @project_id = params[:project] || session[:current_project_id]
      session[:current_project_id] = @project_id
      @project = Project.find(@project_id)  <<- use the filter here

      records_per_page = 10
      page = params['page'].to_i
      page = 1 if page <= 0
      offset   = (page - 1) * records_per_page
      @members = @project.members[offset .. offset+records_per_page-1]
      @member_pages = Paginator.new self,
           @project.members.length,
           records_per_page,
           params['page']
   end

-----------------------

This will have the nice side effect that any selection of project will
be
"sticky", so the user can go to view / edit the project, go to other
parts of
the application and when come back to the list, still it's the "current"
project that is shown.
B45eab4f18aa1bb2a44d6e657531a642?d=identicon&s=25 Alain Ravet (aravet)
on 2005-12-16 15:22
François,

Thanks.

I'm spoiled: it's too much code for my eyes :) It hurts: I keep thinking
about where we started :

  def list
    @member_pages, @members = paginate :member, :per_page => 10
  end

All I wanted was a way to avoid adding the 'condition' clause, because
it looked a little ugly!!
:)


If 'scaffold' knew how to handle 'belongs_to', I'm sure somebody would
have felt the same pain, and created a clean and more Railish solution.

Alain
25e11a00a89683f7e01e425a1a6e305c?d=identicon&s=25 Wilson Bilkovich (Guest)
on 2005-12-16 17:36
(Received via mailing list)
On 12/16/05, Alain Ravet <alainravet-spam2004@yahoo.com> wrote:
>
Here's some example code from one of my projects.  I can't remember
who sent over the original 'paginate_collection' code, but please
pretend that I've given proper credit for the idea. Heh.

Hopefully it helps, and won't make your eyes bleed. Also, hopefully,
Gmail won't kill it with text-wrapping.

# This goes in some user library that you can load from
environment.rb, or you can put it in controllers/application.rb, etc.
  def paginate_collection(collection, options = {})
    # This helps us paginate collections that are extremely difficult
to produce via assocations.
    # Invoke it from a controller like this:
    # @pages, @users = paginate_collection(@some_collection, :page =>
params[:page])
    default_options = {:per_page => 10, :page => 1}
    options = default_options.merge options

    pages = Paginator.new self, collection.size, options[:per_page],
options[:page]
    first = pages.current.offset
    last = [first + options[:per_page], collection.size].min
    slice = collection[first...last]
    return [pages, slice]
  end

#controller method:
def list_exceptions
  @exceptions_for_user = App.find(:all, :conditions => ["user_id = ?
and dispositions.status_code = 'F'", session[:user].id], :include =>
[:disposition])
  @exception_pages, @exceptions =
paginate_collection(@exceptions_for_user, :page => params[:page])
end
0091f92762685860109bbcb02edfdf27?d=identicon&s=25 Alain Ravet (Guest)
on 2005-12-16 20:01
(Received via mailing list)
Wilson,

I feel I'm very close, but I keep getting a funny error : (full
stacktrace at bottom)

       undefined method `zero?' for "2":String

, where '2' is  my_collection.size


I looked into Rails' source, and it happens in the Paginator code :

       def page_count
         @page_count ||= @item_count.zero? ? 1 :   ## <<---- ERROR HERE
                           (q,r=@item_count.divmod(@items_per_page);
r==0? q : q+1)
       end


'@item_count' seems to be a string. How is this possible: the only place
its value is changed is in the constructor:

     class Paginator
       ...
       def initialize(controller, item_count, items_per_page,
current_page=1)
         ...
         @item_count = item_count || 0
     end

And 'item_count', the 2nd parameter, is set to the collection.size
      pages            = Paginator.new self, collection.size,
options[:per_page], options[:page]


Alain

Full code :
----------

class Member_controller < ..
   def   list
      @project = Project.find(1)
      @members, @member_pages = paginate_collection  @project.members ,
:page => params[:page]
   end
   ..
end



class ApplicationController < ActionController::Base

   def paginate_collection(collection, options = {})
      # Invoke it from a controller like this:
      # @pages, @users = paginate_collection(@some_collection, :page =>
params[:page])

      default_options  = {:per_page => 10, :page => 1}
      options          = default_options.merge options

      pages            = Paginator.new self, collection.size,
options[:per_page], options[:page]
      first            = pages.current.offset     # <<------- ERROR
OCCURS HERE
      last             = [first + options[:per_page],
collection.size].min
      slice            = collection[first...last]

      return [pages, slice]
    end
end








Error Stacktrace :

actionpack-1.11.2/... :
lib/action_controller/pagination.rb:253:in `page_count'
lib/action_controller/pagination.rb:261:in `has_page_number?'
lib/action_controller/pagination.rb:287:in `initialize'
lib/action_controller/pagination.rb:267:in `new'
lib/action_controller/pagination.rb:267:in `[]'
lib/action_controller/pagination.rb:235:in `current'
#{RAILS_ROOT}/app/controllers/application.rb:11:in `paginate_collection'
#{RAILS_ROOT}/app/controllers/members_controller.rb:12:in `list'
25e11a00a89683f7e01e425a1a6e305c?d=identicon&s=25 Wilson Bilkovich (Guest)
on 2005-12-17 03:26
(Received via mailing list)
Hrm. Can you really leave out the parentheses when doing
multiple-assignment like that?
Try using parens in the call to paginate_collection in your controller
action.
B45eab4f18aa1bb2a44d6e657531a642?d=identicon&s=25 Alain Ravet (aravet)
on 2005-12-17 14:47
Wilson Bilkovich wrote:
   > Try using parens in the call to paginate_collection in your
controller
   > action.


Same problem, even when I use parentheses :

     @members, @member_pages = paginate_collection  (@project.members ,
              :page => params[:page])


Alain
25e11a00a89683f7e01e425a1a6e305c?d=identicon&s=25 Wilson Bilkovich (Guest)
on 2005-12-17 22:28
(Received via mailing list)
Try taking the space out between paginate_collection and the opening
parentheses.
Hopefully I'm not leading out astray here. Heh.
This topic is locked and can not be replied to.