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.
Alain R. (Guest)
on 2005-12-16 02: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.
Duane J. (Guest)
on 2005-12-16 05:45
(Received via mailing list)
On Dec 15, 2005, at 5:19 PM, Alain R. 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 J.
(canadaduane)
Alain R. (Guest)
on 2005-12-16 11: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
Izidor J. (Guest)
on 2005-12-16 12:28
(Received via mailing list)
On Dec 16, 2005, at 10:23 AM, Alain R. 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
Izidor J. (Guest)
on 2005-12-16 12:37
(Received via mailing list)
On Dec 16, 2005, at 11:26 AM, Izidor J. 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
Izidor J. (Guest)
on 2005-12-16 12:43
(Received via mailing list)
On Dec 16, 2005, at 11:34 AM, Izidor J. 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
Alain R. (Guest)
on 2005-12-16 15: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?
Francois GORET (Guest)
on 2005-12-16 15:59
(Received via mailing list)
On Friday 16 December 2005 20:45, Alain R. 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.
Alain R. (Guest)
on 2005-12-16 16: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
Wilson B. (Guest)
on 2005-12-16 18:36
(Received via mailing list)
On 12/16/05, Alain R. <removed_email_address@domain.invalid> 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
Alain R. (Guest)
on 2005-12-16 21: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'
Wilson B. (Guest)
on 2005-12-17 04: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.
Alain R. (Guest)
on 2005-12-17 15:47
Wilson B. 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
Wilson B. (Guest)
on 2005-12-17 23: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.