From 2c2f948fa0ea5e2fb05940c636c9f078a09a33b6 Mon Sep 17 00:00:00 2001 From: Bruno Bacarini Date: Mon, 17 Aug 2015 14:25:59 -0300 Subject: [PATCH] Add pagination links automatically Pagination links will be included in your response automatically as long as the resource is paginated using Kaminari or WillPaginate and if you are using a JSON-API adapter. The others adapters does not have this feature. --- README.md | 7 +++- docs/howto/add_pagination_links.md | 18 +++++--- lib/action_controller/serialization.rb | 7 ++-- lib/active_model/serializable_resource.rb | 2 +- .../serializer/adapter/json_api.rb | 2 +- .../adapter/json_api/pagination_links.rb | 4 +- .../json_api/pagination_test.rb | 17 ++------ .../adapter/json_api/pagination_links_test.rb | 42 ++++++++++--------- 8 files changed, 52 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 15e86947..ef103ba1 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ If you wish to use a serializer other than the default, you can explicitly pass render json: @posts, each_serializer: PostPreviewSerializer # Or, you can explicitly provide the collection serializer as well -render json: @posts, serializer: PaginatedSerializer, each_serializer: PostPreviewSerializer +render json: @posts, serializer: CollectionSerializer, each_serializer: PostPreviewSerializer ``` ### Meta @@ -272,6 +272,11 @@ And you can change the JSON key that the serializer should use for a particular The `url` declaration describes which named routes to use while generating URLs for your JSON. Not every adapter will require URLs. +## Pagination + +Pagination links will be included in your response automatically as long as the resource is paginated using [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate) and if you are using a ```JSON-API``` adapter. The others adapters does not have this feature. + +For more information about it, please see in our docs [How to add pagination links](https://github.com/rails-api/active_model_serializers/blob/master/docs/howto/add_pagination_links.md) ## Caching diff --git a/docs/howto/add_pagination_links.md b/docs/howto/add_pagination_links.md index eca74929..d49095b8 100644 --- a/docs/howto/add_pagination_links.md +++ b/docs/howto/add_pagination_links.md @@ -1,15 +1,19 @@ # How to add pagination links -If you want pagination links in your response, specify it in the `render` +Pagination links will be included in your response automatically as long as the resource is paginated and if you are using a ```JSON-API``` adapter. The others adapters does not have this feature. + +If you want pagination links in your response, use [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate). ```ruby - render json: @posts, pagination: true +#kaminari example +@posts = Kaminari.paginate_array(Post.all).page(3).per(1) +render json: @posts + +#will_paginate example +@posts = Post.all.paginate(page: 3, per_page: 1) +render json: @posts ``` -AMS relies on either `Kaminari` or `WillPaginate`. Please install either dependency by adding one of those to your Gemfile. - -Pagination links will only be included in your response if you are using a ```JSON-API``` adapter, the others adapters doesn't have this feature. - ```ruby ActiveModel::Serializer.config.adapter = :json_api ``` @@ -38,3 +42,5 @@ ex: } } ``` + +AMS relies on either [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate). Please install either dependency by adding one of those to your Gemfile. diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb index 99f860f5..927160f3 100644 --- a/lib/action_controller/serialization.rb +++ b/lib/action_controller/serialization.rb @@ -25,9 +25,10 @@ module ActionController "Please pass 'adapter: false' or see ActiveSupport::SerializableResource#serialize" options[:adapter] = false end - if options[:pagination] - options[:original_url] = original_url - options[:query_parameters] = query_parameters + if resource.respond_to?(:current_page) && resource.respond_to?(:total_pages) + options[:pagination] = {} + options[:pagination][:original_url] = original_url + options[:pagination][:query_parameters] = query_parameters end ActiveModel::SerializableResource.serialize(resource, options) do |serializable_resource| if serializable_resource.serializer? diff --git a/lib/active_model/serializable_resource.rb b/lib/active_model/serializable_resource.rb index 9115b396..fa3fbe03 100644 --- a/lib/active_model/serializable_resource.rb +++ b/lib/active_model/serializable_resource.rb @@ -2,7 +2,7 @@ require "set" module ActiveModel class SerializableResource - ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :pagination]) + ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter]) def initialize(resource, options = {}) @resource = resource diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index ceb69487..b48e1d20 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -164,7 +164,7 @@ module ActiveModel def add_links(options) links = @hash.fetch(:links) { {} } resources = serializer.instance_variable_get(:@resource) - @hash[:links] = add_pagination_links(links, resources, options) if @options[:pagination] + @hash[:links] = add_pagination_links(links, resources, options) if options[:pagination] end def add_pagination_links(links, resources, options) diff --git a/lib/active_model/serializer/adapter/json_api/pagination_links.rb b/lib/active_model/serializer/adapter/json_api/pagination_links.rb index 4244721e..7b608e61 100644 --- a/lib/active_model/serializer/adapter/json_api/pagination_links.rb +++ b/lib/active_model/serializer/adapter/json_api/pagination_links.rb @@ -14,7 +14,7 @@ module ActiveModel def serializable_hash(options = {}) pages_from.each_with_object({}) do |(key, value), hash| - query_parameters = options.fetch(:query_parameters) { {} } + query_parameters = options[:pagination].fetch(:query_parameters) { {} } params = query_parameters.merge(page: { number: value, size: collection.size }).to_query hash[key] = "#{url(options)}?#{params}" @@ -51,7 +51,7 @@ module ActiveModel def url(options) self_link = options.fetch(:links) {{}} - self_link.fetch(:self) {} ? options[:links][:self] : options[:original_url] + self_link.fetch(:self) {} ? options[:links][:self] : options[:pagination][:original_url] end end end diff --git a/test/action_controller/json_api/pagination_test.rb b/test/action_controller/json_api/pagination_test.rb index cf7832a8..bc044c3f 100644 --- a/test/action_controller/json_api/pagination_test.rb +++ b/test/action_controller/json_api/pagination_test.rb @@ -31,19 +31,16 @@ module ActionController end def render_pagination_using_kaminari - render json: using_kaminari, adapter: :json_api, pagination: true + render json: using_kaminari, adapter: :json_api end def render_pagination_using_will_paginate - render json: using_will_paginate, adapter: :json_api, pagination: true + render json: using_will_paginate, adapter: :json_api end def render_array_without_pagination_links - render json: using_will_paginate, adapter: :json_api, pagination: false - end - - def render_array_omitting_pagination_options - render json: using_kaminari, adapter: :json_api + setup + render json: @array, adapter: :json_api end end @@ -104,12 +101,6 @@ module ActionController response = JSON.parse(@response.body) refute response.key? 'links' end - - def test_array_omitting_pagination_options - get :render_array_omitting_pagination_options, page: { number: 2, size: 1 } - response = JSON.parse(@response.body) - refute response.key? 'links' - end end end end diff --git a/test/adapter/json_api/pagination_links_test.rb b/test/adapter/json_api/pagination_links_test.rb index 198662fd..2a624ab7 100644 --- a/test/adapter/json_api/pagination_links_test.rb +++ b/test/adapter/json_api/pagination_links_test.rb @@ -27,15 +27,11 @@ module ActiveModel end def data - { - data: [{ - id:"2", - type:"profiles", - attributes:{ - name:"Name 2", - description:"Description 2" - } - }] + { data:[ + { id:"1", type:"profiles", attributes:{name:"Name 1", description:"Description 1" } }, + { id:"2", type:"profiles", attributes:{name:"Name 2", description:"Description 2" } }, + { id:"3", type:"profiles", attributes:{name:"Name 3", description:"Description 3" } } + ] } end @@ -56,42 +52,48 @@ module ActiveModel end def expected_response_with_pagination_links - data.merge links + {}.tap do |hash| + hash[:data] = [data.values.flatten.second] + hash.merge! links + end end def expected_response_with_pagination_links_and_additional_params new_links = links[:links].each_with_object({}) {|(key, value), hash| hash[key] = "#{value}&teste=teste" } - data.merge links: new_links + {}.tap do |hash| + hash[:data] = [data.values.flatten.second] + hash.merge! links: new_links + end end def test_pagination_links_using_kaminari serializer = ArraySerializer.new(using_kaminari) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer, pagination: true) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) assert_equal expected_response_with_pagination_links, - adapter.serializable_hash(original_url: "http://example.com") + adapter.serializable_hash(pagination: { original_url: "http://example.com" }) end def test_pagination_links_using_will_paginate serializer = ArraySerializer.new(using_will_paginate) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer, pagination: true) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) assert_equal expected_response_with_pagination_links, - adapter.serializable_hash(original_url: "http://example.com") + adapter.serializable_hash(pagination: { original_url: "http://example.com" }) end def test_pagination_links_with_additional_params serializer = ArraySerializer.new(using_will_paginate) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer, pagination: true) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) assert_equal expected_response_with_pagination_links_and_additional_params, - adapter.serializable_hash(original_url: "http://example.com", - query_parameters: { teste: "teste"}) + adapter.serializable_hash(pagination: { original_url: "http://example.com", + query_parameters: { teste: "teste"}}) end def test_not_showing_pagination_links - serializer = ArraySerializer.new(using_will_paginate) - adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer, pagination: false) + serializer = ArraySerializer.new(@array) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) assert_equal expected_response_without_pagination_links, adapter.serializable_hash end