Skip to content

Commit

Permalink
Add config for disabling JsonApi automatic pagination
Browse files Browse the repository at this point in the history
The following configuration can now be used to disable automatic
pagination links when using JsonApi adapter:
ActiveModelSerializer.config.collection_serializer =
  ActiveModel::Serializer::Nonpaginatedcollectionserializer
  • Loading branch information
Yohan Robert committed Mar 17, 2016
1 parent db87f8d commit d92a892
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 229 deletions.
125 changes: 52 additions & 73 deletions docs/howto/add_pagination_links.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,34 @@

# How to add pagination links

### JSON API adapter
## JSON API adapter

Pagination links will be included in your response automatically as long as
the resource is paginated and if you are using the ```JsonApi``` adapter.
When using the `JsonApi` adapter, pagination links will be automatically included if you use [Kaminari](https://github.com/amatsuda/kaminari)
or [WillPaginate](https://github.com/mislav/will_paginate) within a Rails controller:

If you want pagination links in your response, use [Kaminari](https://github.com/amatsuda/kaminari)
or [WillPaginate](https://github.com/mislav/will_paginate).

Although the others adapters does not have this feature, it is possible to
implement pagination links to `JSON` adapter. For more information about it,
please see in our docs

###### Kaminari examples
* Using Kaminari:

```ruby
#array
@posts = Kaminari.paginate_array([1, 2, 3]).page(3).per(1)
render json: @posts

#active_record
@posts = Post.page(3).per(1)
render json: @posts
class PostsController < ApplicationController
def index
posts = Post.page(params[:page]).per(params[:per_page])
render json: posts
end
end
```

###### WillPaginate examples

```ruby
#array
@posts = [1,2,3].paginate(page: 3, per_page: 1)
render json: @posts

#active_record
@posts = Post.page(3).per_page(1)
render json: @posts
```
* Using WillPaginate:

```ruby
ActiveModelSerializers.config.adapter = :json_api
class PostsController < ApplicationController
def index
posts = Post.page(params[:page]).per_page(params[:per_page])
render json: posts
end
end
```

ex:
The response might look like:
```json
{
"data": [
Expand All @@ -67,34 +54,50 @@ ex:
}
```

ActiveModelSerializers pagination relies on a paginated collection with the methods `current_page`, `total_pages`, and `size`, such as are supported by both [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate).

ActiveModelSerializers pagination relies on paginated collections which define the methods `#current_page`, `#total_pages`, and `#size`.
Such methods are supported by both [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate),
but you can also roll out your own paginated collection by defining these methods.

### JSON adapter
If you do not want pagination links to be automatically rendered, you may disable it by setting the `ActiveModelSerializers.config.collection_serializer` config to
`ActiveModel::Serializer::NonPaginatedCollectionSerializer`.

If you are using `JSON` adapter, pagination links will not be included automatically, but it is possible to do so using `meta` key.
If you want to disable pagination links for a specific controller, you may set the `serializer` option to `ActiveModel::Serializer::NonPaginatedCollectionSerializer`:

Add this method to your base API controller.

```ruby
def pagination_dict(object)
{
current_page: object.current_page,
next_page: object.next_page,
prev_page: object.prev_page,
total_pages: object.total_pages,
total_count: object.total_count
}
``` ruby
class PostsController < ApplicationController
def index
posts = Post.page(params[:page]).per_page(params[:per_page])
render json: posts, serializer: ActiveModel::Serializer::NonPaginatedCollectionSerializer
end
end
```

Then, use it on your render method.
### Json adapter

If you are using the `Json` adapter, pagination links will not be included automatically, but it is possible to handle pagination using the `meta` option:

```ruby
render json: posts, meta: pagination_dict(posts)
class PostsController < ApplicationController
def index
posts = Post.page(params[:page]).per_page(params[:per_page])
render json: posts, meta: pagination_dict(posts)
end

private

def pagination_dict(object)
{
current_page: object.current_page,
next_page: object.next_page,
prev_page: object.prev_page,
total_pages: object.total_pages,
total_count: object.total_count
}
end
end
```

ex.
The response might look like:
```json
{
"posts": [
Expand All @@ -113,27 +116,3 @@ ex.
}
}
```

You can also achieve the same result if you have a helper method that adds the pagination info in the meta tag. For instance, in your action specify a custom serializer.

```ruby
render json: @posts, each_serializer: PostPreviewSerializer, meta: meta_attributes(@post)
```

```ruby
#expects pagination!
def meta_attributes(resource, extra_meta = {})
{
current_page: resource.current_page,
next_page: resource.next_page,
prev_page: resource.prev_page,
total_pages: resource.total_pages,
total_count: resource.total_count
}.merge(extra_meta)
end
```


### Attributes adapter

This adapter does not allow us to use `meta` key, due to that it is not possible to add pagination links.
2 changes: 1 addition & 1 deletion lib/active_model/serializable_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
require 'active_model_serializers/adapter'
module ActiveModel
class SerializableResource
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links])
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links, :serialization_context])
include ActiveModelSerializers::Logging

delegate :serializable_hash, :as_json, :to_json, to: :adapter
Expand Down
1 change: 1 addition & 0 deletions lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'thread_safe'
require 'active_model/serializer/collection_serializer'
require 'active_model/serializer/non_paginated_collection_serializer'
require 'active_model/serializer/array_serializer'
require 'active_model/serializer/error_serializer'
require 'active_model/serializer/errors_serializer'
Expand Down
3 changes: 1 addition & 2 deletions lib/active_model/serializer/collection_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ def json_key

def paginated?
object.respond_to?(:current_page) &&
object.respond_to?(:total_pages) &&
object.respond_to?(:size)
object.respond_to?(:total_pages)
end

protected
Expand Down
11 changes: 11 additions & 0 deletions lib/active_model/serializer/non_paginated_collection_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'active_model/serializer/collection_serializer'

module ActiveModel
class Serializer
class NonPaginatedCollectionSerializer < CollectionSerializer
def paginated?
false
end
end
end
end
1 change: 1 addition & 0 deletions lib/active_model/serializer/reflection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def initialize(*)
super
@_links = {}
@_include_data = true
@_meta = nil
end

def link(name, value = nil, &block)
Expand Down
14 changes: 11 additions & 3 deletions lib/active_model_serializers/adapter/json_api.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# coding: utf-8
# {http://jsonapi.org/format/ JSON API specification}
# rubocop:disable Style/AsciiComments
# TODO: implement!
Expand Down Expand Up @@ -35,6 +36,7 @@ def initialize(serializer, options = {})
super
@include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(options[:include])
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
@serialization_context = options[:serialization_context]
end

def default_key_transform
Expand Down Expand Up @@ -126,7 +128,7 @@ def success_document(options)
hash[:links].update(instance_options[:links])
end

if is_collection && serializer.paginated?
if paginate?
hash[:links] ||= {}
hash[:links].update(pagination_links_for(serializer, options))
end
Expand Down Expand Up @@ -185,7 +187,7 @@ def fragment_cache(cached_hash, non_cached_hash)

protected

attr_reader :fieldset
attr_reader :fieldset, :serialization_context

private

Expand Down Expand Up @@ -234,6 +236,12 @@ def resource_objects_for(serializers)
[@primary, @included]
end

def paginate?
!serialization_context.nil? &&
serializer.respond_to?(:paginated?) &&
serializer.paginated?
end

def process_resource(serializer, primary)
resource_identifier = ResourceIdentifier.new(serializer).as_json
return false unless @resource_identifiers.add?(resource_identifier)
Expand Down Expand Up @@ -499,7 +507,7 @@ def links_for(serializer)
# prs:
# https://github.com/rails-api/active_model_serializers/pull/1041
def pagination_links_for(serializer, options)
PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options)
PaginationLinks.new(serializer.object, serialization_context).serializable_hash(options)
end

# {http://jsonapi.org/format/#document-meta Docment Meta}
Expand Down
Loading

0 comments on commit d92a892

Please sign in to comment.