require 'will_paginate/collection' # =Pagination for api resource collections # Will paginate adapter for the api client. Utilizes the same interface as will paginate and returns the # same WillPaginate::Collection for finder results. module SparkApi module Paginate DEFAULT_PAGE_SIZE = 25 # == Replacement hook for will_paginate's class method # Does a best effort to mimic the will_paginate method of same name. All arguments are # passed on to the finder method except the special keys for the options hash listed below. # # == Special parameters for paginating finders # * :page -- REQUIRED, but defaults to 1 if false or nil # * :per_page -- defaults to CurrentModel.per_page (which is 25 if not overridden) # * :finder -- name of the finder used (default: "get"). This needs to be a class finder method on the class def paginate(*args) options = args.last.is_a?(::Hash) ? args.pop : {} page = options.delete(:page) || 1 items_per_page = options.delete(:per_page) || self.per_page finder = (options.delete(:finder) || 'get').to_s page_options = { "_pagination" => 1, "_limit" => items_per_page, "_page" => page } options.merge!(page_options) args << options collection = send(finder,*args) end # == Instanciate class instances from array of hash representations. # Needs to be called by all finders that would like to support paging. Takes the hash result # set from the request layer and instanciates instances of the class called for the finder. # # * result_array -- the results object returned from the api request layer. An array of hashes. # # :returns: # An array of class instances for the Class of the calling finder def collect(result_array) # when conducting a count (pagination=count), the result_array is not an array # in those cases, simply return the Fixnum return result_array unless result_array.kind_of? Array collection = result_array.collect { |item| new(item)} result_array.replace(collection) result_array end # Default per_page limit set on all models. Override this method in the model such ala the # will_paginate gem to change def per_page DEFAULT_PAGE_SIZE end end # ==Paginate Api Responses # Module used by the request layer to decorate the response's results array with paging support. # Pagination only happens if the response includes the pagination information as specified by the # API. module PaginateResponse attr_accessor :results def method_missing(method_symbol, *arguments) if results.respond_to?(method_symbol) arguments.empty? ? self.results.send(method_symbol) : self.results.send(method_symbol, arguments) else super end end end # ==Pagination Helpers # Helpers to create the pagination collection module PaginateHelper # ==Enable pagination # * results -- array of hashes representing api resources # * paging_hash -- the pagination response information from the api representing paging state. # # :returns: # The result set decorated as a WillPaginate::Collection def paginate_response(results, paging_hash) pager = Pagination.new(paging_hash) paged_results = WillPaginate::Collection.create(pager.current_page, pager.page_size, pager.total_rows) do |p| p.replace(results) end paged_results.extend PaginateResponse paged_results.results = results paged_results end end # ==Pagination # Simple class representing the API's pagination response object class Pagination attr_accessor :total_rows, :page_size, :total_pages, :current_page def initialize(hash) @total_rows = hash["TotalRows"] @page_size = hash["PageSize"] @total_pages = hash["TotalPages"] @current_page = hash["CurrentPage"] end end end