Revision 1298:4f746d8966dd lib/plugins/classic_pagination
| lib/plugins/classic_pagination/CHANGELOG | ||
|---|---|---|
| 1 |
* Exported the changelog of Pagination code for historical reference. |
|
| 2 |
|
|
| 3 |
* Imported some patches from Rails Trac (others closed as "wontfix"): |
|
| 4 |
#8176, #7325, #7028, #4113. Documentation is much cleaner now and there |
|
| 5 |
are some new unobtrusive features! |
|
| 6 |
|
|
| 7 |
* Extracted Pagination from Rails trunk (r6795) |
|
| 8 |
|
|
| 9 |
# |
|
| 10 |
# ChangeLog for /trunk/actionpack/lib/action_controller/pagination.rb |
|
| 11 |
# |
|
| 12 |
# Generated by Trac 0.10.3 |
|
| 13 |
# 05/20/07 23:48:02 |
|
| 14 |
# |
|
| 15 |
|
|
| 16 |
09/03/06 23:28:54 david [4953] |
|
| 17 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 18 |
Docs and deprecation |
|
| 19 |
|
|
| 20 |
08/07/06 12:40:14 bitsweat [4715] |
|
| 21 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 22 |
Deprecate direct usage of @params. Update ActionView::Base for |
|
| 23 |
instance var deprecation. |
|
| 24 |
|
|
| 25 |
06/21/06 02:16:11 rick [4476] |
|
| 26 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 27 |
Fix indent in pagination documentation. Closes #4990. [Kevin Clark] |
|
| 28 |
|
|
| 29 |
04/25/06 17:42:48 marcel [4268] |
|
| 30 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 31 |
Remove all remaining references to @params in the documentation. |
|
| 32 |
|
|
| 33 |
03/16/06 06:38:08 rick [3899] |
|
| 34 |
* trunk/actionpack/lib/action_view/helpers/pagination_helper.rb (modified) |
|
| 35 |
trivial documentation patch for #pagination_links [Francois |
|
| 36 |
Beausoleil] closes #4258 |
|
| 37 |
|
|
| 38 |
02/20/06 03:15:22 david [3620] |
|
| 39 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 40 |
* trunk/actionpack/test/activerecord/pagination_test.rb (modified) |
|
| 41 |
* trunk/activerecord/CHANGELOG (modified) |
|
| 42 |
* trunk/activerecord/lib/active_record/base.rb (modified) |
|
| 43 |
* trunk/activerecord/test/base_test.rb (modified) |
|
| 44 |
Added :count option to pagination that'll make it possible for the |
|
| 45 |
ActiveRecord::Base.count call to using something else than * for the |
|
| 46 |
count. Especially important for count queries using DISTINCT #3839 |
|
| 47 |
[skaes]. Added :select option to Base.count that'll allow you to |
|
| 48 |
select something else than * to be counted on. Especially important |
|
| 49 |
for count queries using DISTINCT (closes #3839) [skaes]. |
|
| 50 |
|
|
| 51 |
02/09/06 09:17:40 nzkoz [3553] |
|
| 52 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 53 |
* trunk/actionpack/test/active_record_unit.rb (added) |
|
| 54 |
* trunk/actionpack/test/activerecord (added) |
|
| 55 |
* trunk/actionpack/test/activerecord/active_record_assertions_test.rb (added) |
|
| 56 |
* trunk/actionpack/test/activerecord/pagination_test.rb (added) |
|
| 57 |
* trunk/actionpack/test/controller/active_record_assertions_test.rb (deleted) |
|
| 58 |
* trunk/actionpack/test/fixtures/companies.yml (added) |
|
| 59 |
* trunk/actionpack/test/fixtures/company.rb (added) |
|
| 60 |
* trunk/actionpack/test/fixtures/db_definitions (added) |
|
| 61 |
* trunk/actionpack/test/fixtures/db_definitions/sqlite.sql (added) |
|
| 62 |
* trunk/actionpack/test/fixtures/developer.rb (added) |
|
| 63 |
* trunk/actionpack/test/fixtures/developers_projects.yml (added) |
|
| 64 |
* trunk/actionpack/test/fixtures/developers.yml (added) |
|
| 65 |
* trunk/actionpack/test/fixtures/project.rb (added) |
|
| 66 |
* trunk/actionpack/test/fixtures/projects.yml (added) |
|
| 67 |
* trunk/actionpack/test/fixtures/replies.yml (added) |
|
| 68 |
* trunk/actionpack/test/fixtures/reply.rb (added) |
|
| 69 |
* trunk/actionpack/test/fixtures/topic.rb (added) |
|
| 70 |
* trunk/actionpack/test/fixtures/topics.yml (added) |
|
| 71 |
* Fix pagination problems when using include |
|
| 72 |
* Introduce Unit Tests for pagination |
|
| 73 |
* Allow count to work with :include by using count distinct. |
|
| 74 |
|
|
| 75 |
[Kevin Clark & Jeremy Hopple] |
|
| 76 |
|
|
| 77 |
11/05/05 02:10:29 bitsweat [2878] |
|
| 78 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 79 |
Update paginator docs. Closes #2744. |
|
| 80 |
|
|
| 81 |
10/16/05 15:42:03 minam [2649] |
|
| 82 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 83 |
Update/clean up AP documentation (rdoc) |
|
| 84 |
|
|
| 85 |
08/31/05 00:13:10 ulysses [2078] |
|
| 86 |
* trunk/actionpack/CHANGELOG (modified) |
|
| 87 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 88 |
Add option to specify the singular name used by pagination. Closes |
|
| 89 |
#1960 |
|
| 90 |
|
|
| 91 |
08/23/05 14:24:15 minam [2041] |
|
| 92 |
* trunk/actionpack/CHANGELOG (modified) |
|
| 93 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 94 |
Add support for :include with pagination (subject to existing |
|
| 95 |
constraints for :include with :limit and :offset) #1478 |
|
| 96 |
[michael@schubert.cx] |
|
| 97 |
|
|
| 98 |
07/15/05 20:27:38 david [1839] |
|
| 99 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 100 |
* trunk/actionpack/lib/action_view/helpers/pagination_helper.rb (modified) |
|
| 101 |
More pagination speed #1334 [Stefan Kaes] |
|
| 102 |
|
|
| 103 |
07/14/05 08:02:01 david [1832] |
|
| 104 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 105 |
* trunk/actionpack/lib/action_view/helpers/pagination_helper.rb (modified) |
|
| 106 |
* trunk/actionpack/test/controller/addresses_render_test.rb (modified) |
|
| 107 |
Made pagination faster #1334 [Stefan Kaes] |
|
| 108 |
|
|
| 109 |
04/13/05 05:40:22 david [1159] |
|
| 110 |
* trunk/actionpack/CHANGELOG (modified) |
|
| 111 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 112 |
* trunk/activerecord/lib/active_record/base.rb (modified) |
|
| 113 |
Fixed pagination to work with joins #1034 [scott@sigkill.org] |
|
| 114 |
|
|
| 115 |
04/02/05 09:11:17 david [1067] |
|
| 116 |
* trunk/actionpack/CHANGELOG (modified) |
|
| 117 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 118 |
* trunk/actionpack/lib/action_controller/scaffolding.rb (modified) |
|
| 119 |
* trunk/actionpack/lib/action_controller/templates/scaffolds/list.rhtml (modified) |
|
| 120 |
* trunk/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb (modified) |
|
| 121 |
* trunk/railties/lib/rails_generator/generators/components/scaffold/templates/view_list.rhtml (modified) |
|
| 122 |
Added pagination for scaffolding (10 items per page) #964 |
|
| 123 |
[mortonda@dgrmm.net] |
|
| 124 |
|
|
| 125 |
03/31/05 14:46:11 david [1048] |
|
| 126 |
* trunk/actionpack/lib/action_view/helpers/pagination_helper.rb (modified) |
|
| 127 |
Improved the message display on the exception handler pages #963 |
|
| 128 |
[Johan Sorensen] |
|
| 129 |
|
|
| 130 |
03/27/05 00:04:07 david [1017] |
|
| 131 |
* trunk/actionpack/CHANGELOG (modified) |
|
| 132 |
* trunk/actionpack/lib/action_view/helpers/pagination_helper.rb (modified) |
|
| 133 |
Fixed that pagination_helper would ignore :params #947 [Sebastian |
|
| 134 |
Kanthak] |
|
| 135 |
|
|
| 136 |
03/22/05 13:09:44 david [976] |
|
| 137 |
* trunk/actionpack/lib/action_view/helpers/pagination_helper.rb (modified) |
|
| 138 |
Fixed documentation and prepared for 0.11.0 release |
|
| 139 |
|
|
| 140 |
03/21/05 14:35:36 david [967] |
|
| 141 |
* trunk/actionpack/lib/action_controller/pagination.rb (modified) |
|
| 142 |
* trunk/actionpack/lib/action_view/helpers/pagination_helper.rb (modified) |
|
| 143 |
Tweaked the documentation |
|
| 144 |
|
|
| 145 |
03/20/05 23:12:05 david [949] |
|
| 146 |
* trunk/actionpack/CHANGELOG (modified) |
|
| 147 |
* trunk/actionpack/lib/action_controller.rb (modified) |
|
| 148 |
* trunk/actionpack/lib/action_controller/pagination.rb (added) |
|
| 149 |
* trunk/actionpack/lib/action_view/helpers/pagination_helper.rb (added) |
|
| 150 |
* trunk/activesupport/lib/active_support/core_ext/kernel.rb (added) |
|
| 151 |
Added pagination support through both a controller and helper add-on |
|
| 152 |
#817 [Sam Stephenson] |
|
| lib/plugins/classic_pagination/README | ||
|---|---|---|
| 1 |
Pagination |
|
| 2 |
========== |
|
| 3 |
|
|
| 4 |
To install: |
|
| 5 |
|
|
| 6 |
script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination |
|
| 7 |
|
|
| 8 |
This code was extracted from Rails trunk after the release 1.2.3. |
|
| 9 |
WARNING: this code is dead. It is unmaintained, untested and full of cruft. |
|
| 10 |
|
|
| 11 |
There is a much better pagination plugin called will_paginate. |
|
| 12 |
Install it like this and glance through the README: |
|
| 13 |
|
|
| 14 |
script/plugin install svn://errtheblog.com/svn/plugins/will_paginate |
|
| 15 |
|
|
| 16 |
It doesn't have the same API, but is in fact much nicer. You can |
|
| 17 |
have both plugins installed until you change your controller/view code that |
|
| 18 |
handles pagination. Then, simply uninstall classic_pagination. |
|
| lib/plugins/classic_pagination/Rakefile | ||
|---|---|---|
| 1 |
require 'rake' |
|
| 2 |
require 'rake/testtask' |
|
| 3 |
require 'rake/rdoctask' |
|
| 4 |
|
|
| 5 |
desc 'Default: run unit tests.' |
|
| 6 |
task :default => :test |
|
| 7 |
|
|
| 8 |
desc 'Test the classic_pagination plugin.' |
|
| 9 |
Rake::TestTask.new(:test) do |t| |
|
| 10 |
t.libs << 'lib' |
|
| 11 |
t.pattern = 'test/**/*_test.rb' |
|
| 12 |
t.verbose = true |
|
| 13 |
end |
|
| 14 |
|
|
| 15 |
desc 'Generate documentation for the classic_pagination plugin.' |
|
| 16 |
Rake::RDocTask.new(:rdoc) do |rdoc| |
|
| 17 |
rdoc.rdoc_dir = 'rdoc' |
|
| 18 |
rdoc.title = 'Pagination' |
|
| 19 |
rdoc.options << '--line-numbers' << '--inline-source' |
|
| 20 |
rdoc.rdoc_files.include('README')
|
|
| 21 |
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
| 22 |
end |
|
| lib/plugins/classic_pagination/init.rb | ||
|---|---|---|
| 1 |
#-- |
|
| 2 |
# Copyright (c) 2004-2006 David Heinemeier Hansson |
|
| 3 |
# |
|
| 4 |
# Permission is hereby granted, free of charge, to any person obtaining |
|
| 5 |
# a copy of this software and associated documentation files (the |
|
| 6 |
# "Software"), to deal in the Software without restriction, including |
|
| 7 |
# without limitation the rights to use, copy, modify, merge, publish, |
|
| 8 |
# distribute, sublicense, and/or sell copies of the Software, and to |
|
| 9 |
# permit persons to whom the Software is furnished to do so, subject to |
|
| 10 |
# the following conditions: |
|
| 11 |
# |
|
| 12 |
# The above copyright notice and this permission notice shall be |
|
| 13 |
# included in all copies or substantial portions of the Software. |
|
| 14 |
# |
|
| 15 |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
| 16 |
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
| 17 |
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
| 18 |
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
| 19 |
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
| 20 |
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
| 21 |
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
| 22 |
#++ |
|
| 23 |
|
|
| 24 |
require 'pagination' |
|
| 25 |
require 'pagination_helper' |
|
| 26 |
|
|
| 27 |
ActionController::Base.class_eval do |
|
| 28 |
include ActionController::Pagination |
|
| 29 |
end |
|
| 30 |
|
|
| 31 |
ActionView::Base.class_eval do |
|
| 32 |
include ActionView::Helpers::PaginationHelper |
|
| 33 |
end |
|
| lib/plugins/classic_pagination/install.rb | ||
|---|---|---|
| 1 |
puts "\n\n" + File.read(File.dirname(__FILE__) + '/README') |
|
| lib/plugins/classic_pagination/lib/pagination.rb | ||
|---|---|---|
| 1 |
module ActionController |
|
| 2 |
# === Action Pack pagination for Active Record collections |
|
| 3 |
# |
|
| 4 |
# The Pagination module aids in the process of paging large collections of |
|
| 5 |
# Active Record objects. It offers macro-style automatic fetching of your |
|
| 6 |
# model for multiple views, or explicit fetching for single actions. And if |
|
| 7 |
# the magic isn't flexible enough for your needs, you can create your own |
|
| 8 |
# paginators with a minimal amount of code. |
|
| 9 |
# |
|
| 10 |
# The Pagination module can handle as much or as little as you wish. In the |
|
| 11 |
# controller, have it automatically query your model for pagination; or, |
|
| 12 |
# if you prefer, create Paginator objects yourself. |
|
| 13 |
# |
|
| 14 |
# Pagination is included automatically for all controllers. |
|
| 15 |
# |
|
| 16 |
# For help rendering pagination links, see |
|
| 17 |
# ActionView::Helpers::PaginationHelper. |
|
| 18 |
# |
|
| 19 |
# ==== Automatic pagination for every action in a controller |
|
| 20 |
# |
|
| 21 |
# class PersonController < ApplicationController |
|
| 22 |
# model :person |
|
| 23 |
# |
|
| 24 |
# paginate :people, :order => 'last_name, first_name', |
|
| 25 |
# :per_page => 20 |
|
| 26 |
# |
|
| 27 |
# # ... |
|
| 28 |
# end |
|
| 29 |
# |
|
| 30 |
# Each action in this controller now has access to a <tt>@people</tt> |
|
| 31 |
# instance variable, which is an ordered collection of model objects for the |
|
| 32 |
# current page (at most 20, sorted by last name and first name), and a |
|
| 33 |
# <tt>@person_pages</tt> Paginator instance. The current page is determined |
|
| 34 |
# by the <tt>params[:page]</tt> variable. |
|
| 35 |
# |
|
| 36 |
# ==== Pagination for a single action |
|
| 37 |
# |
|
| 38 |
# def list |
|
| 39 |
# @person_pages, @people = |
|
| 40 |
# paginate :people, :order => 'last_name, first_name' |
|
| 41 |
# end |
|
| 42 |
# |
|
| 43 |
# Like the previous example, but explicitly creates <tt>@person_pages</tt> |
|
| 44 |
# and <tt>@people</tt> for a single action, and uses the default of 10 items |
|
| 45 |
# per page. |
|
| 46 |
# |
|
| 47 |
# ==== Custom/"classic" pagination |
|
| 48 |
# |
|
| 49 |
# def list |
|
| 50 |
# @person_pages = Paginator.new self, Person.count, 10, params[:page] |
|
| 51 |
# @people = Person.find :all, :order => 'last_name, first_name', |
|
| 52 |
# :limit => @person_pages.items_per_page, |
|
| 53 |
# :offset => @person_pages.current.offset |
|
| 54 |
# end |
|
| 55 |
# |
|
| 56 |
# Explicitly creates the paginator from the previous example and uses |
|
| 57 |
# Paginator#to_sql to retrieve <tt>@people</tt> from the model. |
|
| 58 |
# |
|
| 59 |
module Pagination |
|
| 60 |
unless const_defined?(:OPTIONS) |
|
| 61 |
# A hash holding options for controllers using macro-style pagination |
|
| 62 |
OPTIONS = Hash.new |
|
| 63 |
|
|
| 64 |
# The default options for pagination |
|
| 65 |
DEFAULT_OPTIONS = {
|
|
| 66 |
:class_name => nil, |
|
| 67 |
:singular_name => nil, |
|
| 68 |
:per_page => 10, |
|
| 69 |
:conditions => nil, |
|
| 70 |
:order_by => nil, |
|
| 71 |
:order => nil, |
|
| 72 |
:join => nil, |
|
| 73 |
:joins => nil, |
|
| 74 |
:count => nil, |
|
| 75 |
:include => nil, |
|
| 76 |
:select => nil, |
|
| 77 |
:group => nil, |
|
| 78 |
:parameter => 'page' |
|
| 79 |
} |
|
| 80 |
else |
|
| 81 |
DEFAULT_OPTIONS[:group] = nil |
|
| 82 |
end |
|
| 83 |
|
|
| 84 |
def self.included(base) #:nodoc: |
|
| 85 |
super |
|
| 86 |
base.extend(ClassMethods) |
|
| 87 |
end |
|
| 88 |
|
|
| 89 |
def self.validate_options!(collection_id, options, in_action) #:nodoc: |
|
| 90 |
options.merge!(DEFAULT_OPTIONS) {|key, old, new| old}
|
|
| 91 |
|
|
| 92 |
valid_options = DEFAULT_OPTIONS.keys |
|
| 93 |
valid_options << :actions unless in_action |
|
| 94 |
|
|
| 95 |
unknown_option_keys = options.keys - valid_options |
|
| 96 |
raise ActionController::ActionControllerError, |
|
| 97 |
"Unknown options: #{unknown_option_keys.join(', ')}" unless
|
|
| 98 |
unknown_option_keys.empty? |
|
| 99 |
|
|
| 100 |
options[:singular_name] ||= ActiveSupport::Inflector.singularize(collection_id.to_s) |
|
| 101 |
options[:class_name] ||= ActiveSupport::Inflector.camelize(options[:singular_name]) |
|
| 102 |
end |
|
| 103 |
|
|
| 104 |
# Returns a paginator and a collection of Active Record model instances |
|
| 105 |
# for the paginator's current page. This is designed to be used in a |
|
| 106 |
# single action; to automatically paginate multiple actions, consider |
|
| 107 |
# ClassMethods#paginate. |
|
| 108 |
# |
|
| 109 |
# +options+ are: |
|
| 110 |
# <tt>:singular_name</tt>:: the singular name to use, if it can't be inferred by singularizing the collection name |
|
| 111 |
# <tt>:class_name</tt>:: the class name to use, if it can't be inferred by |
|
| 112 |
# camelizing the singular name |
|
| 113 |
# <tt>:per_page</tt>:: the maximum number of items to include in a |
|
| 114 |
# single page. Defaults to 10 |
|
| 115 |
# <tt>:conditions</tt>:: optional conditions passed to Model.find(:all, *params) and |
|
| 116 |
# Model.count |
|
| 117 |
# <tt>:order</tt>:: optional order parameter passed to Model.find(:all, *params) |
|
| 118 |
# <tt>:order_by</tt>:: (deprecated, used :order) optional order parameter passed to Model.find(:all, *params) |
|
| 119 |
# <tt>:joins</tt>:: optional joins parameter passed to Model.find(:all, *params) |
|
| 120 |
# and Model.count |
|
| 121 |
# <tt>:join</tt>:: (deprecated, used :joins or :include) optional join parameter passed to Model.find(:all, *params) |
|
| 122 |
# and Model.count |
|
| 123 |
# <tt>:include</tt>:: optional eager loading parameter passed to Model.find(:all, *params) |
|
| 124 |
# and Model.count |
|
| 125 |
# <tt>:select</tt>:: :select parameter passed to Model.find(:all, *params) |
|
| 126 |
# |
|
| 127 |
# <tt>:count</tt>:: parameter passed as :select option to Model.count(*params) |
|
| 128 |
# |
|
| 129 |
# <tt>:group</tt>:: :group parameter passed to Model.find(:all, *params). It forces the use of DISTINCT instead of plain COUNT to come up with the total number of records |
|
| 130 |
# |
|
| 131 |
def paginate(collection_id, options={})
|
|
| 132 |
Pagination.validate_options!(collection_id, options, true) |
|
| 133 |
paginator_and_collection_for(collection_id, options) |
|
| 134 |
end |
|
| 135 |
|
|
| 136 |
# These methods become class methods on any controller |
|
| 137 |
module ClassMethods |
|
| 138 |
# Creates a +before_filter+ which automatically paginates an Active |
|
| 139 |
# Record model for all actions in a controller (or certain actions if |
|
| 140 |
# specified with the <tt>:actions</tt> option). |
|
| 141 |
# |
|
| 142 |
# +options+ are the same as PaginationHelper#paginate, with the addition |
|
| 143 |
# of: |
|
| 144 |
# <tt>:actions</tt>:: an array of actions for which the pagination is |
|
| 145 |
# active. Defaults to +nil+ (i.e., every action) |
|
| 146 |
def paginate(collection_id, options={})
|
|
| 147 |
Pagination.validate_options!(collection_id, options, false) |
|
| 148 |
module_eval do |
|
| 149 |
before_filter :create_paginators_and_retrieve_collections |
|
| 150 |
OPTIONS[self] ||= Hash.new |
|
| 151 |
OPTIONS[self][collection_id] = options |
|
| 152 |
end |
|
| 153 |
end |
|
| 154 |
end |
|
| 155 |
|
|
| 156 |
def create_paginators_and_retrieve_collections #:nodoc: |
|
| 157 |
Pagination::OPTIONS[self.class].each do |collection_id, options| |
|
| 158 |
next unless options[:actions].include? action_name if |
|
| 159 |
options[:actions] |
|
| 160 |
|
|
| 161 |
paginator, collection = |
|
| 162 |
paginator_and_collection_for(collection_id, options) |
|
| 163 |
|
|
| 164 |
paginator_name = "@#{options[:singular_name]}_pages"
|
|
| 165 |
self.instance_variable_set(paginator_name, paginator) |
|
| 166 |
|
|
| 167 |
collection_name = "@#{collection_id.to_s}"
|
|
| 168 |
self.instance_variable_set(collection_name, collection) |
|
| 169 |
end |
|
| 170 |
end |
|
| 171 |
|
|
| 172 |
# Returns the total number of items in the collection to be paginated for |
|
| 173 |
# the +model+ and given +conditions+. Override this method to implement a |
|
| 174 |
# custom counter. |
|
| 175 |
def count_collection_for_pagination(model, options) |
|
| 176 |
model.count(:conditions => options[:conditions], |
|
| 177 |
:joins => options[:join] || options[:joins], |
|
| 178 |
:include => options[:include], |
|
| 179 |
:select => (options[:group] ? "DISTINCT #{options[:group]}" : options[:count]))
|
|
| 180 |
end |
|
| 181 |
|
|
| 182 |
# Returns a collection of items for the given +model+ and +options[conditions]+, |
|
| 183 |
# ordered by +options[order]+, for the current page in the given +paginator+. |
|
| 184 |
# Override this method to implement a custom finder. |
|
| 185 |
def find_collection_for_pagination(model, options, paginator) |
|
| 186 |
model.find(:all, :conditions => options[:conditions], |
|
| 187 |
:order => options[:order_by] || options[:order], |
|
| 188 |
:joins => options[:join] || options[:joins], :include => options[:include], |
|
| 189 |
:select => options[:select], :limit => options[:per_page], |
|
| 190 |
:group => options[:group], :offset => paginator.current.offset) |
|
| 191 |
end |
|
| 192 |
|
|
| 193 |
protected :create_paginators_and_retrieve_collections, |
|
| 194 |
:count_collection_for_pagination, |
|
| 195 |
:find_collection_for_pagination |
|
| 196 |
|
|
| 197 |
def paginator_and_collection_for(collection_id, options) #:nodoc: |
|
| 198 |
klass = options[:class_name].constantize |
|
| 199 |
page = params[options[:parameter]] |
|
| 200 |
count = count_collection_for_pagination(klass, options) |
|
| 201 |
paginator = Paginator.new(self, count, options[:per_page], page) |
|
| 202 |
collection = find_collection_for_pagination(klass, options, paginator) |
|
| 203 |
|
|
| 204 |
return paginator, collection |
|
| 205 |
end |
|
| 206 |
|
|
| 207 |
private :paginator_and_collection_for |
|
| 208 |
|
|
| 209 |
# A class representing a paginator for an Active Record collection. |
|
| 210 |
class Paginator |
|
| 211 |
include Enumerable |
|
| 212 |
|
|
| 213 |
# Creates a new Paginator on the given +controller+ for a set of items |
|
| 214 |
# of size +item_count+ and having +items_per_page+ items per page. |
|
| 215 |
# Raises ArgumentError if items_per_page is out of bounds (i.e., less |
|
| 216 |
# than or equal to zero). The page CGI parameter for links defaults to |
|
| 217 |
# "page" and can be overridden with +page_parameter+. |
|
| 218 |
def initialize(controller, item_count, items_per_page, current_page=1) |
|
| 219 |
raise ArgumentError, 'must have at least one item per page' if |
|
| 220 |
items_per_page <= 0 |
|
| 221 |
|
|
| 222 |
@controller = controller |
|
| 223 |
@item_count = item_count || 0 |
|
| 224 |
@items_per_page = items_per_page |
|
| 225 |
@pages = {}
|
|
| 226 |
|
|
| 227 |
self.current_page = current_page |
|
| 228 |
end |
|
| 229 |
attr_reader :controller, :item_count, :items_per_page |
|
| 230 |
|
|
| 231 |
# Sets the current page number of this paginator. If +page+ is a Page |
|
| 232 |
# object, its +number+ attribute is used as the value; if the page does |
|
| 233 |
# not belong to this Paginator, an ArgumentError is raised. |
|
| 234 |
def current_page=(page) |
|
| 235 |
if page.is_a? Page |
|
| 236 |
raise ArgumentError, 'Page/Paginator mismatch' unless |
|
| 237 |
page.paginator == self |
|
| 238 |
end |
|
| 239 |
page = page.to_i |
|
| 240 |
@current_page_number = has_page_number?(page) ? page : 1 |
|
| 241 |
end |
|
| 242 |
|
|
| 243 |
# Returns a Page object representing this paginator's current page. |
|
| 244 |
def current_page |
|
| 245 |
@current_page ||= self[@current_page_number] |
|
| 246 |
end |
|
| 247 |
alias current :current_page |
|
| 248 |
|
|
| 249 |
# Returns a new Page representing the first page in this paginator. |
|
| 250 |
def first_page |
|
| 251 |
@first_page ||= self[1] |
|
| 252 |
end |
|
| 253 |
alias first :first_page |
|
| 254 |
|
|
| 255 |
# Returns a new Page representing the last page in this paginator. |
|
| 256 |
def last_page |
|
| 257 |
@last_page ||= self[page_count] |
|
| 258 |
end |
|
| 259 |
alias last :last_page |
|
| 260 |
|
|
| 261 |
# Returns the number of pages in this paginator. |
|
| 262 |
def page_count |
|
| 263 |
@page_count ||= @item_count.zero? ? 1 : |
|
| 264 |
(q,r=@item_count.divmod(@items_per_page); r==0? q : q+1) |
|
| 265 |
end |
|
| 266 |
|
|
| 267 |
alias length :page_count |
|
| 268 |
|
|
| 269 |
# Returns true if this paginator contains the page of index +number+. |
|
| 270 |
def has_page_number?(number) |
|
| 271 |
number >= 1 and number <= page_count |
|
| 272 |
end |
|
| 273 |
|
|
| 274 |
# Returns a new Page representing the page with the given index |
|
| 275 |
# +number+. |
|
| 276 |
def [](number) |
|
| 277 |
@pages[number] ||= Page.new(self, number) |
|
| 278 |
end |
|
| 279 |
|
|
| 280 |
# Successively yields all the paginator's pages to the given block. |
|
| 281 |
def each(&block) |
|
| 282 |
page_count.times do |n| |
|
| 283 |
yield self[n+1] |
|
| 284 |
end |
|
| 285 |
end |
|
| 286 |
|
|
| 287 |
# A class representing a single page in a paginator. |
|
| 288 |
class Page |
|
| 289 |
include Comparable |
|
| 290 |
|
|
| 291 |
# Creates a new Page for the given +paginator+ with the index |
|
| 292 |
# +number+. If +number+ is not in the range of valid page numbers or |
|
| 293 |
# is not a number at all, it defaults to 1. |
|
| 294 |
def initialize(paginator, number) |
|
| 295 |
@paginator = paginator |
|
| 296 |
@number = number.to_i |
|
| 297 |
@number = 1 unless @paginator.has_page_number? @number |
|
| 298 |
end |
|
| 299 |
attr_reader :paginator, :number |
|
| 300 |
alias to_i :number |
|
| 301 |
|
|
| 302 |
# Compares two Page objects and returns true when they represent the |
|
| 303 |
# same page (i.e., their paginators are the same and they have the |
|
| 304 |
# same page number). |
|
| 305 |
def ==(page) |
|
| 306 |
return false if page.nil? |
|
| 307 |
@paginator == page.paginator and |
|
| 308 |
@number == page.number |
|
| 309 |
end |
|
| 310 |
|
|
| 311 |
# Compares two Page objects and returns -1 if the left-hand page comes |
|
| 312 |
# before the right-hand page, 0 if the pages are equal, and 1 if the |
|
| 313 |
# left-hand page comes after the right-hand page. Raises ArgumentError |
|
| 314 |
# if the pages do not belong to the same Paginator object. |
|
| 315 |
def <=>(page) |
|
| 316 |
raise ArgumentError unless @paginator == page.paginator |
|
| 317 |
@number <=> page.number |
|
| 318 |
end |
|
| 319 |
|
|
| 320 |
# Returns the item offset for the first item in this page. |
|
| 321 |
def offset |
|
| 322 |
@paginator.items_per_page * (@number - 1) |
|
| 323 |
end |
|
| 324 |
|
|
| 325 |
# Returns the number of the first item displayed. |
|
| 326 |
def first_item |
|
| 327 |
offset + 1 |
|
| 328 |
end |
|
| 329 |
|
|
| 330 |
# Returns the number of the last item displayed. |
|
| 331 |
def last_item |
|
| 332 |
[@paginator.items_per_page * @number, @paginator.item_count].min |
|
| 333 |
end |
|
| 334 |
|
|
| 335 |
# Returns true if this page is the first page in the paginator. |
|
| 336 |
def first? |
|
| 337 |
self == @paginator.first |
|
| 338 |
end |
|
| 339 |
|
|
| 340 |
# Returns true if this page is the last page in the paginator. |
|
| 341 |
def last? |
|
| 342 |
self == @paginator.last |
|
| 343 |
end |
|
| 344 |
|
|
| 345 |
# Returns a new Page object representing the page just before this |
|
| 346 |
# page, or nil if this is the first page. |
|
| 347 |
def previous |
|
| 348 |
if first? then nil else @paginator[@number - 1] end |
|
| 349 |
end |
|
| 350 |
|
|
| 351 |
# Returns a new Page object representing the page just after this |
|
| 352 |
# page, or nil if this is the last page. |
|
| 353 |
def next |
|
| 354 |
if last? then nil else @paginator[@number + 1] end |
|
| 355 |
end |
|
| 356 |
|
|
| 357 |
# Returns a new Window object for this page with the specified |
|
| 358 |
# +padding+. |
|
| 359 |
def window(padding=2) |
|
| 360 |
Window.new(self, padding) |
|
| 361 |
end |
|
| 362 |
|
|
| 363 |
# Returns the limit/offset array for this page. |
|
| 364 |
def to_sql |
|
| 365 |
[@paginator.items_per_page, offset] |
|
| 366 |
end |
|
| 367 |
|
|
| 368 |
def to_param #:nodoc: |
|
| 369 |
@number.to_s |
|
| 370 |
end |
|
| 371 |
end |
|
| 372 |
|
|
| 373 |
# A class for representing ranges around a given page. |
|
| 374 |
class Window |
|
| 375 |
# Creates a new Window object for the given +page+ with the specified |
|
| 376 |
# +padding+. |
|
| 377 |
def initialize(page, padding=2) |
|
| 378 |
@paginator = page.paginator |
|
| 379 |
@page = page |
|
| 380 |
self.padding = padding |
|
| 381 |
end |
|
| 382 |
attr_reader :paginator, :page |
|
| 383 |
|
|
| 384 |
# Sets the window's padding (the number of pages on either side of the |
|
| 385 |
# window page). |
|
| 386 |
def padding=(padding) |
|
| 387 |
@padding = padding < 0 ? 0 : padding |
|
| 388 |
# Find the beginning and end pages of the window |
|
| 389 |
@first = @paginator.has_page_number?(@page.number - @padding) ? |
|
| 390 |
@paginator[@page.number - @padding] : @paginator.first |
|
| 391 |
@last = @paginator.has_page_number?(@page.number + @padding) ? |
|
| 392 |
@paginator[@page.number + @padding] : @paginator.last |
|
| 393 |
end |
|
| 394 |
attr_reader :padding, :first, :last |
|
| 395 |
|
|
| 396 |
# Returns an array of Page objects in the current window. |
|
| 397 |
def pages |
|
| 398 |
(@first.number..@last.number).to_a.collect! {|n| @paginator[n]}
|
|
| 399 |
end |
|
| 400 |
alias to_a :pages |
|
| 401 |
end |
|
| 402 |
end |
|
| 403 |
|
|
| 404 |
end |
|
| 405 |
end |
|
| lib/plugins/classic_pagination/lib/pagination_helper.rb | ||
|---|---|---|
| 1 |
module ActionView |
|
| 2 |
module Helpers |
|
| 3 |
# Provides methods for linking to ActionController::Pagination objects using a simple generator API. You can optionally |
|
| 4 |
# also build your links manually using ActionView::Helpers::AssetHelper#link_to like so: |
|
| 5 |
# |
|
| 6 |
# <%= link_to "Previous page", { :page => paginator.current.previous } if paginator.current.previous %>
|
|
| 7 |
# <%= link_to "Next page", { :page => paginator.current.next } if paginator.current.next %>
|
|
| 8 |
module PaginationHelper |
|
| 9 |
unless const_defined?(:DEFAULT_OPTIONS) |
|
| 10 |
DEFAULT_OPTIONS = {
|
|
| 11 |
:name => :page, |
|
| 12 |
:window_size => 2, |
|
| 13 |
:always_show_anchors => true, |
|
| 14 |
:link_to_current_page => false, |
|
| 15 |
:params => {}
|
|
| 16 |
} |
|
| 17 |
end |
|
| 18 |
|
|
| 19 |
# Creates a basic HTML link bar for the given +paginator+. Links will be created |
|
| 20 |
# for the next and/or previous page and for a number of other pages around the current |
|
| 21 |
# pages position. The +html_options+ hash is passed to +link_to+ when the links are created. |
|
| 22 |
# |
|
| 23 |
# ==== Options |
|
| 24 |
# <tt>:name</tt>:: the routing name for this paginator |
|
| 25 |
# (defaults to +page+) |
|
| 26 |
# <tt>:prefix</tt>:: prefix for pagination links |
|
| 27 |
# (i.e. Older Pages: 1 2 3 4) |
|
| 28 |
# <tt>:suffix</tt>:: suffix for pagination links |
|
| 29 |
# (i.e. 1 2 3 4 <- Older Pages) |
|
| 30 |
# <tt>:window_size</tt>:: the number of pages to show around |
|
| 31 |
# the current page (defaults to <tt>2</tt>) |
|
| 32 |
# <tt>:always_show_anchors</tt>:: whether or not the first and last |
|
| 33 |
# pages should always be shown |
|
| 34 |
# (defaults to +true+) |
|
| 35 |
# <tt>:link_to_current_page</tt>:: whether or not the current page |
|
| 36 |
# should be linked to (defaults to |
|
| 37 |
# +false+) |
|
| 38 |
# <tt>:params</tt>:: any additional routing parameters |
|
| 39 |
# for page URLs |
|
| 40 |
# |
|
| 41 |
# ==== Examples |
|
| 42 |
# # We'll assume we have a paginator setup in @person_pages... |
|
| 43 |
# |
|
| 44 |
# pagination_links(@person_pages) |
|
| 45 |
# # => 1 <a href="/?page=2/">2</a> <a href="/?page=3/">3</a> ... <a href="/?page=10/">10</a> |
|
| 46 |
# |
|
| 47 |
# pagination_links(@person_pages, :link_to_current_page => true) |
|
| 48 |
# # => <a href="/?page=1/">1</a> <a href="/?page=2/">2</a> <a href="/?page=3/">3</a> ... <a href="/?page=10/">10</a> |
|
| 49 |
# |
|
| 50 |
# pagination_links(@person_pages, :always_show_anchors => false) |
|
| 51 |
# # => 1 <a href="/?page=2/">2</a> <a href="/?page=3/">3</a> |
|
| 52 |
# |
|
| 53 |
# pagination_links(@person_pages, :window_size => 1) |
|
| 54 |
# # => 1 <a href="/?page=2/">2</a> ... <a href="/?page=10/">10</a> |
|
| 55 |
# |
|
| 56 |
# pagination_links(@person_pages, :params => { :viewer => "flash" })
|
|
| 57 |
# # => 1 <a href="/?page=2&viewer=flash/">2</a> <a href="/?page=3&viewer=flash/">3</a> ... |
|
| 58 |
# # <a href="/?page=10&viewer=flash/">10</a> |
|
| 59 |
def pagination_links(paginator, options={}, html_options={})
|
|
| 60 |
name = options[:name] || DEFAULT_OPTIONS[:name] |
|
| 61 |
params = (options[:params] || DEFAULT_OPTIONS[:params]).clone |
|
| 62 |
|
|
| 63 |
prefix = options[:prefix] || '' |
|
| 64 |
suffix = options[:suffix] || '' |
|
| 65 |
|
|
| 66 |
pagination_links_each(paginator, options, prefix, suffix) do |n| |
|
| 67 |
params[name] = n |
|
| 68 |
link_to(n.to_s, params, html_options) |
|
| 69 |
end |
|
| 70 |
end |
|
| 71 |
|
|
| 72 |
# Iterate through the pages of a given +paginator+, invoking a |
|
| 73 |
# block for each page number that needs to be rendered as a link. |
|
| 74 |
# |
|
| 75 |
# ==== Options |
|
| 76 |
# <tt>:window_size</tt>:: the number of pages to show around |
|
| 77 |
# the current page (defaults to +2+) |
|
| 78 |
# <tt>:always_show_anchors</tt>:: whether or not the first and last |
|
| 79 |
# pages should always be shown |
|
| 80 |
# (defaults to +true+) |
|
| 81 |
# <tt>:link_to_current_page</tt>:: whether or not the current page |
|
| 82 |
# should be linked to (defaults to |
|
| 83 |
# +false+) |
|
| 84 |
# |
|
| 85 |
# ==== Example |
|
| 86 |
# # Turn paginated links into an Ajax call |
|
| 87 |
# pagination_links_each(paginator, page_options) do |link| |
|
| 88 |
# options = { :url => {:action => 'list'}, :update => 'results' }
|
|
| 89 |
# html_options = { :href => url_for(:action => 'list') }
|
|
| 90 |
# |
|
| 91 |
# link_to_remote(link.to_s, options, html_options) |
|
| 92 |
# end |
|
| 93 |
def pagination_links_each(paginator, options, prefix = nil, suffix = nil) |
|
| 94 |
options = DEFAULT_OPTIONS.merge(options) |
|
| 95 |
link_to_current_page = options[:link_to_current_page] |
|
| 96 |
always_show_anchors = options[:always_show_anchors] |
|
| 97 |
|
|
| 98 |
current_page = paginator.current_page |
|
| 99 |
window_pages = current_page.window(options[:window_size]).pages |
|
| 100 |
return if window_pages.length <= 1 unless link_to_current_page |
|
| 101 |
|
|
| 102 |
first, last = paginator.first, paginator.last |
|
| 103 |
|
|
| 104 |
html = '' |
|
| 105 |
|
|
| 106 |
html << prefix if prefix |
|
| 107 |
|
|
| 108 |
if always_show_anchors and not (wp_first = window_pages[0]).first? |
|
| 109 |
html << yield(first.number) |
|
| 110 |
html << ' ... ' if wp_first.number - first.number > 1 |
|
| 111 |
html << ' ' |
|
| 112 |
end |
|
| 113 |
|
|
| 114 |
window_pages.each do |page| |
|
| 115 |
if current_page == page && !link_to_current_page |
|
| 116 |
html << page.number.to_s |
|
| 117 |
else |
|
| 118 |
html << yield(page.number) |
|
| 119 |
end |
|
| 120 |
html << ' ' |
|
| 121 |
end |
|
| 122 |
|
|
| 123 |
if always_show_anchors and not (wp_last = window_pages[-1]).last? |
|
| 124 |
html << ' ... ' if last.number - wp_last.number > 1 |
|
| 125 |
html << yield(last.number) |
|
| 126 |
end |
|
| 127 |
|
|
| 128 |
html << suffix if suffix |
|
| 129 |
|
|
| 130 |
html |
|
| 131 |
end |
|
| 132 |
|
|
| 133 |
end # PaginationHelper |
|
| 134 |
end # Helpers |
|
| 135 |
end # ActionView |
|
| lib/plugins/classic_pagination/test/fixtures/companies.yml | ||
|---|---|---|
| 1 |
thirty_seven_signals: |
|
| 2 |
id: 1 |
|
| 3 |
name: 37Signals |
|
| 4 |
rating: 4 |
|
| 5 |
|
|
| 6 |
TextDrive: |
|
| 7 |
id: 2 |
|
| 8 |
name: TextDrive |
|
| 9 |
rating: 4 |
|
| 10 |
|
|
| 11 |
PlanetArgon: |
|
| 12 |
id: 3 |
|
| 13 |
name: Planet Argon |
|
| 14 |
rating: 4 |
|
| 15 |
|
|
| 16 |
Google: |
|
| 17 |
id: 4 |
|
| 18 |
name: Google |
|
| 19 |
rating: 4 |
|
| 20 |
|
|
| 21 |
Ionist: |
|
| 22 |
id: 5 |
|
| 23 |
name: Ioni.st |
|
| 24 |
rating: 4 |
|
| lib/plugins/classic_pagination/test/fixtures/company.rb | ||
|---|---|---|
| 1 |
class Company < ActiveRecord::Base |
|
| 2 |
attr_protected :rating |
|
| 3 |
set_sequence_name :companies_nonstd_seq |
|
| 4 |
|
|
| 5 |
validates_presence_of :name |
|
| 6 |
def validate |
|
| 7 |
errors.add('rating', 'rating should not be 2') if rating == 2
|
|
| 8 |
end |
|
| 9 |
end |
|
| lib/plugins/classic_pagination/test/fixtures/developer.rb | ||
|---|---|---|
| 1 |
class Developer < ActiveRecord::Base |
|
| 2 |
has_and_belongs_to_many :projects |
|
| 3 |
end |
|
| 4 |
|
|
| 5 |
class DeVeLoPeR < ActiveRecord::Base |
|
| 6 |
self.table_name = "developers" |
|
| 7 |
end |
|
| lib/plugins/classic_pagination/test/fixtures/developers.yml | ||
|---|---|---|
| 1 |
david: |
|
| 2 |
id: 1 |
|
| 3 |
name: David |
|
| 4 |
salary: 80000 |
|
| 5 |
|
|
| 6 |
jamis: |
|
| 7 |
id: 2 |
|
| 8 |
name: Jamis |
|
| 9 |
salary: 150000 |
|
| 10 |
|
|
| 11 |
<% for digit in 3..10 %> |
|
| 12 |
dev_<%= digit %>: |
|
| 13 |
id: <%= digit %> |
|
| 14 |
name: fixture_<%= digit %> |
|
| 15 |
salary: 100000 |
|
| 16 |
<% end %> |
|
| 17 |
|
|
| 18 |
poor_jamis: |
|
| 19 |
id: 11 |
|
| 20 |
name: Jamis |
|
| 21 |
salary: 9000 |
|
| lib/plugins/classic_pagination/test/fixtures/developers_projects.yml | ||
|---|---|---|
| 1 |
david_action_controller: |
|
| 2 |
developer_id: 1 |
|
| 3 |
project_id: 2 |
|
| 4 |
joined_on: 2004-10-10 |
|
| 5 |
|
|
| 6 |
david_active_record: |
|
| 7 |
developer_id: 1 |
|
| 8 |
project_id: 1 |
|
| 9 |
joined_on: 2004-10-10 |
|
| 10 |
|
|
| 11 |
jamis_active_record: |
|
| 12 |
developer_id: 2 |
|
| 13 |
project_id: 1 |
|
| lib/plugins/classic_pagination/test/fixtures/project.rb | ||
|---|---|---|
| 1 |
class Project < ActiveRecord::Base |
|
| 2 |
has_and_belongs_to_many :developers, :uniq => true |
|
| 3 |
end |
|
| lib/plugins/classic_pagination/test/fixtures/projects.yml | ||
|---|---|---|
| 1 |
action_controller: |
|
| 2 |
id: 2 |
|
| 3 |
name: Active Controller |
|
| 4 |
|
|
| 5 |
active_record: |
|
| 6 |
id: 1 |
|
| 7 |
name: Active Record |
|
| lib/plugins/classic_pagination/test/fixtures/replies.yml | ||
|---|---|---|
| 1 |
witty_retort: |
|
| 2 |
id: 1 |
|
| 3 |
topic_id: 1 |
|
| 4 |
content: Birdman is better! |
|
| 5 |
created_at: <%= 6.hours.ago.to_s(:db) %> |
|
| 6 |
updated_at: nil |
|
| 7 |
|
|
| 8 |
another: |
|
| 9 |
id: 2 |
|
| 10 |
topic_id: 2 |
|
| 11 |
content: Nuh uh! |
|
| 12 |
created_at: <%= 1.hour.ago.to_s(:db) %> |
|
| 13 |
updated_at: nil |
|
| lib/plugins/classic_pagination/test/fixtures/reply.rb | ||
|---|---|---|
| 1 |
class Reply < ActiveRecord::Base |
|
| 2 |
belongs_to :topic, :include => [:replies] |
|
| 3 |
|
|
| 4 |
validates_presence_of :content |
|
| 5 |
end |
|
| lib/plugins/classic_pagination/test/fixtures/schema.sql | ||
|---|---|---|
| 1 |
CREATE TABLE 'companies' ( |
|
| 2 |
'id' INTEGER PRIMARY KEY NOT NULL, |
|
| 3 |
'name' TEXT DEFAULT NULL, |
|
| 4 |
'rating' INTEGER DEFAULT 1 |
|
| 5 |
); |
|
| 6 |
|
|
| 7 |
CREATE TABLE 'replies' ( |
|
| 8 |
'id' INTEGER PRIMARY KEY NOT NULL, |
|
| 9 |
'content' text, |
|
| 10 |
'created_at' datetime, |
|
| 11 |
'updated_at' datetime, |
|
| 12 |
'topic_id' integer |
|
| 13 |
); |
|
| 14 |
|
|
| 15 |
CREATE TABLE 'topics' ( |
|
| 16 |
'id' INTEGER PRIMARY KEY NOT NULL, |
|
| 17 |
'title' varchar(255), |
|
| 18 |
'subtitle' varchar(255), |
|
| 19 |
'content' text, |
|
| 20 |
'created_at' datetime, |
|
| 21 |
'updated_at' datetime |
|
| 22 |
); |
|
| 23 |
|
|
| 24 |
CREATE TABLE 'developers' ( |
|
| 25 |
'id' INTEGER PRIMARY KEY NOT NULL, |
|
| 26 |
'name' TEXT DEFAULT NULL, |
|
| 27 |
'salary' INTEGER DEFAULT 70000, |
|
| 28 |
'created_at' DATETIME DEFAULT NULL, |
|
| 29 |
'updated_at' DATETIME DEFAULT NULL |
|
| 30 |
); |
|
| 31 |
|
|
| 32 |
CREATE TABLE 'projects' ( |
|
| 33 |
'id' INTEGER PRIMARY KEY NOT NULL, |
|
| 34 |
'name' TEXT DEFAULT NULL |
|
| 35 |
); |
|
| 36 |
|
|
| 37 |
CREATE TABLE 'developers_projects' ( |
|
| 38 |
'developer_id' INTEGER NOT NULL, |
|
| 39 |
'project_id' INTEGER NOT NULL, |
|
| 40 |
'joined_on' DATE DEFAULT NULL, |
|
| 41 |
'access_level' INTEGER DEFAULT 1 |
|
| 42 |
); |
|
| lib/plugins/classic_pagination/test/fixtures/topic.rb | ||
|---|---|---|
| 1 |
class Topic < ActiveRecord::Base |
|
| 2 |
has_many :replies, :include => [:user], :dependent => :destroy |
|
| 3 |
end |
|
| lib/plugins/classic_pagination/test/fixtures/topics.yml | ||
|---|---|---|
| 1 |
futurama: |
|
| 2 |
id: 1 |
|
| 3 |
title: Isnt futurama awesome? |
|
| 4 |
subtitle: It really is, isnt it. |
|
| 5 |
content: I like futurama |
|
| 6 |
created_at: <%= 1.day.ago.to_s(:db) %> |
|
| 7 |
updated_at: |
|
| 8 |
|
|
| 9 |
harvey_birdman: |
|
| 10 |
id: 2 |
|
| 11 |
title: Harvey Birdman is the king of all men |
|
| 12 |
subtitle: yup |
|
| 13 |
content: It really is |
|
| 14 |
created_at: <%= 2.hours.ago.to_s(:db) %> |
|
| 15 |
updated_at: |
|
| 16 |
|
|
| 17 |
rails: |
|
| 18 |
id: 3 |
|
| 19 |
title: Rails is nice |
|
| 20 |
subtitle: It makes me happy |
|
| 21 |
content: except when I have to hack internals to fix pagination. even then really. |
|
| 22 |
created_at: <%= 20.minutes.ago.to_s(:db) %> |
|
| lib/plugins/classic_pagination/test/helper.rb | ||
|---|---|---|
| 1 |
require 'test/unit' |
|
| 2 |
|
|
| 3 |
unless defined?(ActiveRecord) |
|
| 4 |
plugin_root = File.join(File.dirname(__FILE__), '..') |
|
| 5 |
|
|
| 6 |
# first look for a symlink to a copy of the framework |
|
| 7 |
if framework_root = ["#{plugin_root}/rails", "#{plugin_root}/../../rails"].find { |p| File.directory? p }
|
|
| 8 |
puts "found framework root: #{framework_root}"
|
|
| 9 |
# this allows for a plugin to be tested outside an app |
|
| 10 |
$:.unshift "#{framework_root}/activesupport/lib", "#{framework_root}/activerecord/lib", "#{framework_root}/actionpack/lib"
|
|
| 11 |
else |
|
| 12 |
# is the plugin installed in an application? |
|
| 13 |
app_root = plugin_root + '/../../..' |
|
| 14 |
|
|
| 15 |
if File.directory? app_root + '/config' |
|
| 16 |
puts 'using config/boot.rb' |
|
| 17 |
ENV['RAILS_ENV'] = 'test' |
|
| 18 |
require File.expand_path(app_root + '/config/boot') |
|
| 19 |
else |
|
| 20 |
# simply use installed gems if available |
|
| 21 |
puts 'using rubygems' |
|
| 22 |
require 'rubygems' |
|
| 23 |
gem 'actionpack'; gem 'activerecord' |
|
| 24 |
end |
|
| 25 |
end |
|
| 26 |
|
|
| 27 |
%w(action_pack active_record action_controller active_record/fixtures action_controller/test_process).each {|f| require f}
|
|
| 28 |
|
|
| 29 |
Dependencies.load_paths.unshift "#{plugin_root}/lib"
|
|
| 30 |
end |
|
| 31 |
|
|
| 32 |
# Define the connector |
|
| 33 |
class ActiveRecordTestConnector |
|
| 34 |
cattr_accessor :able_to_connect |
|
| 35 |
cattr_accessor :connected |
|
| 36 |
|
|
| 37 |
# Set our defaults |
|
| 38 |
self.connected = false |
|
| 39 |
self.able_to_connect = true |
|
| 40 |
|
|
| 41 |
class << self |
|
| 42 |
def setup |
|
| 43 |
unless self.connected || !self.able_to_connect |
|
| 44 |
setup_connection |
|
| 45 |
load_schema |
|
| 46 |
require_fixture_models |
|
| 47 |
self.connected = true |
|
| 48 |
end |
|
| 49 |
rescue Exception => e # errors from ActiveRecord setup |
|
| 50 |
$stderr.puts "\nSkipping ActiveRecord assertion tests: #{e}"
|
|
| 51 |
#$stderr.puts " #{e.backtrace.join("\n ")}\n"
|
|
| 52 |
self.able_to_connect = false |
|
| 53 |
end |
|
| 54 |
|
|
| 55 |
private |
|
| 56 |
|
|
| 57 |
def setup_connection |
|
| 58 |
if Object.const_defined?(:ActiveRecord) |
|
| 59 |
defaults = { :database => ':memory:' }
|
|
| 60 |
begin |
|
| 61 |
options = defaults.merge :adapter => 'sqlite3', :timeout => 500 |
|
| 62 |
ActiveRecord::Base.establish_connection(options) |
|
| 63 |
ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' => options }
|
|
| 64 |
ActiveRecord::Base.connection |
|
| 65 |
rescue Exception # errors from establishing a connection |
|
| 66 |
$stderr.puts 'SQLite 3 unavailable; trying SQLite 2.' |
|
| 67 |
options = defaults.merge :adapter => 'sqlite' |
|
| 68 |
ActiveRecord::Base.establish_connection(options) |
|
| 69 |
ActiveRecord::Base.configurations = { 'sqlite2_ar_integration' => options }
|
|
| 70 |
ActiveRecord::Base.connection |
|
| 71 |
end |
|
| 72 |
|
|
| 73 |
Object.send(:const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')) unless Object.const_defined?(:QUOTED_TYPE)
|
|
| 74 |
else |
|
| 75 |
raise "Can't setup connection since ActiveRecord isn't loaded." |
|
| 76 |
end |
|
| 77 |
end |
|
| 78 |
|
|
| 79 |
# Load actionpack sqlite tables |
|
| 80 |
def load_schema |
|
| 81 |
File.read(File.dirname(__FILE__) + "/fixtures/schema.sql").split(';').each do |sql|
|
|
| 82 |
ActiveRecord::Base.connection.execute(sql) unless sql.blank? |
|
| 83 |
end |
|
| 84 |
end |
|
| 85 |
|
|
| 86 |
def require_fixture_models |
|
| 87 |
Dir.glob(File.dirname(__FILE__) + "/fixtures/*.rb").each {|f| require f}
|
|
| 88 |
end |
|
| 89 |
end |
|
| 90 |
end |
|
| 91 |
|
|
| 92 |
# Test case for inheritance |
|
| 93 |
class ActiveRecordTestCase < Test::Unit::TestCase |
|
| 94 |
# Set our fixture path |
|
| 95 |
if ActiveRecordTestConnector.able_to_connect |
|
| 96 |
self.fixture_path = "#{File.dirname(__FILE__)}/fixtures/"
|
|
| 97 |
self.use_transactional_fixtures = false |
|
| 98 |
end |
|
| 99 |
|
|
| 100 |
def self.fixtures(*args) |
|
| 101 |
super if ActiveRecordTestConnector.connected |
|
| 102 |
end |
|
| 103 |
|
|
| 104 |
def run(*args) |
|
| 105 |
super if ActiveRecordTestConnector.connected |
|
| 106 |
end |
|
| 107 |
|
|
| 108 |
# Default so Test::Unit::TestCase doesn't complain |
|
| 109 |
def test_truth |
|
| 110 |
end |
|
| 111 |
end |
|
| 112 |
|
|
| 113 |
ActiveRecordTestConnector.setup |
|
| 114 |
ActionController::Routing::Routes.reload rescue nil |
|
| 115 |
ActionController::Routing::Routes.draw do |map| |
|
| 116 |
map.connect ':controller/:action/:id' |
|
| 117 |
end |
|
| lib/plugins/classic_pagination/test/pagination_helper_test.rb | ||
|---|---|---|
| 1 |
require File.dirname(__FILE__) + '/helper' |
|
| 2 |
require File.dirname(__FILE__) + '/../init' |
|
| 3 |
|
|
| 4 |
class PaginationHelperTest < Test::Unit::TestCase |
|
| 5 |
include ActionController::Pagination |
|
| 6 |
include ActionView::Helpers::PaginationHelper |
|
| 7 |
include ActionView::Helpers::UrlHelper |
|
| 8 |
include ActionView::Helpers::TagHelper |
|
| 9 |
|
|
| 10 |
def setup |
|
| 11 |
@controller = Class.new do |
|
| 12 |
attr_accessor :url, :request |
|
| 13 |
def url_for(options, *parameters_for_method_reference) |
|
| 14 |
url |
|
| 15 |
end |
|
| 16 |
end |
|
| 17 |
@controller = @controller.new |
|
| 18 |
@controller.url = "http://www.example.com" |
|
| 19 |
end |
|
| 20 |
|
|
| 21 |
def test_pagination_links |
|
| 22 |
total, per_page, page = 30, 10, 1 |
|
| 23 |
output = pagination_links Paginator.new(@controller, total, per_page, page) |
|
| 24 |
assert_equal "1 <a href=\"http://www.example.com\">2</a> <a href=\"http://www.example.com\">3</a> ", output |
|
| 25 |
end |
|
| 26 |
|
|
| 27 |
def test_pagination_links_with_prefix |
|
| 28 |
total, per_page, page = 30, 10, 1 |
|
| 29 |
output = pagination_links Paginator.new(@controller, total, per_page, page), :prefix => 'Newer ' |
|
| 30 |
assert_equal "Newer 1 <a href=\"http://www.example.com\">2</a> <a href=\"http://www.example.com\">3</a> ", output |
|
| 31 |
end |
|
| 32 |
|
|
| 33 |
def test_pagination_links_with_suffix |
|
| 34 |
total, per_page, page = 30, 10, 1 |
|
| 35 |
output = pagination_links Paginator.new(@controller, total, per_page, page), :suffix => 'Older' |
|
| 36 |
assert_equal "1 <a href=\"http://www.example.com\">2</a> <a href=\"http://www.example.com\">3</a> Older", output |
|
| 37 |
end |
|
| 38 |
end |
|
| lib/plugins/classic_pagination/test/pagination_test.rb | ||
|---|---|---|
| 1 |
require File.dirname(__FILE__) + '/helper' |
|
| 2 |
require File.dirname(__FILE__) + '/../init' |
|
| 3 |
|
|
| 4 |
class PaginationTest < ActiveRecordTestCase |
|
| 5 |
fixtures :topics, :replies, :developers, :projects, :developers_projects |
|
| 6 |
|
|
| 7 |
class PaginationController < ActionController::Base |
|
| 8 |
if respond_to? :view_paths= |
|
| 9 |
self.view_paths = [ "#{File.dirname(__FILE__)}/../fixtures/" ]
|
|
| 10 |
else |
|
| 11 |
self.template_root = [ "#{File.dirname(__FILE__)}/../fixtures/" ]
|
|
| 12 |
end |
|
| 13 |
|
|
| 14 |
def simple_paginate |
|
| 15 |
@topic_pages, @topics = paginate(:topics) |
|
| 16 |
render :nothing => true |
|
| 17 |
end |
|
| 18 |
|
|
| 19 |
def paginate_with_per_page |
|
| 20 |
@topic_pages, @topics = paginate(:topics, :per_page => 1) |
|
| 21 |
render :nothing => true |
|
| 22 |
end |
|
| 23 |
|
|
| 24 |
def paginate_with_order |
|
| 25 |
@topic_pages, @topics = paginate(:topics, :order => 'created_at asc') |
|
| 26 |
render :nothing => true |
|
| 27 |
end |
|
| 28 |
|
|
| 29 |
def paginate_with_order_by |
|
| 30 |
@topic_pages, @topics = paginate(:topics, :order_by => 'created_at asc') |
|
| 31 |
render :nothing => true |
|
| 32 |
end |
|
| 33 |
|
|
| 34 |
def paginate_with_include_and_order |
|
| 35 |
@topic_pages, @topics = paginate(:topics, :include => :replies, :order => 'replies.created_at asc, topics.created_at asc') |
|
| 36 |
render :nothing => true |
|
| 37 |
end |
|
| 38 |
|
|
| 39 |
def paginate_with_conditions |
|
| 40 |
@topic_pages, @topics = paginate(:topics, :conditions => ["created_at > ?", 30.minutes.ago]) |
|
| 41 |
render :nothing => true |
|
| 42 |
end |
|
| 43 |
|
|
| 44 |
def paginate_with_class_name |
|
| 45 |
@developer_pages, @developers = paginate(:developers, :class_name => "DeVeLoPeR") |
|
| 46 |
render :nothing => true |
|
| 47 |
end |
|
| 48 |
|
|
| 49 |
def paginate_with_singular_name |
|
| 50 |
@developer_pages, @developers = paginate() |
|
| 51 |
render :nothing => true |
|
| 52 |
end |
|
| 53 |
|
|
| 54 |
def paginate_with_joins |
|
| 55 |
@developer_pages, @developers = paginate(:developers, |
|
| 56 |
:joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id', |
|
| 57 |
:conditions => 'project_id=1') |
|
| 58 |
render :nothing => true |
|
| 59 |
end |
|
| 60 |
|
|
| 61 |
def paginate_with_join |
|
| 62 |
@developer_pages, @developers = paginate(:developers, |
|
| 63 |
:join => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id', |
|
| 64 |
:conditions => 'project_id=1') |
|
| 65 |
render :nothing => true |
|
| 66 |
end |
|
| 67 |
|
|
| 68 |
def paginate_with_join_and_count |
|
| 69 |
@developer_pages, @developers = paginate(:developers, |
|
| 70 |
:join => 'd LEFT JOIN developers_projects ON d.id = developers_projects.developer_id', |
|
| 71 |
:conditions => 'project_id=1', |
|
| 72 |
:count => "d.id") |
|
| 73 |
render :nothing => true |
|
| 74 |
end |
|
| 75 |
|
|
| 76 |
def paginate_with_join_and_group |
|
| 77 |
@developer_pages, @developers = paginate(:developers, |
|
| 78 |
:join => 'INNER JOIN developers_projects ON developers.id = developers_projects.developer_id', |
|
| 79 |
:group => 'developers.id') |
|
| 80 |
render :nothing => true |
|
| 81 |
end |
|
| 82 |
|
|
| 83 |
def rescue_errors(e) raise e end |
|
| 84 |
|
|
| 85 |
def rescue_action(e) raise end |
|
| 86 |
|
|
| 87 |
end |
|
| 88 |
|
|
| 89 |
def setup |
|
| 90 |
@controller = PaginationController.new |
|
| 91 |
@request = ActionController::TestRequest.new |
|
| 92 |
@response = ActionController::TestResponse.new |
|
| 93 |
super |
|
| 94 |
end |
|
| 95 |
|
|
| 96 |
# Single Action Pagination Tests |
|
| 97 |
|
|
| 98 |
def test_simple_paginate |
|
| 99 |
get :simple_paginate |
|
| 100 |
assert_equal 1, assigns(:topic_pages).page_count |
|
| 101 |
assert_equal 3, assigns(:topics).size |
|
| 102 |
end |
|
| 103 |
|
|
| 104 |
def test_paginate_with_per_page |
|
| 105 |
get :paginate_with_per_page |
|
| 106 |
assert_equal 1, assigns(:topics).size |
|
| 107 |
assert_equal 3, assigns(:topic_pages).page_count |
|
| 108 |
end |
|
| 109 |
|
|
| 110 |
def test_paginate_with_order |
|
| 111 |
get :paginate_with_order |
|
| 112 |
expected = [topics(:futurama), |
|
| 113 |
topics(:harvey_birdman), |
|
| 114 |
topics(:rails)] |
|
| 115 |
assert_equal expected, assigns(:topics) |
|
| 116 |
assert_equal 1, assigns(:topic_pages).page_count |
|
| 117 |
end |
|
| 118 |
|
|
| 119 |
def test_paginate_with_order_by |
|
| 120 |
get :paginate_with_order |
|
| 121 |
expected = assigns(:topics) |
|
| 122 |
get :paginate_with_order_by |
|
| 123 |
assert_equal expected, assigns(:topics) |
|
| 124 |
assert_equal 1, assigns(:topic_pages).page_count |
|
| 125 |
end |
|
| 126 |
|
|
| 127 |
def test_paginate_with_conditions |
|
| 128 |
get :paginate_with_conditions |
|
| 129 |
expected = [topics(:rails)] |
|
| 130 |
assert_equal expected, assigns(:topics) |
|
| 131 |
assert_equal 1, assigns(:topic_pages).page_count |
|
| 132 |
end |
|
| 133 |
|
|
| 134 |
def test_paginate_with_class_name |
|
| 135 |
get :paginate_with_class_name |
|
| 136 |
|
|
| 137 |
assert assigns(:developers).size > 0 |
|
| 138 |
assert_equal DeVeLoPeR, assigns(:developers).first.class |
|
| 139 |
end |
|
| 140 |
|
|
| 141 |
def test_paginate_with_joins |
|
| 142 |
get :paginate_with_joins |
|
| 143 |
assert_equal 2, assigns(:developers).size |
|
| 144 |
developer_names = assigns(:developers).map { |d| d.name }
|
|
| 145 |
assert developer_names.include?('David')
|
|
| 146 |
assert developer_names.include?('Jamis')
|
|
| 147 |
end |
|
| 148 |
|
|
| 149 |
def test_paginate_with_join_and_conditions |
|
| 150 |
get :paginate_with_joins |
|
| 151 |
expected = assigns(:developers) |
|
| 152 |
get :paginate_with_join |
|
| 153 |
assert_equal expected, assigns(:developers) |
|
| 154 |
end |
|
| 155 |
|
|
| 156 |
def test_paginate_with_join_and_count |
|
| 157 |
get :paginate_with_joins |
|
| 158 |
expected = assigns(:developers) |
|
| 159 |
get :paginate_with_join_and_count |
|
| 160 |
assert_equal expected, assigns(:developers) |
|
| 161 |
end |
|
| 162 |
|
|
| 163 |
def test_paginate_with_include_and_order |
|
| 164 |
get :paginate_with_include_and_order |
|
| 165 |
expected = Topic.find(:all, :include => 'replies', :order => 'replies.created_at asc, topics.created_at asc', :limit => 10) |
|
| 166 |
assert_equal expected, assigns(:topics) |
|
| 167 |
end |
|
| 168 |
|
|
| 169 |
def test_paginate_with_join_and_group |
|
| 170 |
get :paginate_with_join_and_group |
|
| 171 |
assert_equal 2, assigns(:developers).size |
|
| 172 |
assert_equal 2, assigns(:developer_pages).item_count |
|
| 173 |
developer_names = assigns(:developers).map { |d| d.name }
|
|
| 174 |
assert developer_names.include?('David')
|
|
| 175 |
assert developer_names.include?('Jamis')
|
|
| 176 |
end |
|
| 177 |
end |
|
Also available in: Unified diff