jm81-paginate 0.1.1
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/LICENSE +20 -0
- data/README.md +138 -0
- data/Rakefile +62 -0
- data/TODO +2 -0
- data/lib/helpers/merb.rb +84 -0
- data/lib/helpers/shared.rb +43 -0
- data/lib/paginate.rb +32 -0
- data/lib/paginate/ar.rb +33 -0
- data/lib/paginate/dm.rb +35 -0
- data/lib/paginate/simple.rb +32 -0
- data/lib/paginators/orm.rb +17 -0
- data/lib/paginators/simple.rb +84 -0
- data/spec/fixtures/ar.rb +7 -0
- data/spec/fixtures/dm.rb +13 -0
- data/spec/helpers/merb_spec.rb +57 -0
- data/spec/helpers/shared_spec.rb +61 -0
- data/spec/paginate/ar_spec.rb +55 -0
- data/spec/paginate/config_spec.rb +31 -0
- data/spec/paginate/dm_spec.rb +47 -0
- data/spec/paginate/simple_spec.rb +27 -0
- data/spec/paginators/orm_spec.rb +49 -0
- data/spec/paginators/simple_spec.rb +37 -0
- data/spec/shared.rb +155 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +6 -0
- metadata +86 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jared Morgan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
Paginate
|
2
|
+
========
|
3
|
+
|
4
|
+
This paginate library assists in paginating collections and results of database
|
5
|
+
queries. It is particularly designed for use with DataMapper and ActiveRecord,
|
6
|
+
and for the Merb and Rails frameworks, but can be used in many other situations.
|
7
|
+
The library includes three sections (with more information on each below)
|
8
|
+
|
9
|
+
1. Paginate modules - These are the modules that a collection (such as an Array)
|
10
|
+
or a model Class extends to allow the collection to call #paginate
|
11
|
+
|
12
|
+
2. Paginator classes - classes in the Paginator module do the actual work.
|
13
|
+
Paginator::Simple is the base class.
|
14
|
+
|
15
|
+
3. Helper modules - modules with helper methods for use by frameworks
|
16
|
+
(currently Merb only).
|
17
|
+
|
18
|
+
##General Information
|
19
|
+
|
20
|
+
There are many pagination libraries available for Ruby (in particular for Ruby
|
21
|
+
ORMs). This is just one more, but with three particular design goals:
|
22
|
+
|
23
|
+
1. Add *#current_page* and *pages* singleton methods to the paginated
|
24
|
+
collection. The pagination libraries I've used usually return two values:
|
25
|
+
the collection and a total pages value. For no good reason, this bugs me.
|
26
|
+
2. A more flexible *:page* option. The *:page* option can be negative,
|
27
|
+
representing number of pages from the last (-1 being the last), as occurs
|
28
|
+
with many Ruby objects. Also, the *paginate* method will always return a
|
29
|
+
page within the actual collection. Finally, the *current_page* singleton
|
30
|
+
method returns the actual number of the page returned, which may be different
|
31
|
+
from that given.
|
32
|
+
3. Re-use as much code as possible for different situations. That is, the method
|
33
|
+
that calculates the current page and adds the singleton methods is shared
|
34
|
+
by implementations for Array, DataMapper, ActiveRecord, etc.
|
35
|
+
|
36
|
+
##Examples
|
37
|
+
|
38
|
+
Two examples are below, one using Paginate::Simple, one using Paginate::DM.
|
39
|
+
See below and the documentation of the individual modules for more details.
|
40
|
+
|
41
|
+
ary = Array.new(1,2,3,4,5)
|
42
|
+
ary.extend(Paginate::Simple)
|
43
|
+
paged = ary.paginate(:page => 1, :limit => 2)
|
44
|
+
=> [1, 2]
|
45
|
+
paged.current_page
|
46
|
+
=> 1
|
47
|
+
paged.pages
|
48
|
+
=> 3
|
49
|
+
|
50
|
+
class DmModel
|
51
|
+
include DataMapper::Resource
|
52
|
+
extend Paginate::DM
|
53
|
+
end
|
54
|
+
paged = DmModel.paginate(:page => 2, :limit => 10)
|
55
|
+
|
56
|
+
Note that neither the :page nor the :limit option is required. :page defaults
|
57
|
+
to 1, :limit defaults to *Paginate.config[:default_limit]*
|
58
|
+
(See **Configuration**).
|
59
|
+
|
60
|
+
##Configuration
|
61
|
+
|
62
|
+
Paginate has one configurable parameter (for defaults across an application):
|
63
|
+
*default_limit*. This specifies the limit (number of records per page) to use
|
64
|
+
if not given as an option to the *#paginate* method. By default, it is 10. To
|
65
|
+
change:
|
66
|
+
|
67
|
+
Paginate.config[:default_limit] = 100
|
68
|
+
|
69
|
+
For Merb apps, Merb plugin configuration can also be used:
|
70
|
+
|
71
|
+
Merb::Plugins.config[:paginate][:default_limit] = 100
|
72
|
+
|
73
|
+
##Paginate Modules
|
74
|
+
|
75
|
+
Paginate modules provide a *paginate* method to Objects that extend them. The
|
76
|
+
actual work is done by a Paginator class.
|
77
|
+
|
78
|
+
*paginate* accepts an options hash with at least two options:
|
79
|
+
|
80
|
+
- *:page*: The desired page number, 1-indexed. Negative numbers represent
|
81
|
+
page from the last page (with -1) being the last.
|
82
|
+
- *:limit*: represents records per page.
|
83
|
+
|
84
|
+
Depending on the module, other options may be passed through to, for example, an
|
85
|
+
*all* method. See individual modules for more details.
|
86
|
+
|
87
|
+
There are currently three modules.
|
88
|
+
|
89
|
+
- Paginate::Simple - for use with Arrays and similar objects.
|
90
|
+
- Paginate::DM - extended by a class that includes DataMapper::Resource
|
91
|
+
- Paginate::AR - extended by a class that inherits ActiveRecord::Base
|
92
|
+
|
93
|
+
##Paginator Classes
|
94
|
+
|
95
|
+
Paginator classes do the actual work of paginating and are called by the
|
96
|
+
modules. They can be used directly. The *#paginate* method in Paginators::Simple
|
97
|
+
does all the generic work. It calls two methods, *#get_full_count*,
|
98
|
+
and *#get_paginated_collection* to do the implementation-specific work.
|
99
|
+
Paginator classes should need to override only these two methods.
|
100
|
+
|
101
|
+
##Custom Paginators
|
102
|
+
|
103
|
+
To create an additional Paginator, create a class that inherits
|
104
|
+
Paginate::Paginators::Simple (or another Paginator). The key is to override
|
105
|
+
the following methods:
|
106
|
+
|
107
|
+
- *#get_paginated_collection* - This returns the paginated collection. At it's
|
108
|
+
disposal is the @options hash and two particular options:
|
109
|
+
- @options[:offset] is the zero-indexed offset of the first record of the
|
110
|
+
page.
|
111
|
+
- @options[:limit] is the number of items per page.
|
112
|
+
- Other options passed the #initialize method will also be available.
|
113
|
+
|
114
|
+
- *#get_full_count* - Return the total number of items on all pages. The @options
|
115
|
+
hash is available here, but :offset, :order and :limit options will not be
|
116
|
+
included. Options such as conditions will be available.
|
117
|
+
|
118
|
+
See Paginators for examples.
|
119
|
+
|
120
|
+
To use, add a paginate method to your Class or Object:
|
121
|
+
|
122
|
+
def paginate(options = {})
|
123
|
+
MyPaginator.new(self, options).paginate
|
124
|
+
end
|
125
|
+
|
126
|
+
##Helper Modules
|
127
|
+
|
128
|
+
There is currently a Helpers::Shared module and Helpers::Merb.
|
129
|
+
|
130
|
+
###Helpers::Shared
|
131
|
+
|
132
|
+
Contains one method, *#page_set*, which returns an Array of page numbers useful
|
133
|
+
in creating page links for the collection.
|
134
|
+
|
135
|
+
###Helpers::Merb
|
136
|
+
|
137
|
+
In addition to including Shared, this helper includes other methods useful for
|
138
|
+
displaying page links. See Helpers::Merb module for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'rspec'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
QBFC_ROOT = File.dirname(__FILE__)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
desc "Run all specs in spec/unit directory"
|
10
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
11
|
+
t.spec_opts = ['--options', '"spec/spec.opts"']
|
12
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'rake/gempackagetask'
|
16
|
+
|
17
|
+
require 'merb-core'
|
18
|
+
require 'merb-core/tasks/merb'
|
19
|
+
|
20
|
+
GEM_NAME = "paginate"
|
21
|
+
GEM_VERSION = "0.1.1"
|
22
|
+
AUTHOR = "Jared Morgan"
|
23
|
+
EMAIL = "jmorgan@morgancreative.net"
|
24
|
+
HOMEPAGE = "http://github.com/jm81/paginate/"
|
25
|
+
SUMMARY = "(Yet another) Pagination for DataMapper, ActiveRecord, and Array"
|
26
|
+
|
27
|
+
spec = Gem::Specification.new do |s|
|
28
|
+
s.name = GEM_NAME
|
29
|
+
s.version = GEM_VERSION
|
30
|
+
s.platform = Gem::Platform::RUBY
|
31
|
+
s.has_rdoc = true
|
32
|
+
s.extra_rdoc_files = ["README.md", "LICENSE", 'TODO']
|
33
|
+
s.summary = SUMMARY
|
34
|
+
s.description = s.summary
|
35
|
+
s.author = AUTHOR
|
36
|
+
s.email = EMAIL
|
37
|
+
s.homepage = HOMEPAGE
|
38
|
+
s.require_path = 'lib'
|
39
|
+
s.files = %w(LICENSE README.md Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
44
|
+
pkg.gem_spec = spec
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "install the plugin as a gem"
|
48
|
+
task :install do
|
49
|
+
Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Uninstall the gem"
|
53
|
+
task :uninstall do
|
54
|
+
Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "Create a gemspec file"
|
58
|
+
task :gemspec do
|
59
|
+
File.open("#{GEM_NAME}.gemspec", "w") do |file|
|
60
|
+
file.puts spec.to_ruby
|
61
|
+
end
|
62
|
+
end
|
data/lib/helpers/merb.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
module Paginate
|
2
|
+
module Helpers
|
3
|
+
# Pagination helpers for Merb applications. See Paginate::Helpers::Shared
|
4
|
+
# for additional methods
|
5
|
+
module Merb
|
6
|
+
include Shared
|
7
|
+
|
8
|
+
# A quick method for creating pagination links, using a view partial,
|
9
|
+
# (layouts/_page_links, by default).
|
10
|
+
# Arguments:
|
11
|
+
#
|
12
|
+
# - +collection+: an enumerable collection with #current_page and
|
13
|
+
# #pages methods.
|
14
|
+
# - +partial_name+: location of the partial to use
|
15
|
+
# - +padding+: Maximum number of page links before and after current_page.
|
16
|
+
def pagination_partial(collection, partial_name = "layout/page_links", padding = 3)
|
17
|
+
padding ||= 3
|
18
|
+
partial(partial_name, :current_page => collection.current_page, :pages => collection.pages, :padding => padding)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns links for pages, given a +collection+ and optional
|
22
|
+
# +padding+ (see Shared#page_set). Returned html will be along the lines
|
23
|
+
# of:
|
24
|
+
#
|
25
|
+
# <div class="pageLinks">' +
|
26
|
+
# <span class="pagePrevious"><a href="/list?page=4">«</a></span>
|
27
|
+
# <span class="pageSpacer">...</span>
|
28
|
+
# <span class="pageNumber"><a href="/list?page=3">3</a></span>
|
29
|
+
# <span class="pageNumber"><a href="/list?page=4">4</a></span>
|
30
|
+
# <span class="pageCurrent">5</span>
|
31
|
+
# <span class="pageNumber"><a href="/list?page=6">6</a></span>
|
32
|
+
# <span class="pageNumber"><a href="/list?page=7">7</a></span>
|
33
|
+
# <span class="pageSpacer">...</span>
|
34
|
+
# <span class="pageNext"><a href="/list?page=6">»</a></span>
|
35
|
+
# </div>
|
36
|
+
#
|
37
|
+
# CSS classes are pageSpacer, pageNumber, pageDisabled, pageCurrent,
|
38
|
+
# pagePrevious, and pageNext. pageLinks is the class of the enclosing div.
|
39
|
+
def page_links(collection, padding = 3)
|
40
|
+
current = collection.current_page
|
41
|
+
pages = collection.pages
|
42
|
+
|
43
|
+
tag(:div, :class => 'pageLinks') do
|
44
|
+
if current_page == 1
|
45
|
+
tag(:span, '«', :class => 'pageDisabled pagePrevious')
|
46
|
+
else
|
47
|
+
tag(:a, '«', :href => page_url(current - 1), :class => 'pagePrevious')
|
48
|
+
end
|
49
|
+
|
50
|
+
page_set(current, pages, padding).each do |page|
|
51
|
+
case page
|
52
|
+
when 0
|
53
|
+
tag(:span, "...", :class => 'pageSpacer')
|
54
|
+
when current
|
55
|
+
tag(:span, page, :class => 'pageCurrent')
|
56
|
+
else
|
57
|
+
tag(:a, page, :href => page_url(page), :class => 'pageNumber')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
if current == pages
|
62
|
+
tag(:span, '»', :class => 'pageDisabled pageNext')
|
63
|
+
else
|
64
|
+
tag(:a, '»', :href => page_url(current + 1), :class => 'pageNext')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# +page_url+ generates a URL (for page links), given a page number and
|
70
|
+
# optionally a path and query string. By default, the path is the path
|
71
|
+
# of the current request, and the query_string is also that of the
|
72
|
+
# current request. This allows for order and condition related fields
|
73
|
+
# in the query string to be used in the page link.
|
74
|
+
def page_url(page, path = request.path, q = request.query_string)
|
75
|
+
# Remove any current reference to page in the query string
|
76
|
+
q.to_s.gsub!(/page=(\d+)(&?)/, '')
|
77
|
+
# Assemble new link
|
78
|
+
link = "#{path}?page=#{page}&#{q}"
|
79
|
+
link = link[0..-2] if link[-1..-1] == '&' # Strip trailing ampersand
|
80
|
+
link
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Paginate
|
2
|
+
module Helpers
|
3
|
+
# Shared helper methods included in other Helper modules.
|
4
|
+
module Shared
|
5
|
+
# +page_set+ returns an Array of page numbers that can be used by a view
|
6
|
+
# for displaying page links. It includes the first page, the current page,
|
7
|
+
# with up to +padding+ pages before and after, and the last page. If pages
|
8
|
+
# are skipped between any of these groups, 0 stands in for them, as an
|
9
|
+
# indicator to the view that, for example, elipses might be used here.
|
10
|
+
# For example:
|
11
|
+
#
|
12
|
+
# page_set(3, 10, 3)
|
13
|
+
# => [1, 2, 3, 4, 5, 6, 0, 10] TODO test this in particular
|
14
|
+
#
|
15
|
+
# The 0 at index 6 is an indication that there are skipped pages.
|
16
|
+
def page_set(current_page, pages, padding = 3)
|
17
|
+
|
18
|
+
# Determine first and last page in group around current page
|
19
|
+
first = [1, current_page - padding].max
|
20
|
+
last = [pages, current_page + padding].min
|
21
|
+
|
22
|
+
# Determine if an additional "First page" is needed and whether any
|
23
|
+
# pages are skipped between it and the first around the current page
|
24
|
+
leader = case first
|
25
|
+
when 1 then [] # First not needed
|
26
|
+
when 2 then [1] # First needed, but none skipped
|
27
|
+
else [1, 0] # First needed, some skipped
|
28
|
+
end
|
29
|
+
|
30
|
+
# Determine if an additional "Last page" is needed and whether any
|
31
|
+
# pages are skipped between the last around the current page and it.
|
32
|
+
footer = case last
|
33
|
+
when pages then [] # Last not needed
|
34
|
+
when pages - 1 then [pages] # Last needed, but none skipped
|
35
|
+
else [0, pages] # Last needed, some skipped
|
36
|
+
end
|
37
|
+
|
38
|
+
# Join Arrays together
|
39
|
+
leader + (first..last).to_a + footer
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/paginate.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Paginate
|
2
|
+
DEFAULTS = {
|
3
|
+
:default_limit => 10
|
4
|
+
}
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# Paginate::config method returns Hash that can be edited.
|
8
|
+
def config
|
9
|
+
@config ||= DEFAULTS
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
if defined?(Merb::Plugins)
|
15
|
+
# Make config accessible through Merb's Merb::Plugins.config hash
|
16
|
+
Merb::Plugins.config[:paginate] = Paginate.config
|
17
|
+
end
|
18
|
+
|
19
|
+
# Require Paginators
|
20
|
+
%w{ simple orm }.each do |file|
|
21
|
+
require File.dirname(__FILE__) + '/paginators/' + file
|
22
|
+
end
|
23
|
+
|
24
|
+
# Require Paginate Modules
|
25
|
+
%w{ simple dm ar }.each do |file|
|
26
|
+
require File.dirname(__FILE__) + '/paginate/' + file
|
27
|
+
end
|
28
|
+
|
29
|
+
# Require Helper Modules
|
30
|
+
%w{ shared merb }.each do |file|
|
31
|
+
require File.dirname(__FILE__) + '/helpers/' + file
|
32
|
+
end
|
data/lib/paginate/ar.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Paginate
|
2
|
+
|
3
|
+
# .pagination method for ActiveRecord models.
|
4
|
+
module AR
|
5
|
+
|
6
|
+
# Implementation of .paginate for ActiveRecord.
|
7
|
+
#
|
8
|
+
# To make available to your model:
|
9
|
+
# class MyModel < ActiveRecord::Base
|
10
|
+
# extend Paginate::AR
|
11
|
+
# ...
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# Accepts same options as ActiveRecord::Base.all, plus:
|
15
|
+
# - +page+: The desired page number, 1-indexed. Negative numbers represent
|
16
|
+
# page from the last page (with -1) being the last.
|
17
|
+
#
|
18
|
+
# The +limit+ option should also be specified and represents records per
|
19
|
+
# page. Any +offset+ option will be overriden.
|
20
|
+
#
|
21
|
+
# Returns the records for the given page number, with other options
|
22
|
+
# processed by +all+. In addition, two methods are added to the result:
|
23
|
+
# - +pages+: The total number of pages that the given options would produce.
|
24
|
+
# - +current_page+: The number of the current page (as actually returned).
|
25
|
+
# This is never a negative number even if given.
|
26
|
+
# If the +page+ option is zero, or less than (-1 * pages) or greater than
|
27
|
+
# the total pages, it will be recalculated to return either page 1 or the
|
28
|
+
# last page, respectively.
|
29
|
+
def paginate(options = {})
|
30
|
+
Paginators::ORM.new(self, options).paginate
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/paginate/dm.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Paginate
|
2
|
+
|
3
|
+
# .pagination method for DataMapper models.
|
4
|
+
module DM
|
5
|
+
|
6
|
+
# Implementation of .paginate for DataMapper.
|
7
|
+
# (This is loosely based on http://github.com/lholden/dm-is-paginated)
|
8
|
+
#
|
9
|
+
# To make available to your model:
|
10
|
+
# class MyModel
|
11
|
+
# include DataMapper::Resource
|
12
|
+
# extend Paginate::DM
|
13
|
+
# ...
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Accepts same options as DataMapper::Resource.all, plus:
|
17
|
+
# - +page+: The desired page number, 1-indexed. Negative numbers represent
|
18
|
+
# page from the last page (with -1) being the last.
|
19
|
+
#
|
20
|
+
# The +limit+ option should also be specified and represents records per
|
21
|
+
# page. Any +offset+ option will be overriden.
|
22
|
+
#
|
23
|
+
# Returns the records for the given page number, with other options
|
24
|
+
# processed by +all+. In addition, two methods are added to the result:
|
25
|
+
# - +pages+: The total number of pages that the given options would produce.
|
26
|
+
# - +current_page+: The number of the current page (as actually returned).
|
27
|
+
# This is never a negative number even if given.
|
28
|
+
# If the +page+ option is zero, or less than (-1 * pages) or greater than
|
29
|
+
# the total pages, it will be recalculated to return either page 1 or the
|
30
|
+
# last page, respectively.
|
31
|
+
def paginate(options = {})
|
32
|
+
Paginators::ORM.new(self, options).paginate
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Paginate
|
2
|
+
|
3
|
+
# .pagination method for Arrays and similar objects.
|
4
|
+
module Simple
|
5
|
+
|
6
|
+
# Implementation of .paginate for Arrays and similar objects (must respond
|
7
|
+
# to +length+ and +[](start_index, length)+.
|
8
|
+
#
|
9
|
+
# To make available to all Arrays:
|
10
|
+
# Array.include(Paginate::Simple)
|
11
|
+
#
|
12
|
+
# For a single Array:
|
13
|
+
# array.extend(Paginate::Simple)
|
14
|
+
#
|
15
|
+
# Accepts two options:
|
16
|
+
# - +page+: The desired page number, 1-indexed. Negative numbers represent
|
17
|
+
# page from the last page (with -1) being the last.
|
18
|
+
#- +limit+: represents records per page.
|
19
|
+
#
|
20
|
+
# Returns a slice of the Array with records for the given page number.
|
21
|
+
# Two methods are added to the result:
|
22
|
+
# - +pages+: The total number of pages that the given options would produce.
|
23
|
+
# - +current_page+: The number of the current page (as actually returned).
|
24
|
+
# This is never a negative number even if given.
|
25
|
+
# If the +page+ option is zero, or less than (-1 * pages) or greater than
|
26
|
+
# the total pages, it will be recalculated to return either page 1 or the
|
27
|
+
# last page, respectively.
|
28
|
+
def paginate(options = {})
|
29
|
+
Paginators::Simple.new(self, options).paginate
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Paginate
|
2
|
+
module Paginators
|
3
|
+
class ORM < Simple
|
4
|
+
private
|
5
|
+
|
6
|
+
# Return the total number of records based on the given conditions.
|
7
|
+
def get_full_count
|
8
|
+
model.count(:id, @options)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return the records for the given page.
|
12
|
+
def get_paginated_collection
|
13
|
+
model.all(@options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Paginate
|
2
|
+
module Paginators
|
3
|
+
# This is for use by +Array+ and similar objects.
|
4
|
+
class Simple
|
5
|
+
# full_collection is an +Array+ (or any Object that responds to +length+
|
6
|
+
# and +[](start_index, length)+, such as +LazyArray+. #paginate will
|
7
|
+
# return the appropriate slice of that Object.
|
8
|
+
# options include:
|
9
|
+
# - +page+: The desired page (may be negative)
|
10
|
+
# - +limit+: Number of items per page.
|
11
|
+
# Other options are ignored.
|
12
|
+
# See README for more details. (TODO)
|
13
|
+
def initialize(full_collection, options = {})
|
14
|
+
@full_collection = full_collection
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
# Perform the pagination. Returns the records for the given page, with
|
19
|
+
# singleton methods:
|
20
|
+
# - +current_page+: The number of the current page.
|
21
|
+
# - +pages+: The total number of pages of records.
|
22
|
+
#
|
23
|
+
# See the documents for the module that your class extends for more details.
|
24
|
+
#
|
25
|
+
# Descendents classes should not need to override this method, but rather
|
26
|
+
# + get_full_count+ and +get_paginated_collection+ private methods.
|
27
|
+
def paginate
|
28
|
+
page = @options.delete(:page).to_i
|
29
|
+
limit = @options[:limit] ? @options[:limit].to_i : Paginate.config[:default_limit]
|
30
|
+
order = @options[:order]
|
31
|
+
|
32
|
+
# Remove some options before calling +count+ that are not applicable.
|
33
|
+
# order and limit are needed later and have been saved above.
|
34
|
+
[:offset, :limit, :order].each do |key|
|
35
|
+
@options.delete(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Determine total number of pages and set offset option.
|
39
|
+
pages = (get_full_count.to_f / limit).ceil
|
40
|
+
page = (pages + 1 + page) if page < 0 # Negative page
|
41
|
+
page = pages if page > pages # page should not be more than total pages
|
42
|
+
page = 1 if page < 1 # Minimum is 1 even if 0 records.
|
43
|
+
pages = 1 if pages < 1 # Minimum is 1 even if 0 records.
|
44
|
+
@options[:offset] = ((page - 1) * limit)
|
45
|
+
|
46
|
+
# Add limit and order back into options, from above.
|
47
|
+
@options[:limit] = limit
|
48
|
+
@options[:order] = order if order
|
49
|
+
|
50
|
+
# Call +all+.
|
51
|
+
collection = get_paginated_collection
|
52
|
+
|
53
|
+
# Create +pages+ and +current_page+ methods for collection, for use by
|
54
|
+
# pagination links.
|
55
|
+
collection.instance_variable_set(:@pages, pages)
|
56
|
+
collection.instance_variable_set(:@current_page, page)
|
57
|
+
def collection.pages; @pages; end
|
58
|
+
def collection.current_page; @current_page; end
|
59
|
+
|
60
|
+
return collection
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Alias for @full_collection because that's what the @full_collection
|
66
|
+
# usually is for ORM class
|
67
|
+
def model
|
68
|
+
@full_collection
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return the total number of items/records based on the given conditions.
|
72
|
+
# This may be overriden by inherited classes.
|
73
|
+
def get_full_count
|
74
|
+
@full_collection.length
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return the records for the given page.
|
78
|
+
# This may be overriden by inherited classes.
|
79
|
+
def get_paginated_collection
|
80
|
+
@full_collection[@options[:offset], @options[:limit]]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/spec/fixtures/ar.rb
ADDED
data/spec/fixtures/dm.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Paginate::Helpers::Merb do
|
4
|
+
before(:all) do
|
5
|
+
@klass = Class.new
|
6
|
+
@klass.__send__(:include, Paginate::Helpers::Merb)
|
7
|
+
@object = @klass.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should include #page_set method from Shared' do
|
11
|
+
@object.page_set(5, 10, 2).should ==
|
12
|
+
[1,0,3,4,5,6,7,0,10]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Being lazy and just using a mock here.
|
16
|
+
describe '#pagination_partial' do
|
17
|
+
it 'should call partial' do
|
18
|
+
collection = (1..50).to_a
|
19
|
+
collection.extend(Paginate::Simple)
|
20
|
+
collection = collection.paginate(:page => 5, :limit => 5)
|
21
|
+
vars = {
|
22
|
+
:current_page => 5,
|
23
|
+
:pages => 10,
|
24
|
+
:padding => 4
|
25
|
+
}
|
26
|
+
|
27
|
+
@object.should_receive(:partial).with('partial_name', vars)
|
28
|
+
@object.pagination_partial(collection, 'partial_name', 4)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#page_url' do
|
33
|
+
it 'should add page query option' do
|
34
|
+
@object.page_url(5, 'path', '').should ==
|
35
|
+
'path?page=5'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should leave other query options' do
|
39
|
+
@object.page_url(5, 'path', 'limit=10&something=text').should ==
|
40
|
+
'path?page=5&limit=10&something=text'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should remove existing page option' do
|
44
|
+
# middle
|
45
|
+
@object.page_url(5, 'path', 'limit=10&page=10&something=text').should ==
|
46
|
+
'path?page=5&limit=10&something=text'
|
47
|
+
|
48
|
+
# start
|
49
|
+
@object.page_url(5, 'path', 'page=10&limit=10&something=text').should ==
|
50
|
+
'path?page=5&limit=10&something=text'
|
51
|
+
|
52
|
+
# end
|
53
|
+
@object.page_url(5, 'path', 'limit=10&something=text&page=10').should ==
|
54
|
+
'path?page=5&limit=10&something=text'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Paginate::Helpers::Shared do
|
4
|
+
before(:all) do
|
5
|
+
@klass = Class.new
|
6
|
+
@klass.__send__(:include, Paginate::Helpers::Shared)
|
7
|
+
@object = @klass.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#page_set' do
|
11
|
+
it 'should pad around current page' do
|
12
|
+
@object.page_set(5, 10, 2).should ==
|
13
|
+
[1,0,3,4,5,6,7,0,10]
|
14
|
+
|
15
|
+
@object.page_set(5, 10, 1).should ==
|
16
|
+
[1,0,4,5,6,0,10]
|
17
|
+
|
18
|
+
@object.page_set(5, 10, 0).should ==
|
19
|
+
[1,0,5,0,10]
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should default padding to 3' do
|
23
|
+
@object.page_set(10, 20).should ==
|
24
|
+
[1,0,7,8,9,10,11,12,13,0,20]
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should not repeat first page' do
|
28
|
+
@object.page_set(2, 10, 2).should ==
|
29
|
+
[1,2,3,4,0,10]
|
30
|
+
|
31
|
+
@object.page_set(1, 10, 2).should ==
|
32
|
+
[1,2,3,0,10]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should not repeat last page' do
|
36
|
+
@object.page_set(9, 10, 2).should ==
|
37
|
+
[1,0,7,8,9,10]
|
38
|
+
|
39
|
+
@object.page_set(10, 10, 2).should ==
|
40
|
+
[1,0,8,9,10]
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should include 0 when pages skipped' do
|
44
|
+
@object.page_set(5, 10, 2).should ==
|
45
|
+
[1,0,3,4,5,6,7,0,10]
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should not include 0 when padding is next to first or last' do
|
49
|
+
@object.page_set(3, 10, 2).should ==
|
50
|
+
[1,2,3,4,5,0,10]
|
51
|
+
|
52
|
+
@object.page_set(8, 10, 2).should ==
|
53
|
+
[1,0,6,7,8,9,10]
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should just return [1] if only 1 page' do
|
57
|
+
@object.page_set(1, 1, 3).should ==
|
58
|
+
[1]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
# Setup AR connection, schema
|
4
|
+
ActiveRecord::Base.establish_connection(
|
5
|
+
:adapter => "sqlite3",
|
6
|
+
:dbfile => ":memory:"
|
7
|
+
)
|
8
|
+
|
9
|
+
ActiveRecord::Schema.define do
|
10
|
+
create_table :ar_models do |table|
|
11
|
+
table.column :name, :string
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
16
|
+
require File.dirname(__FILE__) + '/../fixtures/ar'
|
17
|
+
|
18
|
+
describe 'Paginate::AR' do
|
19
|
+
before(:each) do
|
20
|
+
@model = Paginate::Fixtures::ArModel
|
21
|
+
@model.destroy_all
|
22
|
+
1.upto(50) do |i|
|
23
|
+
@model.create(:name => "Person #{i}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def paginated(options = {})
|
28
|
+
@model.paginate(options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def paginated_collected(options = {})
|
32
|
+
@model.paginate(options).collect {|r| r.name}
|
33
|
+
end
|
34
|
+
|
35
|
+
def destroy_all
|
36
|
+
@model.destroy_all
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#paginate' do
|
40
|
+
it_should_behave_like "all paginate methods"
|
41
|
+
it_should_behave_like "ORM paginate methods"
|
42
|
+
|
43
|
+
it 'should order per options passed' do
|
44
|
+
paginated_collected(:limit => 3, :page => 1, :order => 'id desc').should ==
|
45
|
+
["Person 50", "Person 49", "Person 48"]
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should accept conditions (representative of options to .all)' do
|
49
|
+
collection = paginated(:limit => 3, :page => 1, :conditions => ['name like ?', 'Person 3%'])
|
50
|
+
collection.collect {|r| r.name}.should ==
|
51
|
+
["Person 3", "Person 30", "Person 31"]
|
52
|
+
collection.pages.should == 4
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "Paginate.config" do
|
4
|
+
before(:each) do
|
5
|
+
# Force to default state
|
6
|
+
Paginate.instance_variable_set(:@config, nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
after(:all) do
|
10
|
+
# Force to default state for other specs
|
11
|
+
Paginate.instance_variable_set(:@config, nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should initialize with DEFAULTS' do
|
15
|
+
Paginate.config.should == Paginate::DEFAULTS
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should be writable' do
|
19
|
+
Paginate.config[:default_limit] = 1
|
20
|
+
Paginate.instance_variable_get(:@config)[:default_limit].should == 1
|
21
|
+
end
|
22
|
+
|
23
|
+
describe ':default_limit' do
|
24
|
+
it 'should be used as default limit' do
|
25
|
+
Paginate.config[:default_limit] = 3
|
26
|
+
ary = (1..20).collect {|i| 'Item #{i}'}
|
27
|
+
ary.extend(Paginate::Simple)
|
28
|
+
ary.paginate.length.should == 3
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-aggregates'
|
3
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
4
|
+
require File.dirname(__FILE__) + '/../fixtures/dm'
|
5
|
+
|
6
|
+
describe Paginate::DM do
|
7
|
+
before(:all) do
|
8
|
+
@model = Paginate::Fixtures::DmModel
|
9
|
+
@model.auto_migrate!
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@model.all.destroy!
|
14
|
+
1.upto(50) do |i|
|
15
|
+
@model.create(:name => "Person #{i}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def paginated(options = {})
|
20
|
+
@model.paginate(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def paginated_collected(options = {})
|
24
|
+
@model.paginate(options).collect {|r| r.name}
|
25
|
+
end
|
26
|
+
|
27
|
+
def destroy_all
|
28
|
+
@model.all.destroy!
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#paginate' do
|
32
|
+
it_should_behave_like "all paginate methods"
|
33
|
+
it_should_behave_like "ORM paginate methods"
|
34
|
+
|
35
|
+
it 'should order per options passed' do
|
36
|
+
paginated_collected(:limit => 3, :page => 1, :order => [:id.desc]).should ==
|
37
|
+
["Person 50", "Person 49", "Person 48"]
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should accept conditions (representative of options to .all)' do
|
41
|
+
collection = paginated(:limit => 3, :page => 1, :name.like => 'Person 3%')
|
42
|
+
collection.collect {|r| r.name}.should ==
|
43
|
+
["Person 3", "Person 30", "Person 31"]
|
44
|
+
collection.pages.should == 4
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Paginate::Simple do
|
4
|
+
before(:each) do
|
5
|
+
@full_collection = []
|
6
|
+
1.upto(50) do |i|
|
7
|
+
@full_collection << "Person #{i}"
|
8
|
+
end
|
9
|
+
@full_collection.extend(Paginate::Simple)
|
10
|
+
end
|
11
|
+
|
12
|
+
def paginated(options = {})
|
13
|
+
@full_collection.paginate(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def paginated_collected(options = {})
|
17
|
+
paginated(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def destroy_all
|
21
|
+
50.times { @full_collection.pop }
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#paginate' do
|
25
|
+
it_should_behave_like "all paginate methods"
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-aggregates'
|
3
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
4
|
+
require File.dirname(__FILE__) + '/../fixtures/dm'
|
5
|
+
|
6
|
+
describe Paginate::Paginators::ORM do
|
7
|
+
Model = Paginate::Fixtures::DmModel
|
8
|
+
before(:all) do
|
9
|
+
Model.auto_migrate!
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
Model.all.destroy!
|
14
|
+
1.upto(50) do |i|
|
15
|
+
Model.create(:name => "Person #{i}")
|
16
|
+
end
|
17
|
+
@model = Model
|
18
|
+
@klass = Paginate::Paginators::ORM
|
19
|
+
end
|
20
|
+
|
21
|
+
def paginated(options = {})
|
22
|
+
@klass.new(Model, options).paginate
|
23
|
+
end
|
24
|
+
|
25
|
+
def paginated_collected(options = {})
|
26
|
+
paginated(options).collect {|r| r.name}
|
27
|
+
end
|
28
|
+
|
29
|
+
def destroy_all
|
30
|
+
Model.all.destroy!
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#paginate' do
|
34
|
+
it_should_behave_like "all paginate methods"
|
35
|
+
it_should_behave_like "ORM paginate methods"
|
36
|
+
|
37
|
+
it 'should order per options passed' do
|
38
|
+
paginated_collected(:limit => 3, :page => 1, :order => [:id.desc]).should ==
|
39
|
+
["Person 50", "Person 49", "Person 48"]
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should accept conditions (representative of options to .all)' do
|
43
|
+
collection = paginated(:limit => 3, :page => 1, :name.like => 'Person 3%')
|
44
|
+
collection.collect {|r| r.name}.should ==
|
45
|
+
["Person 3", "Person 30", "Person 31"]
|
46
|
+
collection.pages.should == 4
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Paginate::Paginators::Simple do
|
4
|
+
# No instance variables are expected by the shared specs, only a +paginated+
|
5
|
+
# and a +destroy_all+ method.
|
6
|
+
before(:each) do
|
7
|
+
@full_collection = []
|
8
|
+
1.upto(50) do |i|
|
9
|
+
@full_collection << "Person #{i}"
|
10
|
+
end
|
11
|
+
@klass = Paginate::Paginators::Simple
|
12
|
+
end
|
13
|
+
|
14
|
+
# paginated is called by shared specs to get a paginated result for the
|
15
|
+
# collection.
|
16
|
+
def paginated(options = {})
|
17
|
+
@klass.new(@full_collection, options).paginate
|
18
|
+
end
|
19
|
+
|
20
|
+
# paginated_collected returns results in a standard format: It should just
|
21
|
+
# return an Array of Strings, such as:
|
22
|
+
# ["Person 1", "Person 2"], so #collect may be needed in more complex
|
23
|
+
# classes.
|
24
|
+
def paginated_collected(options = {})
|
25
|
+
paginated(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
# destroy_all is called to make @full_collection an empty set. This may mean
|
29
|
+
# that all records need to be deleted in, for example, an ORM Paginator.
|
30
|
+
def destroy_all
|
31
|
+
@full_collection = []
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#paginate' do
|
35
|
+
it_should_behave_like "all paginate methods"
|
36
|
+
end
|
37
|
+
end
|
data/spec/shared.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# Specs shared by Paginator classes and Paginate modules. These test that the
|
2
|
+
# Paginator (directly or via a module) returns the right results based on
|
3
|
+
# +page+ and +limit+ options.
|
4
|
+
#
|
5
|
+
# +paginated+, +paginated_collected+ and +destroy_all+ methods are expected
|
6
|
+
# by specs. A +@model+ instance variable is expected for "ORM paginate methods".
|
7
|
+
# See simple_spec.rb for examples.
|
8
|
+
#
|
9
|
+
# +paginated+ is called by shared specs to get a paginated result for the
|
10
|
+
# collection.
|
11
|
+
#
|
12
|
+
# +paginated_collected+ calls paginate, but returns an Array of Strings. If all
|
13
|
+
# results were returned, it should return an Array of
|
14
|
+
# ["Person 1", "Person 2", ..., "Person 50"] (50 records total). #collect may be
|
15
|
+
# needed in more complex classes. For example:
|
16
|
+
#
|
17
|
+
# @klass.paginate(@full_collection, options).collect {|r| r.name}
|
18
|
+
# Model.paginate(options).collect {|r| r.name}
|
19
|
+
#
|
20
|
+
# +destroy_all+ is called to make the full collection an empty set. This may
|
21
|
+
# mean that all records need to be deleted in, for example, an ORM Paginator.
|
22
|
+
|
23
|
+
shared_examples_for "all paginate methods" do
|
24
|
+
describe ':page option' do
|
25
|
+
it 'should return correct results per page (correct offset, limit)' do
|
26
|
+
paginated_collected(:page => 1, :limit => 5).should ==
|
27
|
+
["Person 1", "Person 2", "Person 3", "Person 4", "Person 5"]
|
28
|
+
|
29
|
+
paginated_collected(:page => 2, :limit => 5).should ==
|
30
|
+
["Person 6", "Person 7", "Person 8", "Person 9", "Person 10"]
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should return page 1 if options[:page] == 0' do
|
34
|
+
paginated_collected(:page => 0, :limit => 3).should ==
|
35
|
+
["Person 1", "Person 2", "Person 3"]
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return page 1 if options[:page] not set' do
|
39
|
+
paginated_collected(:limit => 3, :page => -50).should ==
|
40
|
+
["Person 1", "Person 2", "Person 3"]
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should return last page if options[:page] > total pages' do
|
44
|
+
paginated_collected(:limit => 5, :page => 11).should ==
|
45
|
+
["Person 46", "Person 47", "Person 48", "Person 49", "Person 50"]
|
46
|
+
|
47
|
+
paginated_collected(:limit => 5, :page => 20).should ==
|
48
|
+
["Person 46", "Person 47", "Person 48", "Person 49", "Person 50"]
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should return last page for page == -1' do
|
52
|
+
paginated_collected(:limit => 5, :page => -1).should ==
|
53
|
+
["Person 46", "Person 47", "Person 48", "Person 49", "Person 50"]
|
54
|
+
|
55
|
+
paginated_collected(:limit => 3, :page => -1).should ==
|
56
|
+
["Person 49", "Person 50"]
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should return page from last for negative options[:page]' do
|
60
|
+
paginated_collected(:limit => 3, :page => -2).should ==
|
61
|
+
["Person 46", "Person 47", "Person 48"]
|
62
|
+
|
63
|
+
paginated_collected(:limit => 3, :page => -3).should ==
|
64
|
+
["Person 43", "Person 44", "Person 45"]
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should return first page for very negative options[:page] values' do
|
68
|
+
paginated_collected(:limit => 3, :page => -50).should ==
|
69
|
+
["Person 1", "Person 2", "Person 3"]
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should have at least one result on the last page' do
|
73
|
+
paginated(:limit => 5, :page => 10).should_not be_empty
|
74
|
+
|
75
|
+
collection = paginated(:limit => 5, :page => 11)
|
76
|
+
collection.should_not be_empty
|
77
|
+
collection.current_page.should == 10
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should default limit to config[:default_limit]' do
|
81
|
+
paginated().length.should == Paginate.config[:default_limit]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#current_page' do
|
86
|
+
it 'should be added to collection' do
|
87
|
+
collection = paginated(:limit => 10)
|
88
|
+
# collection.methods.should include("current_page")
|
89
|
+
# Not sure why the above won't work. #singleton_methods doesn't either.
|
90
|
+
collection.current_page.should be_kind_of(Integer)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should be the number of the current page' do
|
94
|
+
paginated(:limit => 10, :page => 2).current_page.should == 2
|
95
|
+
paginated(:limit => 10, :page => 5).current_page.should == 5
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should be 1 if there are no results' do
|
99
|
+
destroy_all
|
100
|
+
paginated(:limit => 10).current_page.should == 1
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should be >= 1 and <= #pages (ie actual page, not given page)' do
|
104
|
+
paginated(:limit => 10, :page => -100).current_page.should == 1
|
105
|
+
paginated(:limit => 10, :page => 100).current_page.should == 5
|
106
|
+
paginated(:limit => 10, :page => -1).current_page.should == 5
|
107
|
+
paginated(:limit => 10, :page => 0).current_page.should == 1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#pages' do
|
112
|
+
it 'should be added to collection' do
|
113
|
+
collection = paginated(:limit => 10)
|
114
|
+
# collection.methods.should include("pages")
|
115
|
+
# Not sure why the above won't work. #singleton_methods doesn't either.
|
116
|
+
collection.pages.should be_kind_of(Integer)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should be the total number of pages for the collection' do
|
120
|
+
paginated(:limit => 10).pages.should == 5
|
121
|
+
paginated(:limit => 49).pages.should == 2
|
122
|
+
paginated(:limit => 50).pages.should == 1
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should be 1 if there are no results' do
|
126
|
+
destroy_all
|
127
|
+
paginated(:limit => 10).pages.should == 1
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# These specs are specific to ORM classes/modules, testing that +all+ receives
|
133
|
+
# the correct options.
|
134
|
+
#
|
135
|
+
# A +@model+ instance variable is expected.
|
136
|
+
shared_examples_for "ORM paginate methods" do
|
137
|
+
it "should set limit and offset options" do
|
138
|
+
limit = 10
|
139
|
+
|
140
|
+
@model.should_receive(:all).with(:offset => 0, :limit => limit)
|
141
|
+
paginated(:page => 1, :limit => limit)
|
142
|
+
|
143
|
+
@model.should_receive(:all).with(:offset => 0, :limit => limit + 1)
|
144
|
+
paginated(:page => 1, :limit => limit + 1)
|
145
|
+
|
146
|
+
@model.should_receive(:all).with(:offset => 0, :limit => limit + 2)
|
147
|
+
paginated(:limit => limit + 2)
|
148
|
+
|
149
|
+
@model.should_receive(:all).with(:offset => limit, :limit => limit)
|
150
|
+
paginated(:page => 2, :limit => limit)
|
151
|
+
|
152
|
+
@model.should_receive(:all).with(:offset => 40, :limit => limit)
|
153
|
+
paginated(:page => -1, :limit => limit)
|
154
|
+
end
|
155
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jm81-paginate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jared Morgan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-22 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: (Yet another) Pagination for DataMapper, ActiveRecord, and Array
|
17
|
+
email: jmorgan@morgancreative.net
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.md
|
24
|
+
- LICENSE
|
25
|
+
- TODO
|
26
|
+
files:
|
27
|
+
- LICENSE
|
28
|
+
- README.md
|
29
|
+
- Rakefile
|
30
|
+
- TODO
|
31
|
+
- lib/helpers
|
32
|
+
- lib/helpers/merb.rb
|
33
|
+
- lib/helpers/shared.rb
|
34
|
+
- lib/paginate
|
35
|
+
- lib/paginate/ar.rb
|
36
|
+
- lib/paginate/dm.rb
|
37
|
+
- lib/paginate/simple.rb
|
38
|
+
- lib/paginate.rb
|
39
|
+
- lib/paginators
|
40
|
+
- lib/paginators/orm.rb
|
41
|
+
- lib/paginators/simple.rb
|
42
|
+
- spec/fixtures
|
43
|
+
- spec/fixtures/ar.rb
|
44
|
+
- spec/fixtures/dm.rb
|
45
|
+
- spec/helpers
|
46
|
+
- spec/helpers/merb_spec.rb
|
47
|
+
- spec/helpers/shared_spec.rb
|
48
|
+
- spec/paginate
|
49
|
+
- spec/paginate/ar_spec.rb
|
50
|
+
- spec/paginate/config_spec.rb
|
51
|
+
- spec/paginate/dm_spec.rb
|
52
|
+
- spec/paginate/simple_spec.rb
|
53
|
+
- spec/paginators
|
54
|
+
- spec/paginators/orm_spec.rb
|
55
|
+
- spec/paginators/simple_spec.rb
|
56
|
+
- spec/shared.rb
|
57
|
+
- spec/spec.opts
|
58
|
+
- spec/spec_helper.rb
|
59
|
+
has_rdoc: true
|
60
|
+
homepage: http://github.com/jm81/paginate/
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
version:
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.2.0
|
82
|
+
signing_key:
|
83
|
+
specification_version: 2
|
84
|
+
summary: (Yet another) Pagination for DataMapper, ActiveRecord, and Array
|
85
|
+
test_files: []
|
86
|
+
|