diff --git a/README.md b/README.md index 673bd20..efb0368 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,15 @@ class MoviesController < ApplicationController # params[:per_page] (which defaults to 25) will be used. paginate json: actors, per_page: 10 end + + # GET /movies/:id/reviews + def reviews + reviews = Movie.find(params[:id]).reviews + + # Override any configuration setting on request basis. + # For example you may want to disable the total count since the count query is slow. + paginate json: actors, total_count: false + end end ``` @@ -117,6 +126,13 @@ class MoviesController < ApplicationController render json: ActorsSerializer.new(actors) end + + # GET /movies/:id/reviews + def reviews + reviews = paginate Movie.find(params[:id]).reviews, total_count: false + + render json: ReviewSerializer.new(reviews) + end end ``` diff --git a/lib/api-pagination.rb b/lib/api-pagination.rb index d8c94d2..0c7d6fe 100644 --- a/lib/api-pagination.rb +++ b/lib/api-pagination.rb @@ -4,11 +4,12 @@ module ApiPagination class << self def paginate(collection, options = {}) - options[:page] = options[:page].to_i - options[:page] = 1 if options[:page] <= 0 - options[:per_page] = options[:per_page].to_i + options[:page] = options[:page].to_i + options[:page] = 1 if options[:page] <= 0 + options[:per_page] = options[:per_page].to_i + options[:paginator] ||= ApiPagination.config.paginator - case ApiPagination.config.paginator + case options[:paginator] when :pagy paginate_with_pagy(collection, options) when :kaminari @@ -16,12 +17,13 @@ def paginate(collection, options = {}) when :will_paginate paginate_with_will_paginate(collection, options) else - raise StandardError, "Unknown paginator: #{ApiPagination.config.paginator}" + raise StandardError, "Unknown paginator: #{options[:paginator]}" end end def pages_from(collection, options = {}) - return pagy_pages_from(collection) if ApiPagination.config.paginator == :pagy && collection.is_a?(Pagy) + options[:paginator] ||= ApiPagination.config.paginator + return pagy_pages_from(collection, options) if options[:paginator] == :pagy && collection.is_a?(Pagy) {}.tap do |pages| unless collection.first_page? @@ -29,15 +31,16 @@ def pages_from(collection, options = {}) pages[:prev] = collection.current_page - 1 end - unless collection.last_page? || (ApiPagination.config.paginator == :kaminari && collection.out_of_range?) - pages[:last] = collection.total_pages if ApiPagination.config.include_total + unless collection.last_page? || (options[:paginator] == :kaminari && collection.out_of_range?) + pages[:last] = collection.total_pages if options[:include_total] pages[:next] = collection.current_page + 1 end end end - def total_from(collection) - case ApiPagination.config.paginator + def total_from(collection, options) + options[:paginator] ||= ApiPagination.config.paginator + case options[:paginator] when :pagy then collection.count.to_s when :kaminari then collection.total_count.to_s when :will_paginate then collection.total_entries.to_s @@ -69,11 +72,11 @@ def pagy_from(collection, options) else count = collection.is_a?(Array) ? collection.count : collection.count(:all) end - + Pagy.new(count: count, items: options[:per_page], page: options[:page]) end - def pagy_pages_from(pagy) + def pagy_pages_from(pagy, options) {}.tap do |pages| unless pagy.page == 1 pages[:first] = 1 @@ -81,7 +84,7 @@ def pagy_pages_from(pagy) end unless pagy.page == pagy.pages - pages[:last] = pagy.pages if ApiPagination.config.include_total + pages[:last] = pagy.pages if options[:include_total] pages[:next] = pagy.next end end @@ -96,7 +99,7 @@ def paginate_with_kaminari(collection, options, paginate_array_options = {}) collection = Kaminari.paginate_array(collection, paginate_array_options) if collection.is_a?(Array) collection = collection.page(options[:page]).per(options[:per_page]) - collection.without_count if !collection.is_a?(Array) && !ApiPagination.config.include_total + collection.without_count if !collection.is_a?(Array) && !options[:include_total] [collection, nil] end diff --git a/lib/grape/pagination.rb b/lib/grape/pagination.rb index 86799e6..6aec41e 100644 --- a/lib/grape/pagination.rb +++ b/lib/grape/pagination.rb @@ -2,13 +2,20 @@ module Grape module Pagination def self.included(base) Grape::Endpoint.class_eval do - def paginate(collection) - per_page = ApiPagination.config.per_page_param(params) || route_setting(:per_page) - - options = { - :page => ApiPagination.config.page_param(params), - :per_page => [per_page, route_setting(:max_per_page)].compact.min + def paginate(collection, options = {}) + per_page = ApiPagination.config.per_page_param(params) || route_setting(:per_page) + options[:per_page] = [per_page, route_setting(:max_per_page)].compact.min + options[:page] = ApiPagination.config.page_param(params) + + default_options = { + :total_header => ApiPagination.config.total_header, + :per_page_header => ApiPagination.config.per_page_header, + :page_header => ApiPagination.config.page_header, + :include_total => ApiPagination.config.include_total, + :paginator => ApiPagination.config.paginator } + options.reverse_merge!(default_options) + collection, pagy = ApiPagination.paginate(collection, options) links = (header['Link'] || "").split(',').map(&:strip) @@ -21,15 +28,10 @@ def paginate(collection) links << %(<#{url}?#{new_params.to_param}>; rel="#{k}") end - total_header = ApiPagination.config.total_header - per_page_header = ApiPagination.config.per_page_header - page_header = ApiPagination.config.page_header - include_total = ApiPagination.config.include_total - - header 'Link', links.join(', ') unless links.empty? - header total_header, ApiPagination.total_from(pagy || collection).to_s if include_total - header per_page_header, options[:per_page].to_s - header page_header, options[:page].to_s unless page_header.nil? + header 'Link', links.join(', ') unless links.empty? + header options[:total_header], ApiPagination.total_from(pagy || collection, options).to_s if options[:include_total] + header options[:per_page_header], options[:per_page].to_s + header options[:page_header], options[:page].to_s unless options[:page_header].nil? return collection end diff --git a/lib/rails/pagination.rb b/lib/rails/pagination.rb index 5d0b5c0..0c210b7 100644 --- a/lib/rails/pagination.rb +++ b/lib/rails/pagination.rb @@ -32,7 +32,15 @@ def _discover_format(options) def _paginate_collection(collection, options={}) options[:page] = ApiPagination.config.page_param(params) - options[:per_page] ||= ApiPagination.config.per_page_param(params) + default_options = { + :per_page => ApiPagination.config.per_page_param(params), + :total_header => ApiPagination.config.total_header, + :per_page_header => ApiPagination.config.per_page_header, + :page_header => ApiPagination.config.page_header, + :include_total => ApiPagination.config.include_total, + :paginator => ApiPagination.config.paginator + } + options.reverse_merge!(default_options) collection, pagy = ApiPagination.paginate(collection, options) @@ -45,25 +53,20 @@ def _paginate_collection(collection, options={}) links << %(<#{url}?#{new_params.to_param}>; rel="#{k}") end - total_header = ApiPagination.config.total_header - per_page_header = ApiPagination.config.per_page_header - page_header = ApiPagination.config.page_header - include_total = ApiPagination.config.include_total - headers['Link'] = links.join(', ') unless links.empty? - headers[per_page_header] = options[:per_page].to_s - headers[page_header] = options[:page].to_s unless page_header.nil? - headers[total_header] = total_count(pagy || collection, options).to_s if include_total + headers[options[:per_page_header]] = options[:per_page].to_s + headers[options[:page_header]] = options[:page].to_s unless options[:page_header].nil? + headers[options[:total_header]] = total_count(pagy || collection, options).to_s if options[:include_total] return collection end def total_count(collection, options) - total_count = if ApiPagination.config.paginator == :kaminari + total_count = if options[:paginator] == :kaminari paginate_array_options = options[:paginate_array_options] paginate_array_options[:total_count] if paginate_array_options end - total_count || ApiPagination.total_from(collection) + total_count || ApiPagination.total_from(collection, options) end def base_url diff --git a/spec/grape_spec.rb b/spec/grape_spec.rb index 168e864..a3b362f 100644 --- a/spec/grape_spec.rb +++ b/spec/grape_spec.rb @@ -161,5 +161,28 @@ expect(links).to include('; rel="next"') end end + + context 'request option to not include the total' do + it 'should not include a Total header' do + get '/numbers_with_inline_options', count: 10 + + expect(last_response.header['Total']).to be_nil + end + + it 'should not include a link with rel "last"' do + get '/numbers_with_inline_options', count: 100 + + expect(link).to_not include('rel="last"') + end + end + + context 'request option to change page_header' do + it 'should give a X-Page header' do + get '/numbers_with_inline_options', count: 10 + + expect(last_response.headers.keys).to include('X-Page') + expect(last_response.headers['X-Page'].to_i).to eq(1) + end + end end end diff --git a/spec/rails_spec.rb b/spec/rails_spec.rb index cde7420..1e61676 100644 --- a/spec/rails_spec.rb +++ b/spec/rails_spec.rb @@ -219,6 +219,29 @@ expect(response.header['Per-Page']).to eq('2') end end + + context 'request option to not include the total' do + it 'should not include a Total header' do + get :index_with_inline_options, params: {count: 10} + + expect(response.header['Total']).to be_nil + end + + it 'should not include a link with rel "last"' do + get :index_with_inline_options, params: { count: 100 } + + expect(link).to_not include('rel="last"') + end + end + + context 'request option to change page_header' do + it 'should give a X-Page header' do + get :index_with_inline_options, params: {count: 10} + + expect(response.headers.keys).to include('X-Page') + expect(response.headers['X-Page'].to_i).to eq(1) + end + end end if ApiPagination.config.paginator.to_sym == :kaminari @@ -309,4 +332,4 @@ class Fixnum end end end -end \ No newline at end of file +end diff --git a/spec/support/numbers_api.rb b/spec/support/numbers_api.rb index 7c29d8e..948a49d 100644 --- a/spec/support/numbers_api.rb +++ b/spec/support/numbers_api.rb @@ -38,4 +38,13 @@ class NumbersAPI < Grape::API get :numbers_with_enforced_max_per_page do paginate (1..params[:count]).to_a end + + desc 'Return some paginated set of numbers with inline options' + paginate :per_page => 10 + params do + requires :count, :type => Integer + end + get :numbers_with_inline_options do + paginate (1..params[:count]).to_a, include_total: false, page_header: 'X-Page' + end end diff --git a/spec/support/numbers_controller.rb b/spec/support/numbers_controller.rb index 0f2a504..737e0f1 100644 --- a/spec/support/numbers_controller.rb +++ b/spec/support/numbers_controller.rb @@ -45,6 +45,7 @@ def teardown(*methods) get :index_with_custom_render get :index_with_no_per_page get :index_with_paginate_array_options + get :index_with_inline_options end end end @@ -98,6 +99,16 @@ def index_with_paginate_array_options render json: NumbersSerializer.new(numbers) end + + def index_with_inline_options + total = params.fetch(:count).to_i + paginate( + :json => (1..total).to_a, + :per_page => 10, + :include_total => false, + :page_header => 'X-Page' + ) + end end ApiPagination::Railtie.initializers.each(&:run)