Skip to content

Instantly share code, notes, and snippets.

@dblock
Created November 2, 2011 23:08
Show Gist options
  • Save dblock/1335242 to your computer and use it in GitHub Desktop.
Save dblock/1335242 to your computer and use it in GitHub Desktop.
pagination helper with Grape
module ApiPageHelper
PAGINATE_OPTIONS = {
:default_page_size => 10
}
PAGINATE_PARAMS = [ "page", "offset", "size" ]
def paginate(coll, options = {})
options = PAGINATE_OPTIONS.merge(options)
if params[:page]
page = params[:page].to_i
size = (params[:size] || options[:default_page_size]).to_i
error!("Invalid page: #{page}", 400) if page < 0
error!("Invalid page size: #{size}", 400) if size <= 0
if coll.respond_to?(:page)
coll = coll.page(page).per(size)
elsif coll.respond_to?(:skip) and coll.respond_to?(:limit)
coll = coll.skip(size * (page - 1)).limit(size)
elsif coll.is_a?(Array)
coll = coll[((page - 1) * size)...(page * size)]
else
error!("Cannot paginate #{coll.class.name}", 500)
end
else
if params[:offset]
offset = params[:offset].to_i
error!("Invalid offset: #{offset}", 400) if offset < 0
if coll.respond_to?(:skip)
coll = coll.skip(offset)
elsif coll.is_a?(Array)
coll = coll[offset..-1]
else
error!("Cannot offset #{coll.class.name}", 500)
end
end
limit = nil
if params[:size]
limit = params[:size].to_i
error!("Invalid limit: #{limit}", 400) if limit <= 0
limit = [ limit, options[:max_size] ].min if options[:max_size]
elsif options[:max_size]
limit = options[:max_size].to_i
end
if limit
if coll.respond_to?(:limit)
coll = coll.limit(limit)
elsif coll.is_a?(Array)
coll = coll[0..limit - 1]
else
error!("Cannot limit #{coll.class.name}", 500)
end
end
end
(coll.is_a?(Module) and coll.respond_to?(:all)) ? coll.all : coll
end
end
@dustMason
Copy link

thanks, this is excellent

@niv
Copy link

niv commented Jul 13, 2013

I expanded on this somewhat: https://gist.github.com/niv/5991069

  • You can now add paginable to a route method endpoint thingy and it automagically adds all params with validations.
  • size renamed to limit to be more standard-ish.
  • It adds metadata to the result when using present. This can probably be made prettier by using a custom formatter instead. You can disable this easily by just deleting the "present" helper method.
  • It now works with AR Relations so you can do present SomeModel.scope.scope.whatever and have it work (it now checks if method :offset exists and uses that, because skip(x) would not answer to limit(x)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment