jm81-paginate 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|