ar_pagination 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +86 -0
  7. data/Rakefile +2 -0
  8. data/ar_pagination.gemspec +28 -0
  9. data/lib/ar_pagination.rb +10 -0
  10. data/lib/ar_pagination/cursor_pagination/page.rb +41 -0
  11. data/lib/ar_pagination/cursor_pagination/query.rb +77 -0
  12. data/lib/ar_pagination/helpers/sort.rb +28 -0
  13. data/lib/ar_pagination/offset_pagination/page.rb +31 -0
  14. data/lib/ar_pagination/respond_with_page.rb +30 -0
  15. data/lib/ar_pagination/version.rb +3 -0
  16. data/spec/dummy/.DS_Store +0 -0
  17. data/spec/dummy/README.rdoc +28 -0
  18. data/spec/dummy/Rakefile +6 -0
  19. data/spec/dummy/app/assets/images/.keep +0 -0
  20. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  21. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  22. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  23. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  24. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  25. data/spec/dummy/app/mailers/.keep +0 -0
  26. data/spec/dummy/app/models/.keep +0 -0
  27. data/spec/dummy/app/models/concerns/.keep +0 -0
  28. data/spec/dummy/app/models/foo.rb +2 -0
  29. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  30. data/spec/dummy/bin/bundle +3 -0
  31. data/spec/dummy/bin/rails +4 -0
  32. data/spec/dummy/bin/rake +4 -0
  33. data/spec/dummy/bin/setup +29 -0
  34. data/spec/dummy/config.ru +4 -0
  35. data/spec/dummy/config/application.rb +26 -0
  36. data/spec/dummy/config/boot.rb +5 -0
  37. data/spec/dummy/config/database.yml +85 -0
  38. data/spec/dummy/config/environment.rb +5 -0
  39. data/spec/dummy/config/environments/development.rb +41 -0
  40. data/spec/dummy/config/environments/production.rb +79 -0
  41. data/spec/dummy/config/environments/test.rb +42 -0
  42. data/spec/dummy/config/initializers/assets.rb +11 -0
  43. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  44. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  45. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  46. data/spec/dummy/config/initializers/inflections.rb +16 -0
  47. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  48. data/spec/dummy/config/initializers/session_store.rb +3 -0
  49. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  50. data/spec/dummy/config/locales/en.yml +23 -0
  51. data/spec/dummy/config/routes.rb +56 -0
  52. data/spec/dummy/config/secrets.yml +22 -0
  53. data/spec/dummy/db/migrate/20150527214335_create_foos.rb +10 -0
  54. data/spec/dummy/db/schema.rb +26 -0
  55. data/spec/dummy/lib/assets/.keep +0 -0
  56. data/spec/dummy/public/404.html +67 -0
  57. data/spec/dummy/public/422.html +67 -0
  58. data/spec/dummy/public/500.html +66 -0
  59. data/spec/dummy/public/favicon.ico +0 -0
  60. data/spec/dummy/test/fixtures/foos.yml +9 -0
  61. data/spec/dummy/test/models/foo_test.rb +7 -0
  62. data/spec/lib/ar_pagination/.DS_Store +0 -0
  63. data/spec/lib/ar_pagination/cursor_pagination/query_spec.rb +260 -0
  64. data/spec/lib/ar_pagination/offset_pagination/page_spec.rb +30 -0
  65. data/spec/rails_helper.rb +29 -0
  66. data/spec/spec_helper.rb +102 -0
  67. metadata +259 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5393edb9113a17c5b78f4649638852b8576e16a1
