taylorbarstow-simple_pagination 0.1.0
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.
- data/VERSION.yml +4 -0
- data/lib/simple_pagination.rb +3 -0
- data/lib/simple_pagination/page.rb +59 -0
- data/lib/simple_pagination/page_collection.rb +68 -0
- data/test/simple_pagination/page_collection_test.rb +99 -0
- data/test/simple_pagination/page_test.rb +108 -0
- data/test/test_helper.rb +5 -0
- metadata +62 -0
data/VERSION.yml
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module SimplePagination
|
2
|
+
class Page
|
3
|
+
def initialize(number, collection)
|
4
|
+
@number, @collection = number, collection
|
5
|
+
end
|
6
|
+
|
7
|
+
##
|
8
|
+
# The zero-based offset of the first record in this page
|
9
|
+
def offset
|
10
|
+
(@number-1) * @collection.page_size + 1
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# The one-based page number for this page
|
15
|
+
def number
|
16
|
+
@number
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# The Page object after this page (or +nil+ if +self.last?+)
|
21
|
+
def next
|
22
|
+
@collection[number + 1]
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# The Page object before this page (or +nil+ if +self.first?+)
|
27
|
+
def prev
|
28
|
+
@collection[number - 1]
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Test if this is the first page in the collection
|
33
|
+
def first?
|
34
|
+
number == 1
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Test if this is the last page in the collection
|
39
|
+
def last?
|
40
|
+
number == @collection.length
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Test if this is the current page.
|
45
|
+
def current?
|
46
|
+
self === @collection.current
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Generate a "window" of pages around (and including) this page. +n+
|
51
|
+
# is the number of pages to show (defaults to 10). For example, if this is
|
52
|
+
# page 8 and you ask for window(10) you will get an array of Page objects
|
53
|
+
# representing the following pages: 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
|
54
|
+
def window(n=10)
|
55
|
+
start = [1, [number-(n/2)+1, @collection.length-n+1].min].max
|
56
|
+
(start..start+n-1).collect {|i| @collection[i]}.compact
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module SimplePagination
|
2
|
+
class PageCollection
|
3
|
+
##
|
4
|
+
# Create a new PageCollection object. The following options are available.
|
5
|
+
# [:current_offset] The zero-based offset of the first record in the current page of results
|
6
|
+
# [:current_page] The one-based offset of the current page of results
|
7
|
+
# [:total] The total number of results across all pages
|
8
|
+
# [:page_size] The page size
|
9
|
+
#
|
10
|
+
# You can choose between :current_offset and :current_page based on your
|
11
|
+
# needs. Same goes for :total_records and :total_pages. :page_size
|
12
|
+
# is required.
|
13
|
+
#
|
14
|
+
# A typical use of this class would be to define a <tt>pages</tt> method on your
|
15
|
+
# result set class which returns an instance of PageCollection. (So you can
|
16
|
+
# do, e.g., <tt>my_result_set.pages.current</tt> and so on.)
|
17
|
+
def initialize(options={})
|
18
|
+
check_options(options)
|
19
|
+
@pages, @page_size = {}, options[:page_size]
|
20
|
+
@length = options[:total_pages] || options[:total_records] / @page_size
|
21
|
+
@current_page = options[:current_page] || (options[:current_offset]/@page_size)+1
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# A Page object representing the page at a given page number (one-based).
|
26
|
+
def [](i)
|
27
|
+
@pages[i] ||= Page.new(i, self) unless out_of_bounds?(i)
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# A Page object representing the current page in the collection.
|
32
|
+
def current
|
33
|
+
self[@current_page]
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# A Page object representing the first page in the collection.
|
38
|
+
def first
|
39
|
+
self[1]
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# A Page object representing the last page in the collection.
|
44
|
+
def last
|
45
|
+
self[length]
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# The number of pages in the collection.
|
50
|
+
attr_reader :length
|
51
|
+
|
52
|
+
##
|
53
|
+
# The number of records shown on each page in the collection.
|
54
|
+
attr_reader :page_size
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def check_options(options)
|
59
|
+
raise ArgumentError, "requires :current_offset or :current_page" unless options[:current_offset] || options[:current_page]
|
60
|
+
raise ArgumentError, "requires :page_size" unless options[:page_size]
|
61
|
+
raise ArgumentError, "requires :total" unless options[:total_pages] or options[:total_records]
|
62
|
+
end
|
63
|
+
|
64
|
+
def out_of_bounds?(i)
|
65
|
+
i > length || i < 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper.rb')
|
2
|
+
|
3
|
+
class PageCollectionTest < Test::Unit::TestCase
|
4
|
+
include SimplePagination
|
5
|
+
|
6
|
+
context "creating a new instance" do
|
7
|
+
should "require :current_offset or :current_page" do
|
8
|
+
args = {:page_size => 10, :total_records => 100}
|
9
|
+
assert_raise(ArgumentError) {PageCollection.new(args)}
|
10
|
+
assert_nothing_raised {PageCollection.new(args.merge(:current_offset => 0))}
|
11
|
+
assert_nothing_raised {PageCollection.new(args.merge(:current_page => 1))}
|
12
|
+
end
|
13
|
+
|
14
|
+
should "require :total_records or :total_pages" do
|
15
|
+
args = {:page_size => 10, :current_page => 1}
|
16
|
+
assert_raise(ArgumentError) {PageCollection.new(args)}
|
17
|
+
assert_nothing_raised {PageCollection.new(args.merge(:total_pages => 10))}
|
18
|
+
assert_nothing_raised {PageCollection.new(args.merge(:total_records => 10))}
|
19
|
+
end
|
20
|
+
|
21
|
+
should "require :page_size" do
|
22
|
+
args = {:total_records => 100, :current_offset => 0}
|
23
|
+
assert_raise(ArgumentError) {PageCollection.new(args)}
|
24
|
+
assert_nothing_raised {PageCollection.new(args.merge(:page_size => 10))}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "accessors" do
|
29
|
+
setup do
|
30
|
+
@collection = PageCollection.new(:total_pages => 10, :page_size => 10, :current_offset => 0)
|
31
|
+
end
|
32
|
+
|
33
|
+
should "provide page_size accessor" do
|
34
|
+
assert_equal 10, @collection.page_size
|
35
|
+
end
|
36
|
+
|
37
|
+
should "provide length accessor" do
|
38
|
+
assert_equal 10, @collection.length
|
39
|
+
end
|
40
|
+
|
41
|
+
should "calculate length when given :total_records" do
|
42
|
+
c = PageCollection.new(:total_records => 113, :page_size => 3, :current_offset => 0)
|
43
|
+
assert_equal 37, c.length
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "accessing a specific page" do
|
48
|
+
setup do
|
49
|
+
@collection = PageCollection.new(:total_pages => 10, :page_size => 10, :current_page=>1)
|
50
|
+
end
|
51
|
+
|
52
|
+
should "create and return a new Page instance" do
|
53
|
+
p = Page.new(1, @collection)
|
54
|
+
Page.expects(:new).with(1, @collection).returns(p)
|
55
|
+
assert_same p, @collection[1]
|
56
|
+
end
|
57
|
+
|
58
|
+
should "return the same Page across multiple invocations" do
|
59
|
+
assert_same @collection[3], @collection[3]
|
60
|
+
assert_not_same @collection[3], @collection[4]
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when the page is out of bounds" do
|
64
|
+
should "return nil" do
|
65
|
+
assert_equal nil, @collection[100]
|
66
|
+
assert_equal nil, @collection[0]
|
67
|
+
assert_equal nil, @collection[-1]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "accessing named pages" do
|
73
|
+
setup do
|
74
|
+
@collection = PageCollection.new(:total_pages => 10, :page_size => 10, :current_page=>1)
|
75
|
+
end
|
76
|
+
|
77
|
+
should "access current page" do
|
78
|
+
(1..3).each do |i|
|
79
|
+
c = PageCollection.new(:total_pages => 10, :page_size => 10, :current_page=>i)
|
80
|
+
c.expects(:[]).with(i)
|
81
|
+
c.current
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
should "access first page" do
|
86
|
+
@collection.expects(:[]).with(1)
|
87
|
+
@collection.first
|
88
|
+
end
|
89
|
+
|
90
|
+
should "access last page" do
|
91
|
+
@collection.expects(:[]).with(10)
|
92
|
+
@collection.last
|
93
|
+
|
94
|
+
c = PageCollection.new(:total_records => 113, :page_size => 3, :current_page => 1)
|
95
|
+
c.expects(:[]).with(37)
|
96
|
+
c.last
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper.rb')
|
2
|
+
|
3
|
+
class PageTest < Test::Unit::TestCase
|
4
|
+
include SimplePagination
|
5
|
+
|
6
|
+
def assert_page_window(expected, actual)
|
7
|
+
actual.each {|page| assert_instance_of Page, page}
|
8
|
+
assert_equal expected, actual.collect {|page| page.number}
|
9
|
+
end
|
10
|
+
|
11
|
+
context "accessors" do
|
12
|
+
setup do
|
13
|
+
@collection = PageCollection.new(:current_page => 1, :total_pages => 10, :page_size => 10)
|
14
|
+
@page = Page.new(5, @collection)
|
15
|
+
end
|
16
|
+
|
17
|
+
should "provide accessor for page number" do
|
18
|
+
assert_equal 5, @page.number
|
19
|
+
end
|
20
|
+
|
21
|
+
should "provide accessor for first record offset" do
|
22
|
+
assert_equal 41, @page.offset
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "next/previous page" do
|
27
|
+
setup do
|
28
|
+
@collection = PageCollection.new(:current_page => 1, :total_pages => 10, :page_size => 10)
|
29
|
+
end
|
30
|
+
|
31
|
+
should "provide previous page using PageCollection#[]" do
|
32
|
+
@collection.expects(:[]).with(2)
|
33
|
+
Page.new(3, @collection).prev
|
34
|
+
|
35
|
+
@collection.expects(:[]).with(4)
|
36
|
+
Page.new(5, @collection).prev
|
37
|
+
end
|
38
|
+
|
39
|
+
should "provide next page using PageCollection#[]" do
|
40
|
+
@collection.expects(:[]).with(2)
|
41
|
+
Page.new(1, @collection).next
|
42
|
+
|
43
|
+
@collection.expects(:[]).with(4)
|
44
|
+
Page.new(3, @collection).next
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "named page tests" do
|
49
|
+
setup do
|
50
|
+
@collection = PageCollection.new(:current_page => 1, :total_pages => 10, :page_size => 10)
|
51
|
+
end
|
52
|
+
|
53
|
+
should "identify last page" do
|
54
|
+
assert @collection[10].last?
|
55
|
+
end
|
56
|
+
|
57
|
+
should "not identify non last page as last" do
|
58
|
+
(1..9).each {|i| assert !@collection[i].last?}
|
59
|
+
end
|
60
|
+
|
61
|
+
should "identify first page" do
|
62
|
+
assert @collection[1].first?
|
63
|
+
end
|
64
|
+
|
65
|
+
should "not identify non first page as first" do
|
66
|
+
(2..10).each {|i| assert !@collection[i].first?}
|
67
|
+
end
|
68
|
+
|
69
|
+
should "identify current page" do
|
70
|
+
assert @collection[1].current?
|
71
|
+
end
|
72
|
+
|
73
|
+
should "not identify non current page as current" do
|
74
|
+
(2..10).each {|i| assert !@collection[i].current?}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "generating a page window" do
|
79
|
+
setup do
|
80
|
+
@collection = PageCollection.new(:current_page => 1, :page_size => 10, :total_pages => 50)
|
81
|
+
end
|
82
|
+
|
83
|
+
should "generate page window of the requested size" do
|
84
|
+
assert_equal 10, @collection[10].window(10).length
|
85
|
+
assert_equal 5, @collection[10].window(5).length
|
86
|
+
end
|
87
|
+
|
88
|
+
should "generate page window when near beginning" do
|
89
|
+
(1..5).each do |i|
|
90
|
+
assert_page_window (1..10).to_a, @collection[i].window(10)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
should "generate page window when in middle" do
|
95
|
+
assert_page_window (20..29).to_a, @collection[24].window(10)
|
96
|
+
end
|
97
|
+
|
98
|
+
should "generate page window when near end" do
|
99
|
+
(46..50).each do |i|
|
100
|
+
assert_page_window (41..50).to_a, @collection[i].window(10)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
should "generate page window when requesting window larger than # of pages" do
|
105
|
+
assert_page_window (1..50).to_a, @collection[1].window(100)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: taylorbarstow-simple_pagination
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Taylor Barstow
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-11 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A simple, standalone pagination gem for non-ActiveRecord data.
|
17
|
+
email: taylorbarstow@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- lib/simple_pagination
|
27
|
+
- lib/simple_pagination/page.rb
|
28
|
+
- lib/simple_pagination/page_collection.rb
|
29
|
+
- lib/simple_pagination.rb
|
30
|
+
- test/simple_pagination
|
31
|
+
- test/simple_pagination/page_collection_test.rb
|
32
|
+
- test/simple_pagination/page_test.rb
|
33
|
+
- test/test_helper.rb
|
34
|
+
has_rdoc: true
|
35
|
+
homepage: http://github.com/taylorbarstow/simple_pagination
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --inline-source
|
39
|
+
- --charset=UTF-8
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 1.2.0
|
58
|
+
signing_key:
|
59
|
+
specification_version: 2
|
60
|
+
summary: A simple, standalone pagination gem non-ActiveRecord data.
|
61
|
+
test_files: []
|
62
|
+
|