Forum: Ruby on Rails will_paginate plugin doesn't work with Association Extension

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.
Christos Z. (Guest)
on 2007-07-24 20:14
(Received via mailing list)
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
Rick O. (Guest)
on 2007-07-24 20:24
(Received via mailing list)
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
Obie F. (Guest)
on 2007-07-24 20:34
(Received via mailing list)
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!
http://www.amazon.com/dp/0321445619
Rick O. (Guest)
on 2007-07-24 20:53
(Received via mailing list)
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
byforcesunseen (Guest)
on 2007-07-25 00:21
(Received via mailing list)
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,
Rick O. (Guest)
on 2007-07-25 02:23
(Received via mailing list)
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
byforcesunseen (Guest)
on 2007-07-25 21:30
(Received via mailing list)
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,
lukens (Guest)
on 2007-08-02 19:31
(Received via mailing list)
> 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.
-----
Rick O. (Guest)
on 2007-08-02 20:11
(Received via mailing list)
> 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
byforcesunseen (Guest)
on 2007-08-03 00:17
(Received via mailing list)
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
This topic is locked and can not be replied to.