0

I'm using the will_paginate gem in my Rails 4 project. When I view the ancestors of the object returned from calling paginate(), I thought I would see something related to will_paginate in the list of ancestors, for example, maybe WillPaginate::Collection, but I don't see anything like this. I'm confused how my object is gaining methods like current_page and total_pages, if will_paginate doesn't appear anywhere in the ancestry tree.

For example:

location = Location.first
reviews = location.location_reviews.paginate(page: 1, per_page: 5)

reviews.next_page # => 2
reviews.total_pages # => 16
reviews.total_entires # => 80

However, the output of reviews.ancestors is:

[LocationReview(id: integer, title: string), LocationReview::GeneratedAssociationMethods, #<#<Class:0x007fd6623bec38>:0x007fd6623beda0>, ActiveRecord::Base, GlobalID::Identification, ActiveRecord::Store, ActiveRecord::Serialization, ActiveModel::Serializers::Xml, ActiveModel::Serializers::JSON, ActiveModel::Serialization, ActiveRecord::Reflection, ActiveRecord::NoTouching, ActiveRecord::Transactions, ActiveRecord::Aggregations, ActiveRecord::NestedAttributes, ActiveRecord::AutosaveAssociation, ActiveModel::SecurePassword, ActiveRecord::Associations, ActiveRecord::Timestamp, ActiveModel::Validations::Callbacks, ActiveRecord::Callbacks, ActiveRecord::AttributeMethods::Serialization, ActiveRecord::AttributeMethods::Dirty, ActiveModel::Dirty, ActiveRecord::AttributeMethods::TimeZoneConversion, ActiveRecord::AttributeMethods::PrimaryKey, ActiveRecord::AttributeMethods::Query, ActiveRecord::AttributeMethods::BeforeTypeCast, ActiveRecord::AttributeMethods::Write, ActiveRecord::AttributeMethods::Read, ActiveRecord::Base::GeneratedAssociationMethods, #<#<Class:0x007fd661d14e28>:0x007fd661d14ef0>, ActiveRecord::AttributeMethods, ActiveModel::AttributeMethods, ActiveRecord::Locking::Pessimistic, ActiveRecord::Locking::Optimistic, ActiveRecord::AttributeDecorators, ActiveRecord::Attributes, ActiveRecord::CounterCache, ActiveRecord::Validations, ActiveModel::Validations::HelperMethods, ActiveSupport::Callbacks, ActiveModel::Validations, ActiveRecord::Integration, ActiveModel::Conversion, ActiveRecord::AttributeAssignment, ActiveModel::ForbiddenAttributesProtection, ActiveRecord::Sanitization, ActiveRecord::Scoping::Named, ActiveRecord::Scoping::Default, ActiveRecord::Scoping, ActiveRecord::Inheritance, ActiveRecord::ModelSchema, ActiveRecord::ReadonlyAttributes, ActiveRecord::Persistence, ActiveRecord::Core, Object, ActiveSupport::Dependencies::Loadable, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]

Why isn't there a class related to will_paginate in this output?

flyingL123
  • 7,686
  • 11
  • 66
  • 135

1 Answers1

0

You are calling .paginate method on location.location_reviews which is the collection here. According to this line and this line of the source code of paginate method, seems like the paginate method returns the original collection after applying the options (e.g. per_page) on that.

So, I think it's normal behaviour in your case that location.location_reviews.paginate(page: 1, per_page: 5) will return an object of location.location_reviews.class class.

paginate will return whatever https://github.com/mislav/will_paginate/blob/74a6cd0197072903cd8b83744133744ff4b4c046/lib/will_paginate/array.rb#L30 will return, and pager.replace method is defined here: https://github.com/mislav/will_paginate/blob/74a6cd0197072903cd8b83744133744ff4b4c046/lib/will_paginate/collection.rb#L112-L134 which returns the original collection type object. This proves that the behavior you see is correct.

K M Rakibul Islam
  • 33,760
  • 12
  • 89
  • 110
  • But `reviews.class` returns `LocationReview::ActiveRecord_Associations_CollectionProxy`. Shouldn't it return `WillPaginate::Collection`? – flyingL123 Nov 06 '15 at 16:05
  • If you see this line of code: https://github.com/mislav/will_paginate/blob/74a6cd0197072903cd8b83744133744ff4b4c046/lib/will_paginate/array.rb#L16 when it calls `arr.paginate` it returns an array after applying the paginate options e.g. per_page, page etc. So, in your case, it should return an object of class `location.location_reviews` which is what you are getting and is the normal behaviour. – K M Rakibul Islam Nov 06 '15 at 16:18
  • This line actually confirms that, it returns the original collection: https://github.com/mislav/will_paginate/blob/74a6cd0197072903cd8b83744133744ff4b4c046/lib/will_paginate/array.rb#L30 But, not a `WillPaginate::Collection` – K M Rakibul Islam Nov 06 '15 at 16:24
  • I'm still confused. The comments in the link you provided say: "The result is a WillPaginate::Collection instance, which is an array with a few more properties about its paginated state." So it still sounds like it should be returning a `WillPaginate::Collection`. I also don't understand how all of these methods can be injected into the regular behavior of an ActiveRecord collection without seeing something related to will_paginate in the ancestry chain. I am pretty new to Ruby and Rails if you couldn't tell. – flyingL123 Nov 06 '15 at 16:26
  • It's true that the comment is a bit confusing, but the examples here: https://github.com/mislav/will_paginate/blob/74a6cd0197072903cd8b83744133744ff4b4c046/lib/will_paginate/array.rb#L16-L19 make it clear that it returns the original collection type object. Also, your findings (`reviews.class returns LocationReview::ActiveRecord_Associations_CollectionProxy`) support that. – K M Rakibul Islam Nov 06 '15 at 16:33
  • So, it's fine, but to know more details about how exactly it returns the original collection type object, one would need to dig deeper the gem's code base e.g. how this line works: https://github.com/mislav/will_paginate/blob/74a6cd0197072903cd8b83744133744ff4b4c046/lib/will_paginate/array.rb#L30 – K M Rakibul Islam Nov 06 '15 at 16:33
  • But doesn't the fact that `LocationReview::ActiveRecord_Associations_CollectionProxy` responds to the `paginate` method mean that it must have been included by some module related to will_paginate? How else would the `paginate` method even exist on the collection? It doesn't exist on the object by default: http://edgeapi.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html – flyingL123 Nov 06 '15 at 16:35
  • Okay, so `paginate` will return whatever https://github.com/mislav/will_paginate/blob/74a6cd0197072903cd8b83744133744ff4b4c046/lib/will_paginate/array.rb#L30 will return, right? and `pager.replace` method is defined here: https://github.com/mislav/will_paginate/blob/74a6cd0197072903cd8b83744133744ff4b4c046/lib/will_paginate/collection.rb#L112-L134 which returns the original collection. This proves that the behavior you see is correct even though you don't see it in the ancestor chain. There must be some Ruby magic going on. – K M Rakibul Islam Nov 06 '15 at 16:52
  • Also, try to print this: `location.location_reviews.instance_methods` which should list all the instance_methods for `location.location_reviews` object, see if you find `paginate` there. Also see this to get some help on that: http://stackoverflow.com/questions/8595184/how-to-list-all-methods-for-an-object-in-ruby – K M Rakibul Islam Nov 06 '15 at 16:58
  • I agree it's working correctly. The Ruby magic you're referring to is what I'm trying to understand. I do NOT see `paginate` anywhere in the output of `location.location_reviews.instance_methods` or `location.location_reviews.paginate(page: 1).instance_methods`. Even more confused now. – flyingL123 Nov 06 '15 at 17:01
  • Can you try this: `reviews.method(:next_page).source_location`? this should give you the location of the `next_page` method. – K M Rakibul Islam Nov 06 '15 at 19:40
  • Also, try this: `location.location_reviews.method(:paginate).source_location`. – K M Rakibul Islam Nov 06 '15 at 19:41