ar_pagination 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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