4
+ data.tar.gz: bc89aaaaf25316e4adae150274652ba844bff02c
5
+ SHA512:
6
+ metadata.gz: 19734ea986f4d68b53f4ec410d18003a407b84d76b8f8ed3907491584a22e102d36a2939f9920a96388e09c8d4a7283ef16dfc3eb9df8e6cbc9350f508345860
7
+ data.tar.gz: 677c5c351073647f5a849240127d50687fa6e151df907659139323a0c76d23b765af8041d7e1bcd3ba35bfd0e8fa0d41927b9e09eda2f0f1e782f9e23e83eda0
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /spec/dummy/log/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ar_pagination.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Madeline Carson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,86 @@
1
+ # ArPagination
2
+
3
+ Active Record Pagination is a gem that allows simple pagination for your api's resource endpoint responses.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ar_pagination'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ar_pagination
20
+
21
+ ## Usage
22
+
23
+ ### Cursor Pagination
24
+
25
+ The cursor is the value of an attribute you would like to fetch a number of resources before/after.
26
+
27
+ Add the following to your controller:
28
+
29
+ Query params:
30
+
31
+ ```
32
+ def query_params
33
+ validate_range!(:count, 1..100)
34
+ query_params = params.permit(:cursor, :count, :sort) #sort is like: "+age,-name" to sort first by age ascending and name descending
35
+ query_params
36
+ end
37
+ ```
38
+
39
+ Include the concern:
40
+
41
+ ```
42
+ class SomeController < ActionController::Base
43
+ include ArPagination::RespondWithPage
44
+ ```
45
+
46
+ Use for the response
47
+
48
+ ```
49
+ def index
50
+ ...
51
+ @page = ArPagination::CursorPagination::Query.new(@resource_scope).fetch(query_params)
52
+
53
+ respond_with_page @page
54
+ end
55
+ ```
56
+
57
+ ### Offset Pagination
58
+
59
+ The offset specifies the number of resources to fetch the count number of resources after.
60
+
61
+ Add the query params to your controller:
62
+
63
+ ```
64
+ def query_params
65
+ validate_range!(:count, 1...100)
66
+ query_params = params.permit(:count, :offset, :sort)
67
+ query_params
68
+ end
69
+ ```
70
+
71
+ Use in your endpoint method, similar to:
72
+
73
+ ```
74
+ def index
75
+ ...
76
+ respond_with_page @page = OffsetPagination::Page.new(@resource_scope, query_params)
77
+ end
78
+ ```
79
+
80
+ ## Contributing
81
+
82
+ 1. Fork it ( https://github.com/[my-github-username]/ar_pagination/fork )
83
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
84
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
85
+ 4. Push to the branch (`git push origin my-new-feature`)
86
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ar_pagination/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ar_pagination"
8
+ spec.version = ArPagination::VERSION
9
+ spec.authors = ["Madeline Carson"]
10
+ spec.email = ["madeline.carson@gmail.com"]
11
+ spec.summary = %q{Pagination for Rails controllers. Includes cursor pagination and offset pagination.}
12
+ spec.description = %q{Pagination for Rails controllers.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "rspec-rails"
25
+ spec.add_development_dependency "rails"
26
+ spec.add_development_dependency "pg"
27
+ spec.add_development_dependency "activesupport"
28
+ end
@@ -0,0 +1,10 @@
1
+ require "ar_pagination/version"
2
+
3
+ require "ar_pagination/respond_with_page.rb"
4
+ require "ar_pagination/cursor_pagination/page.rb"
5
+ require "ar_pagination/cursor_pagination/query.rb"
6
+ require "ar_pagination/offset_pagination/page.rb"
7
+
8
+ module ArPagination
9
+ # ...
10
+ end
@@ -0,0 +1,41 @@
1
+ module ArPagination::CursorPagination
2
+ class Page
3
+
4
+ attr_reader :count
5
+ attr_accessor :first, :last
6
+
7
+ # param [Array] window in current window
8
+ # param [Fixnum] cursor previous cursor
9
+ # option [Fixnum] count
10
+ def initialize(window, cursor, cursor_key, count: 20)
11
+ @window = window
12
+ @cursor = cursor
13
+ @cursor_key = cursor_key
14
+ @count = count
15
+ end
16
+
17
+ def data
18
+ @window[1..-2]
19
+ end
20
+
21
+ def next
22
+ @window.last.nil? ? nil : @window.last.try(@cursor_key)
23
+ end
24
+
25
+ def prev
26
+ @window.first.nil? ? nil : @window.first.try(@cursor_key)
27
+ end
28
+
29
+ def params_for(dir)
30
+ case dir
31
+ when :first
32
+ { cursor: cursor, count: @count }
33
+ when :next
34
+ { cursor: self.next, count: @count }
35
+ when :prev
36
+ { cursor: self.prev, count: @count }
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,77 @@
1
+ require "ar_pagination/helpers/sort"
2
+
3
+ module ArPagination::CursorPagination
4
+ class Query
5
+
6
+ # @param [ActiveRecord::Relation] scope inwhich to find current page
7
+ # @option [Symbol] scope `:id` key to use for the cursor
8
+ def initialize(scope, cursor_key = :id)
9
+ @scope = scope
10
+ @cursor_key = cursor_key
11
+ end
12
+
13
+ # Fetch record for given cursor
14
+ #
15
+ # @see https://dev.twitter.com/overview/api/cursoring
16
+ # @option [Fixnum] cursor
17
+ # @option [Fixnum] count
18
+ # @return [Pagination::Page] dataset including next&prev cursors
19
+ def fetch(cursor: -1, count: 50, sort: "")
20
+
21
+ # 1. sort if required
22
+ @scope = ArPagination::Helpers::Sort.new(@scope).sort(sort)
23
+
24
+ # 2. only need array of cursor keys from scope
25
+ scope_keyed = @scope.pluck(@cursor_key)
26
+
27
+ cursor = 0 if cursor == -1
28
+
29
+ # check direction
30
+ window =
31
+ if cursor.to_s.first == "-"
32
+ cursor = cursor.is_a?(Integer) ? cursor.abs : cursor.to_s[1..-1]
33
+ backward(@scope, scope_keyed.index(cursor), count)
34
+ else
35
+ forward(@scope, scope_keyed.index(cursor), count)
36
+ end
37
+
38
+ Page.new(window, cursor, @cursor_key, count: count)
39
+ end
40
+
41
+ private
42
+
43
+ def forward(scope, cursor_index, count)
44
+ if cursor_index == 0
45
+ if count < scope.size # Index at count elements is not the end
46
+ return scope[cursor_index..count].unshift(nil)
47
+ else
48
+ return scope[cursor_index..(count-1)].unshift(nil).push(nil)
49
+ end
50
+ end
51
+
52
+ if count >= (scope.size - cursor_index)
53
+ return scope[(cursor_index - 1)..-1].push(nil)
54
+ end
55
+
56
+ return scope[(cursor_index - 1)..(count + 1)]
57
+ end
58
+
59
+ def backward(scope, cursor_index, count)
60
+ if (cursor_index + 1) - count <= 0 # check the returned data elements will include the first element in scope
61
+ if (cursor_index + 1) == scope.size # cursor is last element in array
62
+ return scope[0..cursor_index].unshift(nil).push(nil)
63
+ else
64
+ return scope[0..(cursor_index+1)].unshift(nil)
65
+ end
66
+ end
67
+
68
+ # Check if cursor element is the last element
69
+ if (cursor_index + 1) == scope.size # cursor is last element in array
70
+ return scope[(cursor_index - count)..cursor_index].push(nil)
71
+ end
72
+
73
+ return scope[(cursor_index - count)..cursor_index+1]
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,28 @@
1
+ module ArPagination::Helpers
2
+ class Sort
3
+
4
+ def initialize(scope)
5
+ @scope = scope
6
+ end
7
+
8
+ def sort(sort)
9
+ order_hash = {}
10
+ order_options = sort.split(',') if sort
11
+ Array.wrap(order_options).each do |order_option|
12
+ case order_option.first
13
+ when '-'
14
+ direction = :desc
15
+ when '+'
16
+ direction = :asc
17
+ else
18
+ next
19
+ end
20
+ sort_attr = order_option[1..-1]
21
+ order_hash[sort_attr.to_sym] = direction
22
+ end
23
+ @scope = @scope.order(order_hash) unless order_hash.empty?
24
+ @scope
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ require "ar_pagination/helpers/sort"
2
+
3
+ module ArPagination::OffsetPagination
4
+ class Page
5
+
6
+ def initialize(scope, options = {})
7
+ @scope = scope
8
+ @limit = Integer(options[:limit] || options[:count] || 50)
9
+ @offset = Integer(options[:offset] || 0)
10
+ @sort = options[:sort]
11
+ end
12
+
13
+ def data
14
+ @scope = ArPagination::Helpers::Sort.new(@scope).sort(@sort)
15
+
16
+ @scope.offset(@offset).limit(@limit)
17
+ end
18
+
19
+ def params_for(direction)
20
+ case direction
21
+ when :first
22
+ { offset: 0, count: @limit }
23
+ when :next
24
+ { offset: @limit + @offset, count: @limit }
25
+ when :prev
26
+ @limit > @offset ? { offset: @limit - @offset, count: @limit } : {}
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ module ArPagination::RespondWithPage
2
+ extend ActiveSupport::Concern
3
+
4
+ private
5
+
6
+ def respond_with_page(page)
7
+ render json: {
8
+ data: serialize_page(page),
9
+ links: {
10
+ first: build_url_for(page, :first),
11
+ last: nil, # @note find efficient way if needed
12
+ next: build_url_for(page, :next),
13
+ prev: build_url_for(page, :prev)
14
+ }
15
+ }
16
+ end
17
+
18
+ def serialize_page(page)
19
+ ActiveModel::ArraySerializer.new(page.data, url_options: url_options, scope: current_user)
20
+ end
21
+
22
+ def query_options
23
+ params.slice(:count, :offset, :cursor)
24
+ end
25
+
26
+ def build_url_for(page, direction)
27
+ url_params = page.params_for(direction)
28
+ url_for(url_params.reverse_merge(params)) if url_params[:cursor]
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module ArPagination
2
+ VERSION = "0.0.7"
3
+ end
Binary file
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks