Will_paginate plugin doesn't work with Association Extension


#1

I have:

class Post < ActiveRecord::Base
has_many :comments do
def published
find( :all,
:conditions => {:published => true} )
end
end
end

When in my controller I do

Post.find(:first).comments.published.paginate :page => params[:page]

I get an error

undefined method `paginate’ for []:Array

Is will_paginate supposed to work through Association Extensions?

-christos


#2

On 7/24/07, Christos Z. removed_email_address@domain.invalid wrote:

end

When in my controller I do

Post.find(:first).comments.published.paginate :page => params[:page]

I get an error

undefined method `paginate’ for []:Array

Is will_paginate supposed to work through Association Extensions?

Nope, it’s returning an Array, not an AssociationProxy.

has_many :published_articles, :class_name => ‘Article’, :conditions =>
{:published=>true}


Rick O.
http://lighthouseapp.com
http://weblog.techno-weenie.net
http://mephistoblog.com


#3

On 7/24/07, Obie F. removed_email_address@domain.invalid wrote:

Desi just posted last night about using will_paginate with arrays.
http://www.devchix.com/2007/07/23/will_paginate-array/

That paginates after the select. I wouldn’t recommend this for a
large association.


Rick O.
http://lighthouseapp.com
http://weblog.techno-weenie.net
http://mephistoblog.com


#4

Is there any better way to do this? I’ve run into this situation a few
times where I have a query which is a little bit too complicated to
turn into an active record association, but I still need to paginate
the results… For example, I have an Event model which
has_many :line_items, and each LineItem has_one :ticket_info (I need
to store extra information about tickets such as a list of names
attending the event)… I need to get a list of ticket information for
an event by doing something like @event.tickets, but I can’t paginate
the results since it returns an array. Can this be turned into an
AssociationProxy to be used with will_paginate?

Thanks,


#5

Desi just posted last night about using will_paginate with arrays.
http://www.devchix.com/2007/07/23/will_paginate-array/

On 7/24/07, Rick O. removed_email_address@domain.invalid wrote:

  end

http://weblog.techno-weenie.net
http://mephistoblog.com


Obie F.
http://jroller.com/obie/

Pre-order my book The Rails Way today!


#6

On 7/24/07, byforcesunseen removed_email_address@domain.invalid wrote:

AssociationProxy to be used with will_paginate?
You can call paginate instead of find in your complex finder.


Rick O.
http://lighthouseapp.com
http://weblog.techno-weenie.net
http://mephistoblog.com


#7

On Jul 24, 6:21 pm, “Rick O.” removed_email_address@domain.invalid wrote:

attending the event)… I need to get a list of ticket information for
an event by doing something like @event.tickets, but I can’t paginate
the results since it returns an array. Can this be turned into an
AssociationProxy to be used with will_paginate?

You can call paginate instead of find in your complex finder.

yeah, that’s what I guess I’ll have to end up doing… I wanted to
avoid this since I’ll end up duplicating my find method wherever I
need to paginate the list of tickets for an Event… Using
@event.tickets.paginate if much preferred, but unfortunately it
doesn’t work. If anyone has any other ideas, please let me know, as
I’ve come across this problem more than once and would like to figure
out an ideal solution. Thanks,


#8

yeah, that’s what I guess I’ll have to end up doing… I wanted to
avoid this since I’ll end up duplicating my find method wherever I
need to paginate the list of tickets for an Event… Using
@event.tickets.paginate if much preferred, but unfortunately it
doesn’t work. If anyone has any other ideas, please let me know, as
I’ve come across this problem more than once and would like to figure
out an ideal solution. Thanks,

OK, first the disclaimer:

I’ve only been using Ruby/Rails for a few weeks, so am very new to
this, and so it is entirely feasible that there is something hideously
wrong with my solution; however, this is what I’ve come up with to
solve this problem for me:

First of all, I have defined a Class called PaginatableFinder (it’s
the third name it’s had in as many hours):

class PaginatableFinder
attr_reader :parent

def initialize(parent, method, *args, &extension)
@parent, @method, @args = parent, method, args

# if a block is given, use it to exetend this object
if block_given?
  extend(Module.new(&extension))
end

end

define a paginate method to gracefuly handle pagination

def paginate(paginate_options = nil)
# replace ‘find’ with ‘paginate’ in the method name
@method = @method.to_s.sub(‘find’, ‘paginate’).to_sym

# get the options from the arguments, or create an empty hash
options = @args.last.is_a?(Hash) ? @args.pop : {}

# merge the paginate options in with the standard options, if

necessary
if paginate_options
options.merge!(paginate_options)
(paginate_options[:conditions] << ’ and ’ <<
options[:conditions]) if paginate_options[:conditions] &&
options[:conditions]
end

# add the options back to the end of the arguments
@args << options

self

end

missing methods should be passed to the object we are proxying

def method_missing(name, *args, &block)
object.send(name, *args, &block)
end

define private method for getting the object we are proxying

private
def object
# get the object by seding the method and args to the parent
object
@object ||= @parent.send(@method, *(@args))
end
end

