-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
37114e0
commit 07ce62d
Showing
14 changed files
with
366 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'next_page/pagination' | ||
require 'next_page/pagination_attributes' | ||
require 'next_page/paginator' | ||
|
||
# = Next Page | ||
module NextPage | ||
# Your code goes here... | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# frozen_string_literal: true | ||
|
||
module NextPage | ||
# = Pagination | ||
# | ||
# Module Pagination provides pagination for index methods. It assigns a limit and offset | ||
# to the resource query and extends the relation with mixin NextPage::PaginationAttributes. | ||
# | ||
# There are two ways to paginate: using a before filter or by calling `paginate_resource` explicitly. | ||
# | ||
# == Before Filter | ||
# Here's an example of using the before filter in a controller: | ||
# before_action :apply_next_page_pagination, only: :index | ||
# | ||
# This entry point uses the following conventions to apply pagination: | ||
# - the name of the instance variable is the same as the component (for example PhotosController -> @photos) | ||
# - the name of the model is the controller name singularized (for example PhotosController -> Photo) | ||
# | ||
# Either can be overridden by calling method `paginate_with` in the controller. The two override options are | ||
# `instance_variable_name` and `model_class`. For example, if the PhotosController used the model Picture and the | ||
# instance variable name @photographs, the controller declares it as follows: | ||
# paginate_with instance_variable_name: :photographs, model_class: 'Picture' | ||
# | ||
# If the before filter is used, it will populate an instance variable. The action should NOT reset the variable, as | ||
# that removes pagination. | ||
# | ||
# == Invoking Pagination Directly | ||
# To paginate a resource pass the resource into method `paginate_resource` then store the return value back in the | ||
# resource: | ||
# | ||
# @photos = paginate_resource(@photos) | ||
# | ||
# == Default Limit | ||
# The default size limit can be overridden with the `paginate_with` method for either type of pagination. Pass option | ||
# `default_limit` to specify an override: | ||
# | ||
# paginate_with default_limit: 25 | ||
# | ||
# All the options can be mixed and matches when calling `paginate_with`: | ||
# | ||
# paginate_with model_class: 'Photo', default_limit: 12 | ||
# paginate_with default_limit: 12, instance_variable_name: 'data' | ||
module Pagination | ||
extend ActiveSupport::Concern | ||
|
||
class_methods do | ||
def next_page_paginator #:nodoc: | ||
@next_page_paginator ||= NextPage::Paginator.new(controller_name, controller_path) | ||
end | ||
|
||
# Configure pagination with any of the following options: | ||
# - instance_variable_name: explicitly name the variable if it does not follow the convention | ||
# - model_class: explicitly specify the model name if it does not follow the convention | ||
# - default_limit: specify an alternate default | ||
def paginate_with(instance_variable_name: nil, model_class: nil, default_limit: nil) | ||
next_page_paginator.paginate_with(instance_variable_name, model_class, default_limit) | ||
end | ||
end | ||
|
||
# Called with before_action in order to automatically paginate the resource. | ||
def apply_next_page_pagination | ||
self.class.next_page_paginator.paginate(self, params[:page]) | ||
end | ||
|
||
# Invokes pagination directly, the result must be stored as the resource itself is not modified. | ||
def paginate_resource(resource) | ||
self.class.next_page_paginator.paginate_resource(resource, params[:page]) | ||
end | ||
|
||
def render(*args) #:nodoc: | ||
return super unless action_name == 'index' && request.headers[:Accept] == 'application/vnd.api+json' | ||
|
||
options = args.first | ||
return super unless options.is_a?(Hash) && options.key?(:json) | ||
|
||
options[:meta] = options.fetch(:meta, {}).merge!(total_pages: options[:json].total_pages) | ||
super | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# frozen_string_literal: true | ||
|
||
module NextPage | ||
# = Pagination Attributes | ||
# | ||
# Module PaginationAttributes adds in methods required for pagination links: current_page, next_page, and total_pages. | ||
# It reads the offset and limit on the query to determine the values. | ||
module PaginationAttributes | ||
def current_page | ||
@current_page ||= offset_value + 1 | ||
end | ||
|
||
def next_page | ||
current_page + 1 | ||
end | ||
|
||
def total_pages | ||
@total_pages ||= unscope(:limit).count / per_page | ||
end | ||
|
||
def per_page | ||
@per_page ||= limit_value | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# frozen_string_literal: true | ||
|
||
module NextPage | ||
# = Paginator | ||
# | ||
# Class Paginator uses the controller information to determine the model and variable name for the | ||
# request, then applies a limit and offset to the query based upon the parameters or the defaults. It also extends | ||
# the resource with the NextPage::PaginationAttributes mixin. | ||
# | ||
# Configuration can be specified in the controller by calling `paginate_with`. The following overrides can be | ||
# specified if necessary: | ||
# - default_limit: limit to use if request does not specify (default value is 10) | ||
# - instance_variable_name: default value is the controller name; for example, @photos in PhotosController | ||
# - model_class: default derived from controller name (or path if nested); for example, Photo for PhotosController | ||
class Paginator | ||
DEFAULT_LIMIT = 10 | ||
|
||
def initialize(controller_name, controller_path) | ||
@controller_name = controller_name | ||
@controller_path = controller_path | ||
|
||
@default_limit = DEFAULT_LIMIT | ||
end | ||
|
||
def paginate_with(instance_variable_name, model_class, default_limit) | ||
@default_limit = default_limit if default_limit.present? | ||
@instance_variable_name = instance_variable_name | ||
@model_class = model_class.is_a?(String) ? model_class.constantize : model_class | ||
end | ||
|
||
def paginate(controller, page_params) | ||
name = "@#{instance_variable_name}" | ||
data = controller.instance_variable_get(name) || model_class.all | ||
|
||
controller.instance_variable_set(name, paginate_resource(data, page_params)) | ||
end | ||
|
||
def paginate_resource(data, page_params) | ||
data.extend(NextPage::PaginationAttributes) | ||
|
||
limit = page_size(page_params) | ||
offset = page_number(page_params) - 1 | ||
data.limit(limit).offset(offset * limit) | ||
end | ||
|
||
private | ||
|
||
def model_class | ||
@model_class ||= @controller_name.classify.safe_constantize || | ||
@controller_path.classify.safe_constantize || | ||
raise('Could not determine model for pagination; please specify using `paginate_with` options.') | ||
end | ||
|
||
def instance_variable_name | ||
@instance_variable_name ||= @controller_name | ||
end | ||
|
||
def page_size(page) | ||
if page.present? && page[:size].present? | ||
page[:size]&.to_i | ||
else | ||
@default_limit | ||
end | ||
end | ||
|
||
def page_number(page) | ||
if page.present? && page[:number].present? | ||
page[:number]&.to_i | ||
else | ||
1 | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
|
||
RSpec.describe UniformsController, type: :controller do | ||
fixtures :jerseys | ||
|
||
describe 'GET #index' do | ||
it 'returns a success response' do | ||
get :index, params: {} | ||
expect(response).to be_successful | ||
end | ||
|
||
it 'returns the specified limit' do | ||
get :index, params: {} | ||
expect(JSON.parse(response.body).size).to eq 8 | ||
end | ||
|
||
it 'with size: 5' do | ||
get :index, params: { page: { size: 5 } } | ||
expect(JSON.parse(response.body).size).to eq 5 | ||
end | ||
|
||
it 'with size 12 and page 2' do | ||
get :index, params: { page: { size: 12, number: 2 } } | ||
expect(JSON.parse(response.body).size).to eq 8 | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# frozen_string_literal: true | ||
|
||
class UniformsController < ApplicationController | ||
include NextPage::Pagination | ||
before_action :apply_next_page_pagination, only: :index | ||
|
||
paginate_with instance_variable_name: :unis, model_class: 'Jersey', default_limit: 8 | ||
|
||
# GET /uniforms | ||
def index | ||
render json: @unis | ||
end | ||
end |
Oops, something went wrong.