From f7c77c1256fa07b79fbe977147ed629be29be613 Mon Sep 17 00:00:00 2001 From: Bruno Bacarini Date: Sun, 2 Aug 2015 18:02:56 -0300 Subject: [PATCH] add feature to include pagination links in response --- .gitignore | 1 + README.md | 12 ++++ lib/active_model/serializer/adapter.rb | 21 ++++++- .../serializer/array_serializer.rb | 4 +- lib/active_model/serializer/pagination.rb | 63 +++++++++++++++++++ lib/active_model_serializers.rb | 1 + 6 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 lib/active_model/serializer/pagination.rb diff --git a/.gitignore b/.gitignore index 0374e060..ad7f37d1 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ test/version_tmp tmp *.swp .ruby-version +.ruby-gemset diff --git a/README.md b/README.md index 15e86947..ce21fa8e 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,18 @@ 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 + +If you want pagination links in your response, specify it in the `render` + +```ruby + render json: @posts, pagination: true +``` + +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 an Adapter that supports `root`, as JsonAPI and Json adapters, the default adapter (FlattenJson) doesn't have `root`. + ## Caching To cache a serializer, call ```cache``` and pass its options. diff --git a/lib/active_model/serializer/adapter.rb b/lib/active_model/serializer/adapter.rb index 0b8118d6..79256a58 100644 --- a/lib/active_model/serializer/adapter.rb +++ b/lib/active_model/serializer/adapter.rb @@ -21,9 +21,9 @@ module ActiveModel end def as_json(options = nil) - hash = serializable_hash(options) - include_meta(hash) - hash + serializable_hash(options).tap do |hash| + include_meta(hash) + end end def self.create(resource, options = {}) @@ -94,6 +94,21 @@ module ActiveModel json[meta_key] = meta if meta json end + + def include_pagination_links(json) + return unless page_links + + links?(json) ? json.merge!(page_links) : json['links'] = page_links + json + end + + def page_links + @links ||= serializer.page_links + end + + def links?(json) + !json['links'].nil? + end end end end diff --git a/lib/active_model/serializer/array_serializer.rb b/lib/active_model/serializer/array_serializer.rb index f2f916e5..7a8d14ac 100644 --- a/lib/active_model/serializer/array_serializer.rb +++ b/lib/active_model/serializer/array_serializer.rb @@ -4,8 +4,9 @@ module ActiveModel NoSerializerError = Class.new(StandardError) include Enumerable delegate :each, to: :@objects + delegate :page_links, to: :pagination - attr_reader :root, :meta, :meta_key + attr_reader :root, :meta, :meta_key, :pagination def initialize(objects, options = {}) @root = options[:root] @@ -24,6 +25,7 @@ module ActiveModel end @meta = options[:meta] @meta_key = options[:meta_key] + @pagination = ActiveModel::Serializer::Pagination.new(objects) if options[:pagination] end def json_key diff --git a/lib/active_model/serializer/pagination.rb b/lib/active_model/serializer/pagination.rb new file mode 100644 index 00000000..a9bbba59 --- /dev/null +++ b/lib/active_model/serializer/pagination.rb @@ -0,0 +1,63 @@ +module ActiveModel + class Serializer + class Pagination + attr_reader :collection + + def initialize(collection) + @collection = collection + end + + def page_links + send(default_adapter) + end + + private + + def kaminari + build_links collection.size + end + + def will_paginate + setup_will_paginate + build_links collection.per_page + end + + def build_links(per_page) + pages = pages_from.each_with_object({}) do |(key, value), hash| + hash[key] = "?page=#{value}&per_page=#{per_page}" + end + { pages: pages } unless pages.empty? + end + + def pages_from + return {} if collection.total_pages == 1 + + {}.tap do |pages| + unless collection.first_page? + pages[:first] = 1 + pages[:prev] = collection.current_page - 1 + end + + unless collection.last_page? + pages[:next] = collection.current_page + 1 + pages[:last] = collection.total_pages + end + end + end + + def default_adapter + return :kaminari if defined?(Kaminari) + return :will_paginate if defined?(WillPaginate::CollectionMethods) + raise "AMS relies on either Kaminari or WillPaginate." + + "Please install either dependency by adding one of those to your Gemfile" + end + + def setup_will_paginate + WillPaginate::CollectionMethods.module_eval do + def first_page?() !previous_page end + def last_page?() !next_page end + end + end + end + end +end diff --git a/lib/active_model_serializers.rb b/lib/active_model_serializers.rb index 211a2870..bf5f5d56 100644 --- a/lib/active_model_serializers.rb +++ b/lib/active_model_serializers.rb @@ -2,6 +2,7 @@ require 'active_model' require 'active_model/serializer/version' require 'active_model/serializer' require 'active_model/serializer/fieldset' +require 'active_model/serializer/pagination' require 'active_model/serializable_resource' begin