Then, in my model, I can do things like:

has_many :favourites, :order => :id do
def with_recommendations
PaginatableFinder.new(
self, :find, :all,
:joins => ‘, recommendations’,
:select => ‘favourites.*’,
:group => “recommender_id”,
:conditions => "recommender_type = ‘Favourite’ and
favourites.id = recommender_id ")
end
end

this allows me, to do the following in my controller:

@user.favourites.with_recommendations

or

@user.favourites.with_recommendations.paginate :page => params[:page]

It also allows you to extend extended associations, so I can now do
the following in my model:

has_many :reviews, :order => :position do
def positive
PaginatableFinder.new(self, :find, :all, :conditions => 'stars

= 3’) do
def with_recommendations
PaginatableFinder.new(
parent, :find, :all,
:joins => ‘, recommendations’,
:select => ‘reviews.*’,
:group => “recommender_id”,
:conditions => "stars >= 3 and recommender_type = ‘Review’
and reviews.id = recommender_id ")
end
end
end
end

so I can then do any of the following in my controller:

@user.reviews.positive
@user.reviews.positive.paginate :page => params[:page]
@user.reviews.positive.with_recommendations
@user.reviews.positive.with_recommendations.paginate :page =>
params[:page]

I hope this helps others, and would be massively grateful for any
advice on how I can improve it.

Cheers

Luke.


#9

On Aug 2, 12:10 pm, “Rick O.” removed_email_address@domain.invalid wrote:

avoid this since I’ll end up duplicating my find method wherever I
Using
@event.tickets.paginate if much preferred, but unfortunately it
doesn’t work. If anyone has any other ideas, please let me know, as

It does work! @event.tickets returns an AssociationProxy which by
default proxy missing methods to the Ticket class. So it’s like
calling

Ticket.paginate :all, :conditions => {:event_id => @event.id}

I should’ve been more specific about my method… tickets is defined as
follows:

in event.rb
def tickets
TicketInfo.find(:all, :conditions => [‘line_item_id =
line_items.id and product_id = ?’, self],
:include => :line_item)
end

so this returns an Array, not an AssociationProxy, so it won’t work
with the paginate method… The problem is that I can’t turn my
tickets method into an association because of the way my classes are
related to each other…

Perhaps this demonstrates that my relationships could be designed in a
better way? Right now I have an Event class which
has_many :line_items. And each LineItem has_one :ticket_info. I
figured it was better to use a has_one relation from LineItem to
TicketInfo, since not every LineItem has ticket_info associated with
it (Event is a subclass of Product - I’ve also got Albums which don’t
need to have TicketInfo associated with them). I can turn my tickets
method into an association by using LineItem has_many :ticket_info,
and then using has_many :ticket_info, :through => :line_items in my
Event class, but this didn’t seem like a good solution, since a
LineItem will only ever have one TicketInfo associated with it.

Anyway, another solution I came across was to paginate the result set
in my ticket method as follows (I think you may have alluded to this
when you said “You can call paginate instead of find in your complex
finder”) :

event.rb

list of tickets for the event, paginated if page param is positive

and non nil
def tickets(page = nil)
args = { :conditions => [‘line_item_id = line_items.id and
product_id = ?’, self], :include => :line_item, :order => ‘last_name’}

if page and page > 0
  TicketInfo.paginate(:all, args.merge(:page => page))
else
  TicketInfo.find(:all, args)
end

end

this allows me to either call @event.tickets or
@event.tickets(params[:page]) from within my controller and will call
either ‘paginate’ or ‘find’ depending upon whether a page param is
passed or not. Does anyone see anything wrong with this approach?
Other than mixing display logic (paginate) in the model?

Or, since published is kindof the default, I tend to do this:

class Blog < AR

default usage

has_many :articles, :conditions => {:published=>true}

added for the admin area and to set the :dependent option

has_many :all_articles, :class_name => ‘Article’, :dependent => :destroy
end

Like I mentioned earlier, the way my models are related, I can’t turn
my tickets method into an AR association, but I think I’ve got a
workable solution for now (unless anyone can point out otherwise, or
give me a better method). Thanks


#10

avoid this since I’ll end up duplicating my find method wherever I
Using
@event.tickets.paginate if much preferred, but unfortunately it
doesn’t work. If anyone has any other ideas, please let me know, as

It does work! @event.tickets returns an AssociationProxy which by
default proxy missing methods to the Ticket class. So it’s like
calling

Ticket.paginate :all, :conditions => {:event_id => @event.id}

Your problem above came from the assocation extension method you
added. #find returns a plain array. I already suggested adding
another association for this:

has_many :published_articles, :class_name => ‘Article’, :conditions =>
{:published=>true}

@foo.published_articles.paginate

Or, since published is kindof the default, I tend to do this:

class Blog < AR

default usage

has_many :articles, :conditions => {:published=>true}

added for the admin area and to set the :dependent option

has_many :all_articles, :class_name => ‘Article’, :dependent =>
:destroy
end


Rick O.
http://lighthouseapp.com
http://weblog.techno-weenie.net
http://mephistoblog.com