will_paginate added to plugins

This commit is contained in:
Eugene Korbut 2009-02-10 04:13:08 +10:00
parent 8f0577bf56
commit ce40b31f7a
60 changed files with 4016 additions and 0 deletions

54
vendor/plugins/will_paginate/.autotest vendored Normal file
View file

@ -0,0 +1,54 @@
Autotest.add_hook :initialize do |at|
at.libs = 'lib:spec'
at.clear_mappings
at.add_mapping(%r{^lib/will_paginate/(.+)\.rb$}) { |_, match|
"spec/#{match[1]}_spec.rb"
}
at.add_mapping(%r{^spec/.+_spec\.rb$}) { |f, _| f }
at.add_mapping(%r{^spec/(finders/activerecord_test_connector.rb|database.yml|fixtures/.+)$}) {
'spec/finders/active_record_spec.rb'
}
at.add_mapping(%r{^spec/((spec_helper|shared/.+)\.rb|spec.opts)$}) {
# simply re-run all specs
at.files_matching %r{^spec/.+_spec\.rb$}
}
# add these to ignore list
%w{ .git test/ rails/ Rakefile README.rdoc init.rb .autotest
doc/ coverage/ LICENSE CHANGELOG .manifest will_paginate.gemspec examples/
spec/tasks.rake spec/console spec/rcov.opts
}.each { |path| at.add_exception path }
end
Autotest::Rspec.class_eval do
# RSpec guys forgot about `libs` in make_test_cmd
def make_test_cmd_with_libs(files_to_test)
make_test_cmd_without_libs(files_to_test).sub(' -S ', " -S -I#{libs} ")
end
alias :make_test_cmd_without_libs :make_test_cmd
alias :make_test_cmd :make_test_cmd_with_libs
# ugh, we have to monkeypatch Autotest ...
# the regexp it generates for the exception list just matches too much
#
# SOLUTION: wrap it up in another regexp that anchors the whole expression to
# the beginning of the path
def exceptions
unless defined? @exceptions then
if @exception_list.empty? then
@exceptions = nil
else
# old (BAD):
# @exceptions = Regexp.union(*@exception_list)
@exceptions = /^\.\/#{Regexp.union(*@exception_list)}/
end
end
@exceptions
end
end

View file

@ -0,0 +1,4 @@
/doc
/rails
*.gem
/coverage

66
vendor/plugins/will_paginate/.manifest vendored Normal file
View file

@ -0,0 +1,66 @@
CHANGELOG.rdoc
LICENSE
README.rdoc
Rakefile
examples
examples/apple-circle.gif
examples/index.haml
examples/index.html
examples/pagination.css
examples/pagination.sass
init.rb
lib
lib/will_paginate
lib/will_paginate.rb
lib/will_paginate/array.rb
lib/will_paginate/collection.rb
lib/will_paginate/core_ext.rb
lib/will_paginate/deprecation.rb
lib/will_paginate/finders
lib/will_paginate/finders.rb
lib/will_paginate/finders/active_record
lib/will_paginate/finders/active_record.rb
lib/will_paginate/finders/active_record/named_scope.rb
lib/will_paginate/finders/active_record/named_scope_patch.rb
lib/will_paginate/finders/active_resource.rb
lib/will_paginate/finders/base.rb
lib/will_paginate/finders/data_mapper.rb
lib/will_paginate/version.rb
lib/will_paginate/view_helpers
lib/will_paginate/view_helpers.rb
lib/will_paginate/view_helpers/action_view.rb
lib/will_paginate/view_helpers/base.rb
lib/will_paginate/view_helpers/link_renderer.rb
lib/will_paginate/view_helpers/link_renderer_base.rb
spec
spec/collection_spec.rb
spec/console
spec/console_fixtures.rb
spec/database.yml
spec/finders
spec/finders/active_record_spec.rb
spec/finders/active_resource_spec.rb
spec/finders/activerecord_test_connector.rb
spec/finders_spec.rb
spec/fixtures
spec/fixtures/admin.rb
spec/fixtures/developer.rb
spec/fixtures/developers_projects.yml
spec/fixtures/project.rb
spec/fixtures/projects.yml
spec/fixtures/replies.yml
spec/fixtures/reply.rb
spec/fixtures/schema.rb
spec/fixtures/topic.rb
spec/fixtures/topics.yml
spec/fixtures/user.rb
spec/fixtures/users.yml
spec/rcov.opts
spec/spec.opts
spec/spec_helper.rb
spec/tasks.rake
spec/view_helpers
spec/view_helpers/action_view_spec.rb
spec/view_helpers/base_spec.rb
spec/view_helpers/link_renderer_base_spec.rb
spec/view_helpers/view_example_group.rb

View file

@ -0,0 +1,104 @@
== "agnostic" branch
* setup Autotest
* added per_page=(limit) attribute writer to set default per_page
* Remove :include option from count_all query when possible (Rails 2.1)
* added WP::ViewHelpers::ActionView and LinkRenderer
* specs for ViewHelpers::Base and LinkRendererBase
* created LinkRendererBase that implements windowed visible page numbers logic
* created WP::ViewHelpers::Base abstract module that implements generic view helpers
* converted finder tests to specs
* added WP::Finders::DataMapper
* added WP::Finders::ActiveRecord mixin for ActiveRecord::Base
* created WP::Finders::Base abstract module that implements generic pagination logic
* removed dependency to ActiveSupport
=== TODO:
* Make a concrete implementation of LinkRendererBase that will generate HTML for both ActionView and Merb
* ActionView and Merb integration tests for view helpers
* 3c4725 Oops, I used return in an iterator block. I obviously write too much JavaScript
* 537f22 ensure that 'href' values in pagination links are escaped URLs
== 2.3.1, released 2008-05-04
* Fixed page numbers not showing with custom routes and implicit first page
* Try to use Hanna for documentation (falls back to default RDoc template if not)
== 2.3.0, released 2008-04-29
* Changed LinkRenderer to receive collection, options and reference to view template NOT in
constructor, but with the #prepare method. This is a step towards supporting passing of
LinkRenderer (or subclass) instances that may be preconfigured in some way
* LinkRenderer now has #page_link and #page_span methods for easier customization of output in
subclasses
* Changed page_entries_info() method to adjust its output according to humanized class name of
collection items. Override this with :entry_name parameter (singular).
page_entries_info(@posts)
#-> "Displaying all 12 posts"
page_entries_info(@posts, :entry_name => 'item')
#-> "Displaying all 12 items"
== 2.2.3, released 2008-04-26
* will_paginate gem is no longer published on RubyForge, but on
gems.github.com:
gem sources -a http://gems.github.com/ (you only need to do this once)
gem install mislav-will_paginate
* extract reusable pagination testing stuff into WillPaginate::View
* rethink the page URL construction mechanizm to be more bulletproof when
combined with custom routing for page parameter
* test that anchor parameter can be used in pagination links
== 2.2.2, released 2008-04-21
* Add support for page parameter in custom routes like "/foo/page/2"
* Change output of "page_entries_info" on single-page collection and erraneous
output with empty collection as reported by Tim Chater
== 2.2.1, released 2008-04-08
* take less risky path when monkeypatching named_scope; fix that it no longer
requires ActiveRecord::VERSION
* use strings in "respond_to?" calls to work around a bug in acts_as_ferret
stable (ugh)
* add rake release task
== 2.2.0, released 2008-04-07
=== API changes
* Rename WillPaginate::Collection#page_count to "total_pages" for consistency.
If you implemented this interface, change your implementation accordingly.
* Remove old, deprecated style of calling Array#paginate as "paginate(page,
per_page)". If you want to specify :page, :per_page or :total_entries, use a
parameter hash.
* Rename LinkRenderer#url_options to "url_for" and drastically optimize it
=== View changes
* Added "prev_page" and "next_page" CSS classes on previous/next page buttons
* Add examples of pagination links styling in "examples/index.html"
* Change gap in pagination links from "..." to
"<span class="gap">&hellip;</span>".
* Add "paginated_section", a block helper that renders pagination both above and
below content in the block
* Add rel="prev|next|start" to page links
=== Other
* Add ability to opt-in for Rails 2.1 feature "named_scope" by calling
WillPaginate.enable_named_scope (tested in Rails 1.2.6 and 2.0.2)
* Support complex page parameters like "developers[page]"
* Move Array#paginate definition to will_paginate/array.rb. You can now easily
use pagination on arrays outside of Rails:
gem 'will_paginate'
require 'will_paginate/array'
* Add "paginated_each" method for iterating through every record by loading only
one page of records at the time
* Rails 2: Rescue from WillPaginate::InvalidPage error with 404 Not Found by
default

18
vendor/plugins/will_paginate/LICENSE vendored Normal file
View file

@ -0,0 +1,18 @@
Copyright (c) 2007 PJ Hyett and Mislav Marohnić
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

123
vendor/plugins/will_paginate/README.rdoc vendored Normal file
View file

@ -0,0 +1,123 @@
= WillPaginate
Pagination is just limiting the number of records displayed. Why should you let it get in your way
while developing?
This is how you paginate on an ActiveRecord model:
Post.paginate :page => 1, :order => 'created_at DESC'
Most of the time it's as simple as replacing "find" with "paginate" and specifying the page you want.
Some resources to get you started:
* The {will_paginate project page}[http://github.com/mislav/will_paginate];
* Your mind reels with questions? Join our {Google group}[http://groups.google.com/group/will_paginate];
* {How to report bugs}[http://github.com/mislav/will_paginate/wikis/report-bugs];
* {Watch the will_paginate screencast}[http://railscasts.com/episodes/51] by Ryan Bates.
== Installation
The recommended way is that you get the gem:
gem install --source=http://gems.github.com/ mislav-will_paginate
After that you don't need the will_paginate <i>plugin</i> in your Rails application anymore. In
<b>Rails 2.1</b>, add a gem dependency:
config.gem 'mislav-will_paginate', :lib => 'will_paginate', :version => '~> 2.5'
If you're using Rails 2.0 or older, just add a simple require to the end of your
"config/environment.rb" instead:
gem 'mislav-will_paginate', '~> 2.5'
require 'will_paginate'
That's it. Remember to install the gem on <b>all</b> machines that you are deploying to.
<i>There are extensive {installation
instructions}[http://github.com/mislav/will_paginate/wikis/installation] on {the
wiki}[http://github.com/mislav/will_paginate/wikis].</i>
== Example usage
Use a paginate finder in the controller:
@posts = Post.paginate_by_board_id(
@board.id,
:page => params[:page],
:order => 'updated_at DESC'
)
Yeah, +paginate+ works just like +find+ -- it just doesn't fetch all the records. Don't forget to
tell it which page you want, or it will complain! Read more about WillPaginate::Finders.
Render the posts in your view like you would normally do. When you need to render pagination, just
stick this in:
<%= will_paginate @posts %>
You're done. (Copy and paste the example fancy CSS styles from the bottom.) You can find the option
list at WillPaginate::ViewHelpers.
How does it know how much items to fetch per page? It asks your model by calling its
<tt>per_page</tt> class method. You can define it like this:
class Post < ActiveRecord::Base
def self.per_page() 50 end
end
... or don't worry about it at all. WillPaginate defines it to be <b>30</b> by default. You can
always specify the count explicitly when calling +paginate+:
@posts = Post.paginate :page => params[:page], :per_page => 50
The +paginate+ finder wraps the original finder and returns your result set that now has some new
properties. You can use the collection as you would use any other array. WillPaginate view helpers
also need that object to be able to render pagination:
<ol>
<% for post in @posts -%>
<li>Render `post` in some nice way.</li>
<% end -%>
</ol>
<p>Now let's render us some pagination!</p>
<%= will_paginate @posts %>
More detailed documentation:
* WillPaginate::Finders for pagination on your models;
* WillPaginate::ViewHelpers for your views.
== Authors and credits
Authors:: Mislav Marohnić, PJ Hyett
Original announcement:: http://errtheblog.com/post/929
Original PHP source:: http://www.strangerstudios.com/sandbox/pagination/diggstyle.php
All these people helped making will_paginate what it is now with their code contributions or just
simply awesome ideas:
Chris Wanstrath, Dr. Nic Williams, K. Adam Christensen, Mike Garey, Bence Golda, Matt Aimonetti,
Charles Brian Quinn, Desi McAdam, James Coglan, Matijs van Zuijlen, Maria, Brendan Ribera, Todd
Willey, Bryan Helmkamp, Jan Berkel, Lourens Naudé, Rick Olson, Russell Norris, Piotr Usewicz, Chris
Eppstein.
== Usable pagination in the UI
There are some CSS styles to get you started in the "examples/" directory. They are showcased in the
<b>"examples/index.html"</b> file.
More reading about pagination as design pattern:
* {Pagination 101}[http://kurafire.net/log/archive/2007/06/22/pagination-101];
* {Pagination gallery}[http://www.smashingmagazine.com/2007/11/16/pagination-gallery-examples-and-good-practices/] featured on Smashing Magazine;
* {Pagination design pattern}[http://developer.yahoo.com/ypatterns/parent.php?pattern=pagination] on Yahoo Design Pattern Library.
Want to discuss, request features, ask questions? Join the {Google
group}[http://groups.google.com/group/will_paginate].

62
vendor/plugins/will_paginate/Rakefile vendored Normal file
View file

@ -0,0 +1,62 @@
require 'rubygems'
begin
hanna_dir = '/Users/mislav/Projects/Hanna/lib'
$:.unshift hanna_dir if File.exists? hanna_dir
require 'hanna/rdoctask'
rescue LoadError
require 'rake'
require 'rake/rdoctask'
end
load 'spec/tasks.rake'
desc 'Default: run specs.'
task :default => :spec
desc 'Generate RDoc documentation for the will_paginate plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'CHANGELOG').
include('lib/**/*.rb').
exclude('lib/will_paginate/named_scope*').
exclude('lib/will_paginate/deprecation.rb').
exclude('lib/will_paginate/version.rb')
rdoc.main = "README.rdoc" # page to start on
rdoc.title = "will_paginate documentation"
rdoc.rdoc_dir = 'doc' # rdoc output folder
rdoc.options << '--inline-source' << '--charset=UTF-8'
rdoc.options << '--webcvs=http://github.com/mislav/will_paginate/tree/master/'
end
desc %{Update ".manifest" with the latest list of project filenames. Respect\
.gitignore by excluding everything that git ignores. Update `files` and\
`test_files` arrays in "*.gemspec" file if it's present.}
task :manifest do
list = Dir['**/*'].sort
spec_file = Dir['*.gemspec'].first
list -= [spec_file] if spec_file
File.read('.gitignore').each_line do |glob|
glob = glob.chomp.sub(/^\//, '')
list -= Dir[glob]
list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
puts "excluding #{glob}"
end
if spec_file
spec = File.read spec_file
spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
assignment = $1
bunch = $2 ? list.grep(/^(test|spec)\//) : list
'%s%%w(%s)' % [assignment, bunch.join(' ')]
end
File.open(spec_file, 'w') {|f| f << spec }
end
File.open('.manifest', 'w') {|f| f << list.join("\n") }
end
task :examples do
%x(haml examples/index.haml examples/index.html)
%x(sass examples/pagination.sass examples/pagination.css)
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

View file

@ -0,0 +1,69 @@
!!!
%html
%head
%title Samples of pagination styling for will_paginate
%link{ :rel => 'stylesheet', :type => 'text/css', :href => 'pagination.css' }
%style{ :type => 'text/css' }
:sass
html
:margin 0
:padding 0
:background #999
:font normal 76% "Lucida Grande", Verdana, Helvetica, sans-serif
body
:margin 2em
:padding 2em
:border 2px solid gray
:background white
:color #222
h1
:font-size 2em
:font-weight normal
:margin 0 0 1em 0
h2
:font-size 1.4em
:margin 1em 0 .5em 0
pre
:font-size 13px
:font-family Monaco, "DejaVu Sans Mono", "Bitstream Vera Mono", "Courier New", monospace
- pagination = '<span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>'
- pagination_no_page_links = '<span class="disabled prev_page">&laquo; Previous</span> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>'
%body
%h1 Samples of pagination styling for will_paginate
%p
Find these styles in <b>"examples/pagination.css"</b> of <i>will_paginate</i> library.
There is a Sass version of it for all you sassy people.
%p
Read about good rules for pagination:
%a{ :href => 'http://kurafire.net/log/archive/2007/06/22/pagination-101' } Pagination 101
%p
%em Warning:
page links below don't lead anywhere (so don't click on them).
%h2 Unstyled pagination <span style="font-weight:normal">(<i>ewww!</i>)</span>
%div= pagination
%h2 Digg.com
.digg_pagination= pagination
%h2 Digg-style, no page links
.digg_pagination= pagination_no_page_links
%p Code that renders this:
%pre= '<code>%s</code>' % %[<%= will_paginate @posts, :page_links => false %>].gsub('<', '&lt;').gsub('>', '&gt;')
%h2 Digg-style, extra content
.digg_pagination
.page_info Displaying entries <b>1&nbsp;-&nbsp;6</b> of <b>180</b> in total
= pagination
%p Code that renders this:
%pre= '<code>%s</code>' % %[<div class="digg_pagination">\n <div clas="page_info">\n <%= page_entries_info @posts %>\n </div>\n <%= will_paginate @posts, :container => false %>\n</div>].gsub('<', '&lt;').gsub('>', '&gt;')
%h2 Apple.com store
.apple_pagination= pagination
%h2 Flickr.com
.flickr_pagination
= pagination
.page_info (118 photos)

View file

@ -0,0 +1,92 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
</html>
<head>
<title>Samples of pagination styling for will_paginate</title>
<link href='pagination.css' rel='stylesheet' type='text/css' />
<style type='text/css'>
html {
margin: 0;
padding: 0;
background: #999;
font: normal 76% "Lucida Grande", Verdana, Helvetica, sans-serif; }
body {
margin: 2em;
padding: 2em;
border: 2px solid gray;
background: white;
color: #222; }
h1 {
font-size: 2em;
font-weight: normal;
margin: 0 0 1em 0; }
h2 {
font-size: 1.4em;
margin: 1em 0 .5em 0; }
pre {
font-size: 13px;
font-family: Monaco, "DejaVu Sans Mono", "Bitstream Vera Mono", "Courier New", monospace; }
</style>
</head>
<body>
<h1>Samples of pagination styling for will_paginate</h1>
<p>
Find these styles in <b>"examples/pagination.css"</b> of <i>will_paginate</i> library.
There is a Sass version of it for all you sassy people.
</p>
<p>
Read about good rules for pagination:
<a href='http://kurafire.net/log/archive/2007/06/22/pagination-101'>Pagination 101</a>
</p>
<p>
<em>Warning:</em>
page links below don't lead anywhere (so don't click on them).
</p>
<h2>
Unstyled pagination <span style="font-weight:normal">(<i>ewww!</i>)</span>
</h2>
<div>
<span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
</div>
<h2>Digg.com</h2>
<div class='digg_pagination'>
<span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
</div>
<h2>Digg-style, no page links</h2>
<div class='digg_pagination'>
<span class="disabled prev_page">&laquo; Previous</span> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
</div>
<p>Code that renders this:</p>
<pre>
<code>&lt;%= will_paginate @posts, :page_links =&gt; false %&gt;</code>
</pre>
<h2>Digg-style, extra content</h2>
<div class='digg_pagination'>
<div class='page_info'>
Displaying entries <b>1&nbsp;-&nbsp;6</b> of <b>180</b> in total
</div>
<span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
</div>
<p>Code that renders this:</p>
<pre>
<code>&lt;div class="digg_pagination"&gt;
&lt;div clas="page_info"&gt;
&lt;%= page_entries_info @posts %&gt;
&lt;/div&gt;
&lt;%= will_paginate @posts, :container =&gt; false %&gt;
&lt;/div&gt;</code>
</pre>
<h2>Apple.com store</h2>
<div class='apple_pagination'>
<span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
</div>
<h2>Flickr.com</h2>
<div class='flickr_pagination'>
<span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
<div class='page_info'>(118 photos)</div>
</div>
</body>

View file

@ -0,0 +1,90 @@
.digg_pagination {
background: white;
/* self-clearing method: */ }
.digg_pagination a, .digg_pagination span {
padding: .2em .5em;
display: block;
float: left;
margin-right: 1px; }
.digg_pagination span.disabled {
color: #999;
border: 1px solid #DDD; }
.digg_pagination span.current {
font-weight: bold;
background: #2E6AB1;
color: white;
border: 1px solid #2E6AB1; }
.digg_pagination a {
text-decoration: none;
color: #105CB6;
border: 1px solid #9AAFE5; }
.digg_pagination a:hover, .digg_pagination a:focus {
color: #003;
border-color: #003; }
.digg_pagination .page_info {
background: #2E6AB1;
color: white;
padding: .4em .6em;
width: 22em;
margin-bottom: .3em;
text-align: center; }
.digg_pagination .page_info b {
color: #003;
background: #6aa6ed;
padding: .1em .25em; }
.digg_pagination:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden; }
* html .digg_pagination {
height: 1%; }
*:first-child+html .digg_pagination {
overflow: hidden; }
.apple_pagination {
background: #F1F1F1;
border: 1px solid #E5E5E5;
text-align: center;
padding: 1em; }
.apple_pagination a, .apple_pagination span {
padding: .2em .3em; }
.apple_pagination span.disabled {
color: #AAA; }
.apple_pagination span.current {
font-weight: bold;
background: transparent url(apple-circle.gif) no-repeat 50% 50%; }
.apple_pagination a {
text-decoration: none;
color: black; }
.apple_pagination a:hover, .apple_pagination a:focus {
text-decoration: underline; }
.flickr_pagination {
text-align: center;
padding: .3em; }
.flickr_pagination a, .flickr_pagination span {
padding: .2em .5em; }
.flickr_pagination span.disabled {
color: #AAA; }
.flickr_pagination span.current {
font-weight: bold;
color: #FF0084; }
.flickr_pagination a {
border: 1px solid #DDDDDD;
color: #0063DC;
text-decoration: none; }
.flickr_pagination a:hover, .flickr_pagination a:focus {
border-color: #003366;
background: #0063DC;
color: white; }
.flickr_pagination .page_info {
color: #aaa;
padding-top: .8em; }
.flickr_pagination .prev_page, .flickr_pagination .next_page {
border-width: 2px; }
.flickr_pagination .prev_page {
margin-right: 1em; }
.flickr_pagination .next_page {
margin-left: 1em; }

View file

@ -0,0 +1,91 @@
.digg_pagination
:background white
a, span
:padding .2em .5em
:display block
:float left
:margin-right 1px
span.disabled
:color #999
:border 1px solid #DDD
span.current
:font-weight bold
:background #2E6AB1
:color white
:border 1px solid #2E6AB1
a
:text-decoration none
:color #105CB6
:border 1px solid #9AAFE5
&:hover, &:focus
:color #003
:border-color #003
.page_info
:background #2E6AB1
:color white
:padding .4em .6em
:width 22em
:margin-bottom .3em
:text-align center
b
:color #003
:background = #2E6AB1 + 60
:padding .1em .25em
/* self-clearing method:
&:after
:content "."
:display block
:height 0
:clear both
:visibility hidden
* html &
:height 1%
*:first-child+html &
:overflow hidden
.apple_pagination
:background #F1F1F1
:border 1px solid #E5E5E5
:text-align center
:padding 1em
a, span
:padding .2em .3em
span.disabled
:color #AAA
span.current
:font-weight bold
:background transparent url(apple-circle.gif) no-repeat 50% 50%
a
:text-decoration none
:color black
&:hover, &:focus
:text-decoration underline
.flickr_pagination
:text-align center
:padding .3em
a, span
:padding .2em .5em
span.disabled
:color #AAA
span.current
:font-weight bold
:color #FF0084
a
:border 1px solid #DDDDDD
:color #0063DC
:text-decoration none
&:hover, &:focus
:border-color #003366
:background #0063DC
:color white
.page_info
:color #aaa
:padding-top .8em
.prev_page, .next_page
:border-width 2px
.prev_page
:margin-right 1em
.next_page
:margin-left 1em

1
vendor/plugins/will_paginate/init.rb vendored Normal file
View file

@ -0,0 +1 @@
require 'will_paginate'

View file

@ -0,0 +1,43 @@
require 'will_paginate/deprecation'
# = You *will* paginate!
#
# First read about WillPaginate::Finder::ClassMethods, then see
# WillPaginate::ViewHelpers. The magical array you're handling in-between is
# WillPaginate::Collection.
#
# Happy paginating!
module WillPaginate
def self.enable
Deprecation.warn "WillPaginate::enable() doesn't do anything anymore"
end
# Enable named_scope, a feature of Rails 2.1, even if you have older Rails
# (tested on Rails 2.0.2 and 1.2.6).
#
# You can pass +false+ for +patch+ parameter to skip monkeypatching
# *associations*. Use this if you feel that <tt>named_scope</tt> broke
# has_many, has_many :through or has_and_belongs_to_many associations in
# your app. By passing +false+, you can still use <tt>named_scope</tt> in
# your models, but not through associations.
def self.enable_named_scope(patch = true)
return if defined? ActiveRecord::NamedScope
require 'will_paginate/finders/active_record/named_scope'
require 'will_paginate/finders/active_record/named_scope_patch' if patch
ActiveRecord::Base.send :include, WillPaginate::NamedScope
end
end
if defined?(Rails)
require 'will_paginate/view_helpers/action_view' if defined?(ActionController)
require 'will_paginate/finders/active_record' if defined?(ActiveRecord)
end
if defined?(Merb::Plugins)
require 'will_paginate/collection'
require 'will_paginate/view_helpers/base'
require 'will_paginate/view_helpers/link_renderer'
# this only includes will_paginate view stuff in Merb (not finder adapters)
Merb::AbstractController.send(:include, WillPaginate::ViewHelpers::Base)
end

View file

@ -0,0 +1,33 @@
require 'will_paginate/collection'
class Array
# Paginates a static array (extracting a subset of it). The result is a
# WillPaginate::Collection instance, which is an array with few more
# properties about its paginated state.
#
# Parameters:
# * <tt>:page</tt> - current page, defaults to 1
# * <tt>:per_page</tt> - limit of items per page, defaults to 30
# * <tt>:total_entries</tt> - total number of items in the array, defaults to
# <tt>array.length</tt> (obviously)
#
# Example:
# arr = ['a', 'b', 'c', 'd', 'e']
# paged = arr.paginate(:per_page => 2) #-> ['a', 'b']
# paged.total_entries #-> 5
# arr.paginate(:page => 2, :per_page => 2) #-> ['c', 'd']
# arr.paginate(:page => 3, :per_page => 2) #-> ['e']
#
# This method was originally {suggested by Desi
# McAdam}[http://www.desimcadam.com/archives/8] and later proved to be the
# most useful method of will_paginate library.
def paginate(options = {})
raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options
WillPaginate::Collection.create options[:page] || 1,
options[:per_page] || 30,
options[:total_entries] || self.length do |pager|
pager.replace self[pager.offset, pager.per_page].to_a
end
end
end

View file

@ -0,0 +1,145 @@
module WillPaginate
# = Invalid page number error
# This is an ArgumentError raised in case a page was requested that is either
# zero or negative number. You should decide how do deal with such errors in
# the controller.
#
# If you're using Rails 2, then this error will automatically get handled like
# 404 Not Found. The hook is in "will_paginate.rb":
#
# ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found
#
# If you don't like this, use your preffered method of rescuing exceptions in
# public from your controllers to handle this differently. The +rescue_from+
# method is a nice addition to Rails 2.
#
# This error is *not* raised when a page further than the last page is
# requested. Use <tt>WillPaginate::Collection#out_of_bounds?</tt> method to
# check for those cases and manually deal with them as you see fit.
class InvalidPage < ArgumentError
def initialize(page, page_num)
super "#{page.inspect} given as value, which translates to '#{page_num}' as page number"
end
end
# = The key to pagination
# Arrays returned from paginating finds are, in fact, instances of this little
# class. You may think of WillPaginate::Collection as an ordinary array with
# some extra properties. Those properties are used by view helpers to generate
# correct page links.
#
# WillPaginate::Collection also assists in rolling out your own pagination
# solutions: see +create+.
#
# If you are writing a library that provides a collection which you would like
# to conform to this API, you don't have to copy these methods over; simply
# make your plugin/gem dependant on the "will_paginate" gem:
#
# gem 'will_paginate'
# require 'will_paginate/collection'
#
# # now use WillPaginate::Collection directly or subclass it
class Collection < Array
attr_reader :current_page, :per_page, :total_entries, :total_pages
# Arguments to the constructor are the current page number, per-page limit
# and the total number of entries. The last argument is optional because it
# is best to do lazy counting; in other words, count *conditionally* after
# populating the collection using the +replace+ method.
def initialize(page, per_page, total = nil)
@current_page = page.to_i
raise InvalidPage.new(page, @current_page) if @current_page < 1
@per_page = per_page.to_i
raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
self.total_entries = total if total
end
# Just like +new+, but yields the object after instantiation and returns it
# afterwards. This is very useful for manual pagination:
#
# @entries = WillPaginate::Collection.create(1, 10) do |pager|
# result = Post.find(:all, :limit => pager.per_page, :offset => pager.offset)
# # inject the result array into the paginated collection:
# pager.replace(result)
#
# unless pager.total_entries
# # the pager didn't manage to guess the total count, do it manually
# pager.total_entries = Post.count
# end
# end
#
# The possibilities with this are endless. For another example, here is how
# WillPaginate used to define pagination for Array instances:
#
# Array.class_eval do
# def paginate(page = 1, per_page = 15)
# WillPaginate::Collection.create(page, per_page, size) do |pager|
# pager.replace self[pager.offset, pager.per_page].to_a
# end
# end
# end
#
# The Array#paginate API has since then changed, but this still serves as a
# fine example of WillPaginate::Collection usage.
def self.create(page, per_page, total = nil, &block)
pager = new(page, per_page, total)
yield pager
pager
end
# Helper method that is true when someone tries to fetch a page with a
# larger number than the last page. Can be used in combination with flashes
# and redirecting.
def out_of_bounds?
current_page > total_pages
end
# Current offset of the paginated collection. If we're on the first page,
# it is always 0. If we're on the 2nd page and there are 30 entries per page,
# the offset is 30. This property is useful if you want to render ordinals
# besides your records: simply start with offset + 1.
def offset
(current_page - 1) * per_page
end
# current_page - 1 or nil if there is no previous page
def previous_page
current_page > 1 ? (current_page - 1) : nil
end
# current_page + 1 or nil if there is no next page
def next_page
current_page < total_pages ? (current_page + 1) : nil
end
def total_entries=(number)
@total_entries = number.to_i
@total_pages = (@total_entries / per_page.to_f).ceil
end
# This is a magic wrapper for the original Array#replace method. It serves
# for populating the paginated collection after initialization.
#
# Why magic? Because it tries to guess the total number of entries judging
# by the size of given array. If it is shorter than +per_page+ limit, then we
# know we're on the last page. This trick is very useful for avoiding
# unnecessary hits to the database to do the counting after we fetched the
# data for the current page.
#
# However, after using +replace+ you should always test the value of
# +total_entries+ and set it to a proper value if it's +nil+. See the example
# in +create+.
def replace(array)
result = super
# The collection is shorter then page limit? Rejoice, because
# then we know that we are on the last page!
if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
self.total_entries = offset + length
end
result
end
end
end

View file

@ -0,0 +1,58 @@
require 'set'
require 'will_paginate/array'
## Everything below blatantly stolen from ActiveSupport :o
unless Hash.instance_methods.include? 'except'
Hash.class_eval do
# Returns a new hash without the given keys.
def except(*keys)
rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
reject { |key,| rejected.include?(key) }
end
# Replaces the hash without only the given keys.
def except!(*keys)
replace(except(*keys))
end
end
end
unless Hash.instance_methods.include? 'slice'
Hash.class_eval do
# Returns a new hash with only the given keys.
def slice(*keys)
allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
reject { |key,| !allowed.include?(key) }
end
# Replaces the hash with only the given keys.
def slice!(*keys)
replace(slice(*keys))
end
end
end
unless String.instance_methods.include? 'constantize'
String.class_eval do
def constantize
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
raise NameError, "#{self.inspect} is not a valid constant name!"
end
Object.module_eval("::#{$1}", __FILE__, __LINE__)
end
end
end
unless String.instance_methods.include? 'underscore'
String.class_eval do
def underscore
self.to_s.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
end
end

View file

@ -0,0 +1,50 @@
# borrowed from ActiveSupport::Deprecation
module WillPaginate
module Deprecation
def self.debug() @debug; end
def self.debug=(value) @debug = value; end
self.debug = false
# Choose the default warn behavior according to RAILS_ENV.
# Ignore deprecation warnings in production.
BEHAVIORS = {
'test' => Proc.new { |message, callstack|
$stderr.puts(message)
$stderr.puts callstack.join("\n ") if debug
},
'development' => Proc.new { |message, callstack|
logger = defined?(::RAILS_DEFAULT_LOGGER) ? ::RAILS_DEFAULT_LOGGER : Logger.new($stderr)
logger.warn message
logger.debug callstack.join("\n ") if debug
}
}
def self.warn(message, callstack = caller)
if behavior
message = 'WillPaginate: ' + message.strip.gsub(/\s+/, ' ')
behavior.call(message, callstack)
end
end
def self.default_behavior
if defined?(RAILS_ENV)
BEHAVIORS[RAILS_ENV.to_s]
else
BEHAVIORS['test']
end
end
# Behavior is a block that takes a message argument.
def self.behavior() @behavior; end
def self.behavior=(value) @behavior = value; end
self.behavior = default_behavior
def self.silence
old_behavior = self.behavior
self.behavior = nil
yield
ensure
self.behavior = old_behavior
end
end
end

View file

@ -0,0 +1,9 @@
require 'will_paginate/core_ext'
module WillPaginate
# Database logic for different ORMs
#
# See WillPaginate::Finders::Base
module Finders
end
end

View file

@ -0,0 +1,204 @@
require 'will_paginate/finders/base'
require 'active_record'
module WillPaginate::Finders
# = Paginating finders for ActiveRecord models
#
# WillPaginate adds +paginate+, +per_page+ and other methods to
# ActiveRecord::Base class methods and associations. It also hooks into
# +method_missing+ to intercept pagination calls to dynamic finders such as
# +paginate_by_user_id+ and translate them to ordinary finders
# (+find_all_by_user_id+ in this case).
#
# In short, paginating finders are equivalent to ActiveRecord finders; the
# only difference is that we start with "paginate" instead of "find" and
# that <tt>:page</tt> is required parameter:
#
# @posts = Post.paginate :all, :page => params[:page], :order => 'created_at DESC'
#
# In paginating finders, "all" is implicit. There is no sense in paginating
# a single record, right? So, you can drop the <tt>:all</tt> argument:
#
# Post.paginate(...) => Post.find :all
# Post.paginate_all_by_something => Post.find_all_by_something
# Post.paginate_by_something => Post.find_all_by_something
#
# == The importance of the <tt>:order</tt> parameter
#
# In ActiveRecord finders, <tt>:order</tt> parameter specifies columns for
# the <tt>ORDER BY</tt> clause in SQL. It is important to have it, since
# pagination only makes sense with ordered sets. Without the <tt>ORDER
# BY</tt> clause, databases aren't required to do consistent ordering when
# performing <tt>SELECT</tt> queries; this is especially true for
# PostgreSQL.
#
# Therefore, make sure you are doing ordering on a column that makes the
# most sense in the current context. Make that obvious to the user, also.
# For perfomance reasons you will also want to add an index to that column.
module ActiveRecord
include WillPaginate::Finders::Base
# Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
# based on the params otherwise used by paginating finds: +page+ and
# +per_page+.
#
# Example:
#
# @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
# :page => params[:page], :per_page => 3
#
# A query for counting rows will automatically be generated if you don't
# supply <tt>:total_entries</tt>. If you experience problems with this
# generated SQL, you might want to perform the count manually in your
# application.
#
def paginate_by_sql(sql, options)
WillPaginate::Collection.create(*wp_parse_options(options)) do |pager|
query = sanitize_sql(sql.dup)
original_query = query.dup
# add limit, offset
add_limit! query, :offset => pager.offset, :limit => pager.per_page
# perfom the find
pager.replace find_by_sql(query)
unless pager.total_entries
count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s]+$/mi, ''
count_query = "SELECT COUNT(*) FROM (#{count_query})"
unless ['oracle', 'oci'].include?(self.connection.adapter_name.downcase)
count_query << ' AS count_table'
end
# perform the count query
pager.total_entries = count_by_sql(count_query)
end
end
end
def respond_to?(method, include_priv = false) #:nodoc:
super(method.to_s.sub(/^paginate/, 'find'), include_priv)
end
protected
def method_missing_with_paginate(method, *args, &block) #:nodoc:
# did somebody tried to paginate? if not, let them be
unless method.to_s.index('paginate') == 0
return method_missing_without_paginate(method, *args, &block)
end
# paginate finders are really just find_* with limit and offset
finder = method.to_s.sub('paginate', 'find')
finder.sub!('find', 'find_all') if finder.index('find_by_') == 0
options = args.pop
raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys
options = options.dup
options[:finder] = finder
args << options
paginate(*args, &block)
end
def wp_query(options, pager, args, &block)
finder = (options.delete(:finder) || 'find').to_s
find_options = options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
if finder == 'find'
if Array === args.first and !pager.total_entries
pager.total_entries = args.first.size
end
args << :all if args.empty?
end
args << find_options
pager.replace send(finder, *args, &block)
unless pager.total_entries
# magic counting
pager.total_entries = wp_count(options, args, finder)
end
end
# Does the not-so-trivial job of finding out the total number of entries
# in the database. It relies on the ActiveRecord +count+ method.
def wp_count(options, args, finder)
# find out if we are in a model or an association proxy
klass = (@owner and @reflection) ? @reflection.klass : self
count_options = wp_parse_count_options(options, klass)
# we may have to scope ...
counter = Proc.new { count(count_options) }
count = if finder.index('find_') == 0 and klass.respond_to?(scoper = finder.sub('find', 'with'))
# scope_out adds a 'with_finder' method which acts like with_scope, if it's present
# then execute the count with the scoping provided by the with_finder
send(scoper, &counter)
elsif finder =~ /^find_(all_by|by)_([_a-zA-Z]\w*)$/
# extract conditions from calls like "paginate_by_foo_and_bar"
attribute_names = $2.split('_and_')
conditions = construct_attributes_from_arguments(attribute_names, args)
with_scope(:find => { :conditions => conditions }, &counter)
else
counter.call
end
count.respond_to?(:length) ? count.length : count
end
def wp_parse_count_options(options, klass)
excludees = [:count, :order, :limit, :offset, :readonly]
unless ::ActiveRecord::Calculations::CALCULATIONS_OPTIONS.include?(:from)
# :from parameter wasn't supported in count() before this change
excludees << :from
end
# Use :select from scope if it isn't already present.
options[:select] = scope(:find, :select) unless options[:select]
if options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
# Remove quoting and check for table_name.*-like statement.
if options[:select].gsub('`', '') =~ /\w+\.\*/
options[:select] = "DISTINCT #{klass.table_name}.#{klass.primary_key}"
end
else
excludees << :select
end
# count expects (almost) the same options as find
count_options = options.except *excludees
# merge the hash found in :count
# this allows you to specify :select, :order, or anything else just for the count query
count_options.update options[:count] if options[:count]
# forget about includes if they are irrelevant (Rails 2.1)
if count_options[:include] and
klass.private_methods.include?('references_eager_loaded_tables?') and
!klass.send(:references_eager_loaded_tables?, count_options)
count_options.delete :include
end
count_options
end
end
end
ActiveRecord::Base.class_eval do
extend WillPaginate::Finders::ActiveRecord
class << self
alias_method_chain :method_missing, :paginate
end
end
# support pagination on associations
a = ActiveRecord::Associations
returning([ a::AssociationCollection ]) { |classes|
# detect http://dev.rubyonrails.org/changeset/9230
unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation
classes << a::HasManyThroughAssociation
end
}.each do |klass|
klass.send :include, WillPaginate::Finders::ActiveRecord
klass.class_eval { alias_method_chain :method_missing, :paginate }
end

View file

@ -0,0 +1,170 @@
module WillPaginate
# This is a feature backported from Rails 2.1 because of its usefullness not only with will_paginate,
# but in other aspects when managing complex conditions that you want to be reusable.
module NamedScope
# All subclasses of ActiveRecord::Base have two named_scopes:
# * <tt>all</tt>, which is similar to a <tt>find(:all)</tt> query, and
# * <tt>scoped</tt>, which allows for the creation of anonymous scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
#
# These anonymous scopes tend to be useful when procedurally generating complex queries, where passing
# intermediate values (scopes) around as first-class objects is convenient.
def self.included(base)
base.class_eval do
extend ClassMethods
named_scope :scoped, lambda { |scope| scope }
end
end
module ClassMethods
def scopes
read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
end
# Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
# such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
#
# class Shirt < ActiveRecord::Base
# named_scope :red, :conditions => {:color => 'red'}
# named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
# end
#
# The above calls to <tt>named_scope</tt> define class methods <tt>Shirt.red</tt> and <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>,
# in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
#
# Unlike Shirt.find(...), however, the object returned by <tt>Shirt.red</tt> is not an Array; it resembles the association object
# constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.find(:first)</tt>, <tt>Shirt.red.count</tt>,
# <tt>Shirt.red.find(:all, :conditions => {:size => 'small'})</tt>. Also, just
# as with the association objects, name scopes acts like an Array, implementing Enumerable; <tt>Shirt.red.each(&block)</tt>,
# <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if Shirt.red really were an Array.
#
# These named scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
# Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
# for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
#
# All scopes are available as class methods on the ActiveRecord::Base descendent upon which the scopes were defined. But they are also available to
# <tt>has_many</tt> associations. If,
#
# class Person < ActiveRecord::Base
# has_many :shirts
# end
#
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
# only shirts.
#
# Named scopes can also be procedural.
#
# class Shirt < ActiveRecord::Base
# named_scope :colored, lambda { |color|
# { :conditions => { :color => color } }
# }
# end
#
# In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
#
# Named scopes can also have extensions, just as with <tt>has_many</tt> declarations:
#
# class Shirt < ActiveRecord::Base
# named_scope :red, :conditions => {:color => 'red'} do
# def dom_id
# 'red_shirts'
# end
# end
# end
#
#
# For testing complex named scopes, you can examine the scoping options using the
# <tt>proxy_options</tt> method on the proxy itself.
#
# class Shirt < ActiveRecord::Base
# named_scope :colored, lambda { |color|
# { :conditions => { :color => color } }
# }
# end
#
# expected_options = { :conditions => { :colored => 'red' } }
# assert_equal expected_options, Shirt.colored('red').proxy_options
def named_scope(name, options = {}, &block)
name = name.to_sym
scopes[name] = lambda do |parent_scope, *args|
Scope.new(parent_scope, case options
when Hash
options
when Proc
options.call(*args)
end, &block)
end
(class << self; self end).instance_eval do
define_method name do |*args|
scopes[name].call(self, *args)
end
end
end
end
class Scope
attr_reader :proxy_scope, :proxy_options
[].methods.each do |m|
unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|^find$|count|sum|average|maximum|minimum|paginate|first|last|empty\?|respond_to\?)/
delegate m, :to => :proxy_found
end
end
delegate :scopes, :with_scope, :to => :proxy_scope
def initialize(proxy_scope, options, &block)
[options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
extend Module.new(&block) if block_given?
@proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
end
def reload
load_found; self
end
def first(*args)
if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
proxy_found.first(*args)
else
find(:first, *args)
end
end
def last(*args)
if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
proxy_found.last(*args)
else
find(:last, *args)
end
end
def empty?
@found ? @found.empty? : count.zero?
end
def respond_to?(method, include_private = false)
super || @proxy_scope.respond_to?(method, include_private)
end
protected
def proxy_found
@found || load_found
end
private
def method_missing(method, *args, &block)
if scopes.include?(method)
scopes[method].call(self, *args)
else
with_scope :find => proxy_options do
proxy_scope.send(method, *args, &block)
end
end
end
def load_found
@found = find(:all)
end
end
end
end

View file

@ -0,0 +1,39 @@
## based on http://dev.rubyonrails.org/changeset/9084
ActiveRecord::Associations::AssociationProxy.class_eval do
protected
def with_scope(*args, &block)
@reflection.klass.send :with_scope, *args, &block
end
end
[ ActiveRecord::Associations::AssociationCollection,
ActiveRecord::Associations::HasManyThroughAssociation ].each do |klass|
klass.class_eval do
protected
alias :method_missing_without_scopes :method_missing_without_paginate
def method_missing_without_paginate(method, *args, &block)
if @reflection.klass.scopes.include?(method)
@reflection.klass.scopes[method].call(self, *args, &block)
else
method_missing_without_scopes(method, *args, &block)
end
end
end
end
# Rails 1.2.6
ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
protected
def method_missing(method, *args, &block)
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
super
elsif @reflection.klass.scopes.include?(method)
@reflection.klass.scopes[method].call(self, *args)
else
@reflection.klass.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
@reflection.klass.send(method, *args, &block)
end
end
end
end if ActiveRecord::Base.respond_to? :find_first

View file

@ -0,0 +1,48 @@
require 'will_paginate/finders/base'
require 'active_resource'
module WillPaginate::Finders
# Paginate your ActiveResource models.
#
# @posts = Post.paginate :all, :params => { :page => params[:page], :order => 'created_at DESC' }
module ActiveResource
include WillPaginate::Finders::Base
protected
def wp_query(options, pager, args, &block)
unless args.empty? or args.first == :all
raise ArgumentError, "finder arguments other than :all are not supported for pagination (#{args.inspect} given)"
end
params = (options[:params] ||= {})
params[:page] = pager.current_page
params[:per_page] = pager.per_page
pager.replace find_every(options, &block)
end
# Takes the format that Hash.from_xml produces out of an unknown type
# (produced by WillPaginate::Collection#to_xml_with_collection_type),
# parses it into a WillPaginate::Collection,
# and forwards the result to the former +instantiate_collection+ method.
# It only does this for hashes that have a :type => "collection".
def instantiate_collection_with_collection(collection, prefix_options = {})
if collection.is_a?(Hash) && collection["type"] == "collection"
collectables = collection.values.find{ |c| c.is_a?(Hash) || c.is_a?(Array) }
collectables = [collectables].compact unless collectables.kind_of?(Array)
instantiated_collection = WillPaginate::Collection.create(collection["current_page"], collection["per_page"], collection["total_entries"]) do |pager|
pager.replace instantiate_collection_without_collection(collectables, prefix_options)
end
else
instantiate_collection_without_collection(collection, prefix_options)
end
end
end
end
ActiveResource::Base.class_eval do
extend WillPaginate::Finders::ActiveResource
class << self
# alias_method_chain :instantiate_collection, :collection
end
end

View file

@ -0,0 +1,80 @@
require 'will_paginate/core_ext'
module WillPaginate
module Finders
# Database-agnostic finder logic
module Base
def per_page
@per_page ||= 30
end
def per_page=(limit)
@per_page = limit.to_i
end
# This is the main paginating finder.
#
# == Special parameters for paginating finders
# * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
# * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 30 if not overridden)
# * <tt>:total_entries</tt> -- use only if you manually count total entries
# * <tt>:count</tt> -- additional options that are passed on to +count+
# * <tt>:finder</tt> -- name of the finder method to use (default: "find")
#
# All other options (+conditions+, +order+, ...) are forwarded to +find+
# and +count+ calls.
def paginate(*args, &block)
options = args.pop
page, per_page, total_entries = wp_parse_options(options)
WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
query_options = options.except :page, :per_page, :total_entries
wp_query(query_options, pager, args, &block)
end
end
# Iterates through all records by loading one page at a time. This is useful
# for migrations or any other use case where you don't want to load all the
# records in memory at once.
#
# It uses +paginate+ internally; therefore it accepts all of its options.
# You can specify a starting page with <tt>:page</tt> (default is 1). Default
# <tt>:order</tt> is <tt>"id"</tt>, override if necessary.
#
# {Jamis Buck describes this}[http://weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord]
# and also uses a more efficient way for MySQL.
def paginated_each(options = {}, &block)
options = { :order => 'id', :page => 1 }.merge options
options[:page] = options[:page].to_i
options[:total_entries] = 0 # skip the individual count queries
total = 0
begin
collection = paginate(options)
total += collection.each(&block).size
options[:page] += 1
end until collection.size < collection.per_page
total
end
protected
def wp_parse_options(options) #:nodoc:
raise ArgumentError, 'parameter hash expected' unless Hash === options
raise ArgumentError, ':page parameter required' unless options.key? :page
if options[:count] and options[:total_entries]
raise ArgumentError, ':count and :total_entries are mutually exclusive'
end
page = options[:page] || 1
per_page = options[:per_page] || self.per_page
total = options[:total_entries]
return [page, per_page, total]
end
end
end
end

View file

@ -0,0 +1,30 @@
require 'will_paginate/finders/base'
require 'dm-core'
module WillPaginate::Finders
module DataMapper
include WillPaginate::Finders::Base
protected
def wp_query(options, pager, args, &block)
find_options = options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
pager.replace all(find_options, &block)
unless pager.total_entries
pager.total_entries = wp_count(options)
end
end
def wp_count(options)
count_options = options.except(:count, :order)
# merge the hash found in :count
count_options.update options[:count] if options[:count]
count_options.empty?? count() : count(count_options)
end
end
end
DataMapper::Model.send(:include, WillPaginate::Finders::DataMapper)

View file

@ -0,0 +1,9 @@
module WillPaginate #:nodoc:
module VERSION #:nodoc:
MAJOR = 2
MINOR = 5
TINY = 0
STRING = [MAJOR, MINOR, TINY].join('.')
end
end

View file

@ -0,0 +1,39 @@
require 'will_paginate/deprecation'
module WillPaginate
# = Will Paginate view helpers
#
# Currently there is only one view helper: +will_paginate+. It renders the
# pagination links for the given collection. The helper itself is lightweight
# and serves only as a wrapper around link renderer instantiation; the
# renderer then does all the hard work of generating the HTML.
#
# == Global options for helpers
#
# Options for pagination helpers are optional and get their default values from the
# WillPaginate::ViewHelpers.pagination_options hash. You can write to this hash to
# override default options on the global level:
#
# WillPaginate::ViewHelpers.pagination_options[:previous_label] = 'Previous page'
#
# By putting this into your environment.rb you can easily translate link texts to previous
# and next pages, as well as override some other defaults to your liking.
module ViewHelpers
def self.pagination_options() @pagination_options; end
def self.pagination_options=(value) @pagination_options = value; end
self.pagination_options = {
:class => 'pagination',
:previous_label => '&laquo; Previous',
:next_label => 'Next &raquo;',
:inner_window => 4, # links around the current page
:outer_window => 1, # links around beginning and end
:separator => ' ', # single space is friendly to spiders and non-graphic browsers
:param_name => :page,
:params => nil,
:renderer => 'WillPaginate::ViewHelpers::LinkRenderer',
:page_links => true,
:container => true
}
end
end

View file

@ -0,0 +1,82 @@
require 'will_paginate/view_helpers/base'
require 'action_view'
require 'will_paginate/view_helpers/link_renderer'
module WillPaginate
module ViewHelpers
# ActionView helpers for Rails integration
module ActionView
include WillPaginate::ViewHelpers::Base
def will_paginate(collection = nil, options = {})
options, collection = collection, nil if collection.is_a? Hash
collection ||= infer_collection_from_controller
super(collection, options.symbolize_keys)
end
def page_entries_info(collection = nil, options = {})
options, collection = collection, nil if collection.is_a? Hash
collection ||= infer_collection_from_controller
super(collection, options.symbolize_keys)
end
# Wrapper for rendering pagination links at both top and bottom of a block
# of content.
#
# <% paginated_section @posts do %>
# <ol id="posts">
# <% for post in @posts %>
# <li> ... </li>
# <% end %>
# </ol>
# <% end %>
#
# will result in:
#
# <div class="pagination"> ... </div>
# <ol id="posts">
# ...
# </ol>
# <div class="pagination"> ... </div>
#
# Arguments are passed to a <tt>will_paginate</tt> call, so the same options
# apply. Don't use the <tt>:id</tt> option; otherwise you'll finish with two
# blocks of pagination links sharing the same ID (which is invalid HTML).
def paginated_section(*args, &block)
pagination = will_paginate(*args).to_s
content = pagination + capture(&block) + pagination
concat content, block.binding
end
protected
def infer_collection_from_controller
collection_name = "@#{controller.controller_name}"
collection = instance_variable_get(collection_name)
raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
"forget to pass the collection object for will_paginate?" if collection.nil?
collection
end
end
end
end
ActionView::Base.send :include, WillPaginate::ViewHelpers::ActionView
if defined?(ActionController::Base) and ActionController::Base.respond_to? :rescue_responses
ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found
end
WillPaginate::ViewHelpers::LinkRenderer.class_eval do
protected
def default_url_params
{ :escape => false }
end
def generate_url(params)
@template.url_for(params)
end
end

View file

@ -0,0 +1,137 @@
require 'will_paginate/core_ext'
require 'will_paginate/view_helpers'
module WillPaginate
module ViewHelpers
module Base
# Renders Digg/Flickr-style pagination for a WillPaginate::Collection
# object. Nil is returned if there is only one page in total; no point in
# rendering the pagination in that case...
#
# ==== Options
# * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
# * <tt>:previous_label</tt> -- default: "« Previous"
# * <tt>:next_label</tt> -- default: "Next »"
# * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
# * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
# * <tt>:separator</tt> -- string separator for page HTML elements (default: single space)
# * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
# * <tt>:params</tt> -- additional parameters when generating pagination links
# (eg. <tt>:controller => "foo", :action => nil</tt>)
# * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default:
# <tt>WillPaginate::LinkRenderer</tt>)
# * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
# * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
# false only when you are rendering your own pagination markup (default: true)
# * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID
# automatically generated from the class name of objects in collection: for example, paginating
# ArticleComment models would yield an ID of "article_comments_pagination".
#
# All options beside listed ones are passed as HTML attributes to the container
# element for pagination links (the DIV). For example:
#
# <%= will_paginate @posts, :id => 'wp_posts' %>
#
# ... will result in:
#
# <div class="pagination" id="wp_posts"> ... </div>
#
# ==== Using the helper without arguments
# If the helper is called without passing in the collection object, it will
# try to read from the instance variable inferred by the controller name.
# For example, calling +will_paginate+ while the current controller is
# PostsController will result in trying to read from the <tt>@posts</tt>
# variable. Example:
#
# <%= will_paginate :id => true %>
#
# ... will result in <tt>@post</tt> collection getting paginated:
#
# <div class="pagination" id="posts_pagination"> ... </div>
#
def will_paginate(collection, options = {})
# early exit if there is nothing to render
return nil unless collection.total_pages > 1
options = WillPaginate::ViewHelpers.pagination_options.merge(options)
if options[:prev_label]
WillPaginate::Deprecation::warn(":prev_label view parameter is now :previous_label; the old name has been deprecated.")
options[:previous_label] = options.delete(:prev_label)
end
# get the renderer instance
renderer = case options[:renderer]
when String
options[:renderer].constantize.new
when Class
options[:renderer].new
else
options[:renderer]
end
# render HTML for pagination
renderer.prepare collection, options, self
renderer.to_html
end
# Renders a helpful message with numbers of displayed vs. total entries.
# You can use this as a blueprint for your own, similar helpers.
#
# <%= page_entries_info @posts %>
# #-> Displaying posts 6 - 10 of 26 in total
#
# By default, the message will use the humanized class name of objects
# in collection: for instance, "project types" for ProjectType models.
# Override this to your liking with the <tt>:entry_name</tt> parameter:
#
# <%= page_entries_info @posts, :entry_name => 'item' %>
# #-> Displaying items 6 - 10 of 26 in total
#
# Entry name is entered in singular and pluralized with
# <tt>String#pluralize</tt> method from ActiveSupport. If it isn't
# loaded, specify plural with <tt>:plural_name</tt> parameter:
#
# <%= page_entries_info @posts, :entry_name => 'item', :plural_name => 'items' %>
#
# By default, this method produces HTML output. You can trigger plain
# text output by passing <tt>:html => false</tt> in options.
def page_entries_info(collection, options = {})
entry_name = options[:entry_name] || (collection.empty?? 'entry' :
collection.first.class.name.underscore.gsub('_', ' '))
plural_name = if options[:plural_name]
options[:plural_name]
elsif entry_name == 'entry'
plural_name = 'entries'
elsif entry_name.respond_to? :pluralize
plural_name = entry_name.pluralize
else
entry_name + 's'
end
unless options[:html] == false
b = '<b>'
eb = '</b>'
sp = '&nbsp;'
else
b = eb = ''
sp = ' '
end
if collection.total_pages < 2
case collection.size
when 0; "No #{plural_name} found"
when 1; "Displaying #{b}1#{eb} #{entry_name}"
else; "Displaying #{b}all #{collection.size}#{eb} #{plural_name}"
end
else
%{Displaying #{plural_name} #{b}%d#{sp}-#{sp}%d#{eb} of #{b}%d#{eb} in total} % [
collection.offset + 1,
collection.offset + collection.length,
collection.total_entries
]
end
end
end
end
end

View file

@ -0,0 +1,177 @@
require 'cgi'
require 'will_paginate/core_ext'
require 'will_paginate/view_helpers/link_renderer_base'
module WillPaginate
module ViewHelpers
# This class does the heavy lifting of actually building the pagination
# links. It is used by +will_paginate+ helper internally.
class LinkRenderer < LinkRendererBase
# * +collection+ is a WillPaginate::Collection instance or any other object
# that conforms to that API
# * +options+ are forwarded from +will_paginate+ view helper
# * +template+ is the reference to the template being rendered
def prepare(collection, options, template)
super(collection, options)
@template = template
@container_attributes = @base_url_params = nil
end
# Process it! This method returns the complete HTML string which contains
# pagination links. Feel free to subclass LinkRenderer and change this
# method as you see fit.
def to_html
html = pagination.map do |item|
item.is_a?(Fixnum) ?
page_number(item) :
send(item)
end.join(@options[:separator])
@options[:container] ? html_container(html) : html
end
# Returns the subset of +options+ this instance was initialized with that
# represent HTML attributes for the container element of pagination links.
def container_attributes
@container_attributes ||= begin
attributes = @options.except *(WillPaginate::ViewHelpers.pagination_options.keys - [:class])
# pagination of Post models will have the ID of "posts_pagination"
if @options[:container] and @options[:id] === true
attributes[:id] = @collection.first.class.name.underscore.pluralize + '_pagination'
end
attributes
end
end
protected
def page_number(page)
unless page == current_page
link(page, page, :rel => rel_value(page))
else
tag(:em, page)
end
end
def gap
'<span class="gap">&hellip;</span>'
end
def previous_page
previous_or_next_page(@collection.previous_page, @options[:previous_label], 'previous_page')
end
def next_page
previous_or_next_page(@collection.next_page, @options[:next_label], 'next_page')
end
def previous_or_next_page(page, text, classname)
if page
link(text, page, :class => classname)
else
tag(:span, text, :class => classname + ' disabled')
end
end
def html_container(html)
tag(:div, html, container_attributes)
end
# Returns URL params for +page_link_or_span+, taking the current GET params
# and <tt>:params</tt> option into account.
def url(page)
@base_url_params ||= begin
url_params = base_url_params
merge_optional_params(url_params)
url_params
end
url_params = @base_url_params.dup
add_current_page_param(url_params, page)
generate_url(url_params)
end
def default_url_params
{ }
end
def base_url_params
url_params = default_url_params
# page links should preserve GET parameters
symbolized_update(url_params, @template.params) if get_request?
url_params
end
def merge_optional_params(url_params)
symbolized_update(url_params, @options[:params]) if @options[:params]
end
def add_current_page_param(url_params, page)
unless param_name.index(/[^\w-]/)
url_params[param_name.to_sym] = page
else
page_param = (defined?(CGIMethods) ? CGIMethods : ActionController::AbstractRequest).
parse_query_parameters(param_name + '=' + page.to_s)
symbolized_update(url_params, page_param)
end
end
def get_request?
@template.request.get?
end
def generate_url(params)
@template.url(params)
end
private
def link(text, target, attributes = {})
if target.is_a? Fixnum
attributes[:rel] = rel_value(target)
target = url(target)
end
attributes[:href] = target
tag(:a, text, attributes)
end
def tag(name, value, attributes = {})
string_attributes = attributes.inject('') do |attrs, pair|
unless pair.last.nil?
attrs << %( #{pair.first}="#{CGI::escapeHTML(pair.last.to_s)}")
end
attrs
end
"<#{name}#{string_attributes}>#{value}</#{name}>"
end
def rel_value(page)
case page
when @collection.previous_page; 'prev' + (page == 1 ? ' start' : '')
when @collection.next_page; 'next'
when 1; 'start'
end
end
def symbolized_update(target, other)
other.each do |key, value|
key = key.to_sym
existing = target[key]
if value.is_a?(Hash)
target[key] = existing = {} if existing.nil?
if existing.is_a?(Hash)
symbolized_update(existing, value)
return
end
end
target[key] = value
end
end
end
end
end

View file

@ -0,0 +1,76 @@
require 'will_paginate/view_helpers'
module WillPaginate
module ViewHelpers
# This class does the heavy lifting of actually building the pagination
# links. It is used by +will_paginate+ helper internally.
class LinkRendererBase
# * +collection+ is a WillPaginate::Collection instance or any other object
# that conforms to that API
# * +options+ are forwarded from +will_paginate+ view helper
def prepare(collection, options)
@collection = collection
@options = options
# reset values in case we're re-using this instance
@total_pages = @param_name = nil
end
def pagination
items = @options[:page_links] ? windowed_page_numbers : []
items.unshift :previous_page
items.push :next_page
end
protected
# Calculates visible page numbers using the <tt>:inner_window</tt> and
# <tt>:outer_window</tt> options.
def windowed_page_numbers
inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
window_from = current_page - inner_window
window_to = current_page + inner_window
# adjust lower or upper limit if other is out of bounds
if window_to > total_pages
window_from -= window_to - total_pages
window_to = total_pages
end
if window_from < 1
window_to += 1 - window_from
window_from = 1
window_to = total_pages if window_to > total_pages
end
visible = (1..total_pages).to_a
left_gap = (2 + outer_window)...window_from
right_gap = (window_to + 1)...(total_pages - outer_window)
# replace page numbers that shouldn't be visible with `:gap`
[right_gap, left_gap].each do |gap|
if (gap.last - gap.first) > 1
visible -= gap.to_a
visible.insert(gap.first - 1, :gap)
end
end
visible
end
private
def current_page
@collection.current_page
end
def total_pages
@collection.total_pages
end
def param_name
@param_name ||= @options[:param_name].to_s
end
end
end
end

View file

@ -0,0 +1,147 @@
require 'will_paginate/array'
require 'spec_helper'
describe WillPaginate::Collection do
before :all do
@simple = ('a'..'e').to_a
end
it "should be a subset of original collection" do
@simple.paginate(:page => 1, :per_page => 3).should == %w( a b c )
end
it "can be shorter than per_page if on last page" do
@simple.paginate(:page => 2, :per_page => 3).should == %w( d e )
end
it "should include whole collection if per_page permits" do
@simple.paginate(:page => 1, :per_page => 5).should == @simple
end
it "should be empty if out of bounds" do
@simple.paginate(:page => 2, :per_page => 5).should be_empty
end
it "should default to 1 as current page and 30 per-page" do
result = (1..50).to_a.paginate
result.current_page.should == 1
result.size.should == 30
end
describe "old API" do
it "should fail with numeric params" do
Proc.new { [].paginate(2) }.should raise_error(ArgumentError)
Proc.new { [].paginate(2, 10) }.should raise_error(ArgumentError)
end
it "should fail with both options and numeric param" do
Proc.new { [].paginate({}, 5) }.should raise_error(ArgumentError)
end
end
it "should give total_entries precedence over actual size" do
%w(a b c).paginate(:total_entries => 5).total_entries.should == 5
end
it "should be an augmented Array" do
entries = %w(a b c)
collection = create(2, 3, 10) do |pager|
pager.replace(entries).should == entries
end
collection.should == entries
for method in %w(total_pages each offset size current_page per_page total_entries)
collection.should respond_to(method)
end
collection.should be_kind_of(Array)
collection.entries.should be_instance_of(Array)
# TODO: move to another expectation:
collection.offset.should == 3
collection.total_pages.should == 4
collection.should_not be_out_of_bounds
end
describe "previous/next pages" do
it "should have previous_page nil when on first page" do
collection = create(1, 1, 3)
collection.previous_page.should be_nil
collection.next_page.should == 2
end
it "should have both prev/next pages" do
collection = create(2, 1, 3)
collection.previous_page.should == 1
collection.next_page.should == 3
end
it "should have next_page nil when on last page" do
collection = create(3, 1, 3)
collection.previous_page.should == 2
collection.next_page.should be_nil
end
end
it "should show out of bounds when page number is too high" do
create(2, 3, 2).should be_out_of_bounds
end
it "should not show out of bounds when inside collection" do
create(1, 3, 2).should_not be_out_of_bounds
end
describe "guessing total count" do
it "can guess when collection is shorter than limit" do
collection = create { |p| p.replace array }
collection.total_entries.should == 8
end
it "should allow explicit total count to override guessed" do
collection = create(2, 5, 10) { |p| p.replace array }
collection.total_entries.should == 10
end
it "should not be able to guess when collection is same as limit" do
collection = create { |p| p.replace array(5) }
collection.total_entries.should be_nil
end
it "should not be able to guess when collection is empty" do
collection = create { |p| p.replace array(0) }
collection.total_entries.should be_nil
end
it "should be able to guess when collection is empty and this is the first page" do
collection = create(1) { |p| p.replace array(0) }
collection.total_entries.should == 0
end
end
it "should raise WillPaginate::InvalidPage on invalid input" do
for bad_input in [0, -1, nil, '', 'Schnitzel']
Proc.new { create bad_input }.should raise_error(WillPaginate::InvalidPage)
end
end
it "should raise Argument error on invalid per_page setting" do
Proc.new { create(1, -1) }.should raise_error(ArgumentError)
end
it "should not respond to page_count anymore" do
Proc.new { create.page_count }.should raise_error(NoMethodError)
end
private
def create(page = 2, limit = 5, total = nil, &block)
if block_given?
WillPaginate::Collection.create(page, limit, total, &block)
else
WillPaginate::Collection.new(page, limit, total)
end
end
def array(size = 3)
Array.new(size)
end
end

8
vendor/plugins/will_paginate/spec/console vendored Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env ruby
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
libs = []
libs << 'irb/completion'
libs << 'console_fixtures'
exec "#{irb} -Ilib:spec#{libs.map{ |l| " -r #{l}" }.join} --simple-prompt"

View file

@ -0,0 +1,8 @@
require 'will_paginate/finders/active_record'
require 'finders/activerecord_test_connector'
ActiverecordTestConnector.setup
# load all fixtures
Fixtures.create_fixtures(ActiverecordTestConnector::FIXTURES_PATH, ActiveRecord::Base.connection.tables)

View file

@ -0,0 +1,22 @@
sqlite3:
database: ":memory:"
adapter: sqlite3
timeout: 500
sqlite2:
database: ":memory:"
adapter: sqlite2
mysql:
adapter: mysql
username: rails
password: mislav
encoding: utf8
database: will_paginate_unittest
postgres:
adapter: postgresql
username: mislav
password: mislav
database: will_paginate_unittest
min_messages: warning

View file

@ -0,0 +1,460 @@
require 'spec_helper'
require 'will_paginate/finders/active_record'
require File.dirname(__FILE__) + '/activerecord_test_connector'
require 'will_paginate'
WillPaginate::enable_named_scope
class ArProject < ActiveRecord::Base
def self.column_names
["id"]
end
named_scope :distinct, :select => "DISTINCT #{table_name}.*"
end
ActiverecordTestConnector.setup
describe WillPaginate::Finders::ActiveRecord do
extend ActiverecordTestConnector::FixtureSetup
it "should integrate with ActiveRecord::Base" do
ActiveRecord::Base.should respond_to(:paginate)
end
it "should paginate" do
ArProject.expects(:find).with(:all, { :limit => 5, :offset => 0 }).returns([])
ArProject.paginate(:page => 1, :per_page => 5)
end
it "should respond to paginate_by_sql" do
ArProject.should respond_to(:paginate_by_sql)
end
it "should support explicit :all argument" do
ArProject.expects(:find).with(:all, instance_of(Hash)).returns([])
ArProject.paginate(:all, :page => nil)
end
it "should put implicit all in dynamic finders" do
ArProject.expects(:find_all_by_foo).returns([])
ArProject.expects(:count).returns(0)
ArProject.paginate_by_foo :page => 2
end
it "should leave extra parameters intact" do
ArProject.expects(:find).with(:all, {:foo => 'bar', :limit => 4, :offset => 0 }).returns(Array.new(5))
ArProject.expects(:count).with({:foo => 'bar'}).returns(1)
ArProject.paginate :foo => 'bar', :page => 1, :per_page => 4
end
describe "counting" do
it "should ignore nil in :count parameter" do
ArProject.expects(:find).returns([])
lambda { ArProject.paginate :page => nil, :count => nil }.should_not raise_error
end
it "should guess the total count" do
ArProject.expects(:find).returns(Array.new(2))
ArProject.expects(:count).never
result = ArProject.paginate :page => 2, :per_page => 4
result.total_entries.should == 6
end
it "should guess that there are no records" do
ArProject.expects(:find).returns([])
ArProject.expects(:count).never
result = ArProject.paginate :page => 1, :per_page => 4
result.total_entries.should == 0
end
end
it "should not ignore :select parameter when it says DISTINCT" do
ArProject.stubs(:find).returns([])
ArProject.expects(:count).with(:select => 'DISTINCT salary').returns(0)
ArProject.paginate :select => 'DISTINCT salary', :page => 2
end
it "should count with scoped select when :select => DISTINCT" do
ArProject.stubs(:find).returns([])
ArProject.expects(:count).with(:select => 'DISTINCT ar_projects.id').returns(0)
ArProject.distinct.paginate :page => 2
end
it "should use :with_foo for scope-out compatibility" do
ArProject.expects(:find_best).returns(Array.new(5))
ArProject.expects(:with_best).returns(1)
ArProject.paginate_best :page => 1, :per_page => 4
end
describe "paginate_by_sql" do
it "should paginate" do
ArProject.expects(:find_by_sql).with(regexp_matches(/sql LIMIT 3(,| OFFSET) 3/)).returns([])
ArProject.expects(:count_by_sql).with('SELECT COUNT(*) FROM (sql) AS count_table').returns(0)
ArProject.paginate_by_sql 'sql', :page => 2, :per_page => 3
end
it "should respect total_entrier setting" do
ArProject.expects(:find_by_sql).returns([])
ArProject.expects(:count_by_sql).never
entries = ArProject.paginate_by_sql 'sql', :page => 1, :total_entries => 999
entries.total_entries.should == 999
end
it "should strip the order when counting" do
ArProject.expects(:find_by_sql).returns([])
ArProject.expects(:count_by_sql).with("SELECT COUNT(*) FROM (sql\n ) AS count_table").returns(0)
ArProject.paginate_by_sql "sql\n ORDER\nby foo, bar, `baz` ASC", :page => 2
end
it "shouldn't change the original query string" do
query = 'SQL QUERY'
original_query = query.dup
ArProject.expects(:find_by_sql).returns([])
ArProject.paginate_by_sql(query, :page => 1)
query.should == original_query
end
end
# TODO: counts would still be wrong!
it "should be able to paginate custom finders" do
# acts_as_taggable defines find_tagged_with(tag, options)
ArProject.expects(:find_tagged_with).with('will_paginate', :offset => 5, :limit => 5).returns([])
ArProject.expects(:count).with({}).returns(0)
ArProject.paginate_tagged_with 'will_paginate', :page => 2, :per_page => 5
end
it "should not skip count when given an array argument to a finder" do
ids = (1..8).to_a
ArProject.expects(:find_all_by_id).returns([])
ArProject.expects(:count).returns(0)
ArProject.paginate_by_id(ids, :per_page => 3, :page => 2, :order => 'id')
end
# Is this Rails 2.0? Find out by testing find_all which was removed in [6998]
unless ActiveRecord::Base.respond_to? :find_all
it "should paginate array of IDs" do
# AR finders also accept arrays of IDs
# (this was broken in Rails before [6912])
lambda {
result = Developer.paginate((1..8).to_a, :per_page => 3, :page => 2, :order => 'id')
result.map(&:id).should == (4..6).to_a
result.total_entries.should == 8
}.should run_queries(1)
end
end
it "doesn't mangle options" do
ArProject.expects(:find).returns([])
options = { :page => 1 }
options.expects(:delete).never
options_before = options.dup
ArProject.paginate(options)
options.should == options_before
end
if ::ActiveRecord::Calculations::CALCULATIONS_OPTIONS.include?(:from)
# for ActiveRecord 2.1 and newer
it "keeps the :from parameter in count" do
ArProject.expects(:find).returns([1])
ArProject.expects(:count).with {|options| options.key?(:from) }.returns(0)
ArProject.paginate(:page => 2, :per_page => 1, :from => 'projects')
end
else
it "excludes :from parameter from count" do
ArProject.expects(:find).returns([1])
ArProject.expects(:count).with {|options| !options.key?(:from) }.returns(0)
ArProject.paginate(:page => 2, :per_page => 1, :from => 'projects')
end
end
if ActiverecordTestConnector.able_to_connect
fixtures :topics, :replies, :users, :projects, :developers_projects
it "should get first page of Topics with a single query" do
lambda {
result = Topic.paginate :page => nil
result.current_page.should == 1
result.total_pages.should == 1
result.size.should == 4
}.should run_queries(1)
end
it "should get second (inexistent) page of Topics, requiring 2 queries" do
lambda {
result = Topic.paginate :page => 2
result.total_pages.should == 1
result.should be_empty
}.should run_queries(2)
end
it "should paginate with :order" do
result = Topic.paginate :page => 1, :order => 'created_at DESC'
result.should == topics(:futurama, :harvey_birdman, :rails, :ar).reverse
result.total_pages.should == 1
end
it "should paginate with :conditions" do
result = Topic.paginate :page => 1, :conditions => ["created_at > ?", 30.minutes.ago]
result.should == topics(:rails, :ar)
result.total_pages.should == 1
end
it "should paginate with :include and :conditions" do
result = Topic.paginate \
:page => 1,
:include => :replies,
:conditions => "replies.content LIKE 'Bird%' ",
:per_page => 10
expected = Topic.find :all,
:include => 'replies',
:conditions => "replies.content LIKE 'Bird%' ",
:limit => 10
result.should == expected
result.total_entries.should == 1
end
it "should paginate with :include and :order" do
result = nil
lambda {
result = Topic.paginate \
:page => 1,
:include => :replies,
:order => 'replies.created_at asc, topics.created_at asc',
:per_page => 10
}.should run_queries(2)
expected = Topic.find :all,
:include => 'replies',
:order => 'replies.created_at asc, topics.created_at asc',
:limit => 10
result.should == expected
result.total_entries.should == 4
end
# detect ActiveRecord 2.1
if ActiveRecord::Base.private_methods.include?('references_eager_loaded_tables?')
it "should remove :include for count" do
Developer.expects(:find).returns([1])
Developer.expects(:count).with({}).returns(0)
Developer.paginate :page => 1, :per_page => 1, :include => :projects
end
it "should keep :include for count when they are referenced in :conditions" do
Developer.expects(:find).returns([1])
Developer.expects(:count).with({ :include => :projects, :conditions => 'projects.id > 2' }).returns(0)
Developer.paginate :page => 1, :per_page => 1,
:include => :projects, :conditions => 'projects.id > 2'
end
end
describe "associations" do
it "should paginate with include" do
project = projects(:active_record)
result = project.topics.paginate \
:page => 1,
:include => :replies,
:conditions => ["replies.content LIKE ?", 'Nice%'],
:per_page => 10
expected = Topic.find :all,
:include => 'replies',
:conditions => ["project_id = #{project.id} AND replies.content LIKE ?", 'Nice%'],
:limit => 10
result.should == expected
end
it "should paginate" do
dhh = users(:david)
expected_name_ordered = projects(:action_controller, :active_record)
expected_id_ordered = projects(:active_record, :action_controller)
lambda {
# with association-specified order
result = dhh.projects.paginate(:page => 1)
result.should == expected_name_ordered
result.total_entries.should == 2
}.should run_queries(2)
# with explicit order
result = dhh.projects.paginate(:page => 1, :order => 'projects.id')
result.should == expected_id_ordered
result.total_entries.should == 2
lambda {
dhh.projects.find(:all, :order => 'projects.id', :limit => 4)
}.should_not raise_error
result = dhh.projects.paginate(:page => 1, :order => 'projects.id', :per_page => 4)
result.should == expected_id_ordered
# has_many with implicit order
topic = Topic.find(1)
expected = replies(:spam, :witty_retort)
# FIXME: wow, this is ugly
topic.replies.paginate(:page => 1).map(&:id).sort.should == expected.map(&:id).sort
topic.replies.paginate(:page => 1, :order => 'replies.id ASC').should == expected.reverse
end
it "should paginate through association extension" do
project = Project.find(:first)
expected = [replies(:brave)]
lambda {
result = project.replies.paginate_recent :page => 1
result.should == expected
}.should run_queries(1)
end
end
it "should paginate with joins" do
result = nil
join_sql = 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id'
lambda {
result = Developer.paginate :page => 1, :joins => join_sql, :conditions => 'project_id = 1'
result.size.should == 2
developer_names = result.map(&:name)
developer_names.should include('David')
developer_names.should include('Jamis')
}.should run_queries(1)
lambda {
expected = result.to_a
result = Developer.paginate :page => 1, :joins => join_sql,
:conditions => 'project_id = 1', :count => { :select => "users.id" }
result.should == expected
result.total_entries.should == 2
}.should run_queries(1)
end
it "should paginate with group" do
result = nil
lambda {
result = Developer.paginate :page => 1, :per_page => 10,
:group => 'salary', :select => 'salary', :order => 'salary'
}.should run_queries(1)
expected = users(:david, :jamis, :dev_10, :poor_jamis).map(&:salary).sort
result.map(&:salary).should == expected
end
it "should paginate with dynamic finder" do
expected = replies(:witty_retort, :spam)
Reply.paginate_by_topic_id(1, :page => 1).should == expected
result = Developer.paginate :conditions => { :salary => 100000 }, :page => 1, :per_page => 5
result.total_entries.should == 8
Developer.paginate_by_salary(100000, :page => 1, :per_page => 5).should == result
end
it "should paginate with dynamic finder and conditions" do
result = Developer.paginate_by_salary(100000, :page => 1, :conditions => ['id > ?', 6])
result.total_entries.should == 4
result.map(&:id).should == (7..10).to_a
end
it "should raise error when dynamic finder is not recognized" do
lambda {
Developer.paginate_by_inexistent_attribute 100000, :page => 1
}.should raise_error(NoMethodError)
end
it "should paginate with_scope" do
result = Developer.with_poor_ones { Developer.paginate :page => 1 }
result.size.should == 2
result.total_entries.should == 2
end
describe "named_scope" do
it "should paginate" do
result = Developer.poor.paginate :page => 1, :per_page => 1
result.size.should == 1
result.total_entries.should == 2
end
it "should paginate on habtm association" do
project = projects(:active_record)
lambda {
result = project.developers.poor.paginate :page => 1, :per_page => 1
result.size.should == 1
result.total_entries.should == 1
}.should run_queries(2)
end
it "should paginate on hmt association" do
project = projects(:active_record)
expected = [replies(:brave)]
lambda {
result = project.replies.recent.paginate :page => 1, :per_page => 1
result.should == expected
result.total_entries.should == 1
}.should run_queries(2)
end
it "should paginate on has_many association" do
project = projects(:active_record)
expected = [topics(:ar)]
lambda {
result = project.topics.mentions_activerecord.paginate :page => 1, :per_page => 1
result.should == expected
result.total_entries.should == 1
}.should run_queries(2)
end
end
it "should paginate with :readonly option" do
lambda { Developer.paginate :readonly => true, :page => 1 }.should_not raise_error
end
end
protected
def run_queries(num)
QueryCountMatcher.new(num)
end
end
class QueryCountMatcher
def initialize(num)
@queries = num
@old_query_count = $query_count
end
def matches?(block)
block.call
@queries_run = $query_count - @old_query_count
@queries == @queries_run
end
def failure_message
"expected #{@queries} queries, got #{@queries_run}"
end
def negative_failure_message
"expected query count not to be #{$queries}"
end
end

View file

@ -0,0 +1,52 @@
require 'spec_helper'
require 'will_paginate/finders/active_resource'
require 'active_resource/http_mock'
class AresProject < ActiveResource::Base
self.site = 'http://localhost:4000'
end
describe WillPaginate::Finders::ActiveResource do
before :all do
# ActiveResource::HttpMock.respond_to do |mock|
# mock.get "/ares_projects.xml?page=1&per_page=5", {}, [].to_xml
# end
end
it "should integrate with ActiveResource::Base" do
ActiveResource::Base.should respond_to(:paginate)
end
it "should error when no parameters for #paginate" do
lambda { AresProject.paginate }.should raise_error(ArgumentError)
end
it "should paginate" do
AresProject.expects(:find_every).with(:params => { :page => 1, :per_page => 5 }).returns([])
AresProject.paginate(:page => 1, :per_page => 5)
end
it "should have 30 per_page as default" do
AresProject.expects(:find_every).with(:params => { :page => 1, :per_page => 30 }).returns([])
AresProject.paginate(:page => 1)
end
it "should support #paginate(:all)" do
lambda { AresProject.paginate(:all) }.should raise_error(ArgumentError)
end
it "should error #paginate(:other)" do
lambda { AresProject.paginate(:first) }.should raise_error(ArgumentError)
end
protected
def create(page = 2, limit = 5, total = nil, &block)
if block_given?
WillPaginate::Collection.create(page, limit, total, &block)
else
WillPaginate::Collection.new(page, limit, total)
end
end
end

View file

@ -0,0 +1,107 @@
require 'active_record'
require 'active_record/version'
require 'active_record/fixtures'
class ActiverecordTestConnector
cattr_accessor :able_to_connect
cattr_accessor :connected
FIXTURES_PATH = File.join(File.dirname(__FILE__), '..', 'fixtures')
# Set our defaults
self.connected = false
self.able_to_connect = true
def self.setup
unless self.connected || !self.able_to_connect
setup_connection
load_schema
add_load_path FIXTURES_PATH
self.connected = true
end
rescue Exception => e # errors from ActiveRecord setup
$stderr.puts "\nSkipping ActiveRecord tests: #{e}\n\n"
self.able_to_connect = false
end
private
def self.add_load_path(path)
dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
dep.load_paths.unshift path
end
def self.setup_connection
db = ENV['DB'].blank?? 'sqlite3' : ENV['DB']
configurations = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'database.yml'))
raise "no configuration for '#{db}'" unless configurations.key? db
configuration = configurations[db]
ActiveRecord::Base.logger = Logger.new(STDOUT) if $0 == 'irb'
puts "using #{configuration['adapter']} adapter" unless ENV['DB'].blank?
ActiveRecord::Base.establish_connection(configuration)
ActiveRecord::Base.configurations = { db => configuration }
prepare ActiveRecord::Base.connection
unless Object.const_defined?(:QUOTED_TYPE)
Object.send :const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')
end
end
def self.load_schema
ActiveRecord::Base.silence do
ActiveRecord::Migration.verbose = false
load File.join(FIXTURES_PATH, 'schema.rb')
end
end
def self.prepare(conn)
class << conn
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SHOW FIELDS /]
def execute_with_counting(sql, name = nil, &block)
$query_count ||= 0
$query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
execute_without_counting(sql, name, &block)
end
alias_method_chain :execute, :counting
end
end
module FixtureSetup
def fixtures(*tables)
table_names = tables.map { |t| t.to_s }
fixtures = Fixtures.create_fixtures ActiverecordTestConnector::FIXTURES_PATH, table_names
@@loaded_fixtures = {}
@@fixture_cache = {}
unless fixtures.nil?
if fixtures.instance_of?(Fixtures)
@@loaded_fixtures[fixtures.table_name] = fixtures
else
fixtures.each { |f| @@loaded_fixtures[f.table_name] = f }
end
end
table_names.each do |table_name|
define_method(table_name) do |*fixtures|
@@fixture_cache[table_name] ||= {}
instances = fixtures.map do |fixture|
if @@loaded_fixtures[table_name][fixture.to_s]
@@fixture_cache[table_name][fixture] ||= @@loaded_fixtures[table_name][fixture.to_s].find
else
raise StandardError, "No fixture with name '#{fixture}' found for table '#{table_name}'"
end
end
instances.size == 1 ? instances.first : instances
end
end
end
end
end

View file

@ -0,0 +1,76 @@
require 'spec_helper'
require 'will_paginate/finders/base'
class Model
extend WillPaginate::Finders::Base
end
describe WillPaginate::Finders::Base do
it "should define default per_page of 30" do
Model.per_page.should == 30
end
it "should allow to set custom per_page" do
begin
Model.per_page = 25
Model.per_page.should == 25
ensure
Model.per_page = 30
end
end
it "should result with WillPaginate::Collection" do
Model.expects(:wp_query)
Model.paginate(:page => nil).should be_instance_of(WillPaginate::Collection)
end
it "should delegate pagination to wp_query" do
Model.expects(:wp_query).with({}, instance_of(WillPaginate::Collection), [])
Model.paginate :page => nil
end
it "should complain when no hash parameters given" do
lambda {
Model.paginate
}.should raise_error(ArgumentError, 'parameter hash expected')
end
it "should complain when no :page parameter present" do
lambda {
Model.paginate :per_page => 6
}.should raise_error(ArgumentError, ':page parameter required')
end
it "should complain when both :count and :total_entries are given" do
lambda {
Model.paginate :page => 1, :count => {}, :total_entries => 1
}.should raise_error(ArgumentError, ':count and :total_entries are mutually exclusive')
end
it "should never mangle options" do
options = { :page => 1 }
options.expects(:delete).never
options_before = options.dup
Model.expects(:wp_query)
Model.paginate(options)
options.should == options_before
end
it "should provide paginated_each functionality" do
collection = stub('collection', :size => 5, :empty? => false, :per_page => 5)
collection.expects(:each).times(2).returns(collection)
last_collection = stub('collection', :size => 4, :empty? => false, :per_page => 5)
last_collection.expects(:each).returns(last_collection)
params = { :order => 'id', :total_entries => 0 }
Model.expects(:paginate).with(params.merge(:page => 2)).returns(collection)
Model.expects(:paginate).with(params.merge(:page => 3)).returns(collection)
Model.expects(:paginate).with(params.merge(:page => 4)).returns(last_collection)
total = Model.paginated_each(:page => '2') { }
total.should == 14
end
end

View file

@ -0,0 +1,3 @@
class Admin < User
has_many :companies, :finder_sql => 'SELECT * FROM companies'
end

View file

@ -0,0 +1,13 @@
class Developer < User
has_and_belongs_to_many :projects, :include => :topics, :order => 'projects.name'
def self.with_poor_ones(&block)
with_scope :find => { :conditions => ['salary <= ?', 80000], :order => 'salary' } do
yield
end
end
named_scope :poor, :conditions => ['salary <= ?', 80000], :order => 'salary'
def self.per_page() 10 end
end

View file

@ -0,0 +1,13 @@
david_action_controller:
developer_id: 1
project_id: 2
joined_on: 2004-10-10
david_active_record:
developer_id: 1
project_id: 1
joined_on: 2004-10-10
jamis_active_record:
developer_id: 2
project_id: 1

View file

@ -0,0 +1,15 @@
class Project < ActiveRecord::Base
has_and_belongs_to_many :developers, :uniq => true
has_many :topics
# :finder_sql => 'SELECT * FROM topics WHERE (topics.project_id = #{id})',
# :counter_sql => 'SELECT COUNT(*) FROM topics WHERE (topics.project_id = #{id})'
has_many :replies, :through => :topics do
def find_recent(params = {})
with_scope :find => { :conditions => ['replies.created_at > ?', 15.minutes.ago] } do
find :all, params
end
end
end
end

View file

@ -0,0 +1,6 @@
active_record:
id: 1
name: Active Record
action_controller:
id: 2
name: Active Controller

View file

@ -0,0 +1,29 @@
witty_retort:
id: 1
topic_id: 1
content: Birdman is better!
created_at: <%= 6.hours.ago.to_s(:db) %>
another:
id: 2
topic_id: 2
content: Nuh uh!
created_at: <%= 1.hour.ago.to_s(:db) %>
spam:
id: 3
topic_id: 1
content: Nice site!
created_at: <%= 1.hour.ago.to_s(:db) %>
decisive:
id: 4
topic_id: 4
content: "I'm getting to the bottom of this"
created_at: <%= 30.minutes.ago.to_s(:db) %>
brave:
id: 5
topic_id: 4
content: "AR doesn't scare me a bit"
created_at: <%= 10.minutes.ago.to_s(:db) %>

View file

@ -0,0 +1,7 @@
class Reply < ActiveRecord::Base
belongs_to :topic, :include => [:replies]
named_scope :recent, :conditions => ['replies.created_at > ?', 15.minutes.ago]
validates_presence_of :content
end

View file

@ -0,0 +1,38 @@
ActiveRecord::Schema.define do
create_table "users", :force => true do |t|
t.column "name", :text
t.column "salary", :integer, :default => 70000
t.column "created_at", :datetime
t.column "updated_at", :datetime
t.column "type", :text
end
create_table "projects", :force => true do |t|
t.column "name", :text
end
create_table "developers_projects", :id => false, :force => true do |t|
t.column "developer_id", :integer, :null => false
t.column "project_id", :integer, :null => false
t.column "joined_on", :date
t.column "access_level", :integer, :default => 1
end
create_table "topics", :force => true do |t|
t.column "project_id", :integer
t.column "title", :string
t.column "subtitle", :string
t.column "content", :text
t.column "created_at", :datetime
t.column "updated_at", :datetime
end
create_table "replies", :force => true do |t|
t.column "content", :text
t.column "created_at", :datetime
t.column "updated_at", :datetime
t.column "topic_id", :integer
end
end

View file

@ -0,0 +1,6 @@
class Topic < ActiveRecord::Base
has_many :replies, :dependent => :destroy, :order => 'replies.created_at DESC'
belongs_to :project
named_scope :mentions_activerecord, :conditions => ['topics.title LIKE ?', '%ActiveRecord%']
end

View file

@ -0,0 +1,30 @@
futurama:
id: 1
title: Isnt futurama awesome?
subtitle: It really is, isnt it.
content: I like futurama
created_at: <%= 1.day.ago.to_s(:db) %>
updated_at:
harvey_birdman:
id: 2
title: Harvey Birdman is the king of all men
subtitle: yup
content: He really is
created_at: <%= 2.hours.ago.to_s(:db) %>
updated_at:
rails:
id: 3
project_id: 1
title: Rails is nice
subtitle: It makes me happy
content: except when I have to hack internals to fix pagination. even then really.
created_at: <%= 20.minutes.ago.to_s(:db) %>
ar:
id: 4
project_id: 1
title: ActiveRecord sometimes freaks me out
content: "I mean, what's the deal with eager loading?"
created_at: <%= 15.minutes.ago.to_s(:db) %>

View file

@ -0,0 +1,2 @@
class User < ActiveRecord::Base
end

View file

@ -0,0 +1,35 @@
david:
id: 1
name: David
salary: 80000
type: Developer
jamis:
id: 2
name: Jamis
salary: 150000
type: Developer
<% for digit in 3..10 %>
dev_<%= digit %>:
id: <%= digit %>
name: fixture_<%= digit %>
salary: 100000
type: Developer
<% end %>
poor_jamis:
id: 11
name: Jamis
salary: 9000
type: Developer
admin:
id: 12
name: admin
type: Admin
goofy:
id: 13
name: Goofy
type: Admin

View file

@ -0,0 +1,2 @@
--exclude ^\/,^spec\/,core_ext.rb,deprecation.rb
--no-validator-links

View file

@ -0,0 +1,2 @@
--colour
--reverse

View file

@ -0,0 +1,76 @@
require 'rubygems'
gem 'rspec', '~> 1.1.4'
require 'spec'
module MyExtras
protected
def include_phrase(string)
PhraseMatcher.new(string)
end
def collection(params = {})
if params[:total_pages]
params[:per_page] = 1
params[:total_entries] = params[:total_pages]
end
WillPaginate::Collection.new(params[:page] || 1, params[:per_page] || 30, params[:total_entries])
end
def have_deprecation
DeprecationMatcher.new
end
end
Spec::Runner.configure do |config|
# config.include My::Pony, My::Horse, :type => :farm
config.include MyExtras
# config.predicate_matchers[:swim] = :can_swim?
config.mock_with :mocha
end
class PhraseMatcher
def initialize(string)
@string = string
@pattern = /\b#{string}\b/
end
def matches?(actual)
@actual = actual.to_s
@actual =~ @pattern
end
def failure_message
"expected #{@actual.inspect} to contain phrase #{@string.inspect}"
end
def negative_failure_message
"expected #{@actual.inspect} not to contain phrase #{@string.inspect}"
end
end
class DeprecationMatcher
def initialize
@old_behavior = WillPaginate::Deprecation.behavior
@messages = []
WillPaginate::Deprecation.behavior = lambda { |message, callstack|
@messages << message
}
end
def matches?(block)
block.call
!@messages.empty?
ensure
WillPaginate::Deprecation.behavior = @old_behavior
end
def failure_message
"expected block to raise a deprecation warning"
end
def negative_failure_message
"expected block not to raise deprecation warnings, #{@messages.size} raised"
end
end

View file

@ -0,0 +1,34 @@
require 'spec/rake/spectask'
spec_opts = 'spec/spec.opts'
desc 'Run all specs'
Spec::Rake::SpecTask.new(:spec) do |t|
t.libs << 'lib' << 'spec'
t.spec_opts = ['--options', spec_opts]
end
namespace :spec do
desc 'Analyze spec coverage with RCov'
Spec::Rake::SpecTask.new(:rcov) do |t|
t.libs << 'lib' << 'spec'
t.spec_opts = ['--options', spec_opts]
t.rcov = true
t.rcov_opts = lambda do
IO.readlines('spec/rcov.opts').map { |l| l.chomp.split(" ") }.flatten
end
end
desc 'Print Specdoc for all specs'
Spec::Rake::SpecTask.new(:doc) do |t|
t.libs << 'lib' << 'spec'
t.spec_opts = ['--format', 'specdoc', '--dry-run']
end
desc 'Generate HTML report'
Spec::Rake::SpecTask.new(:html) do |t|
t.libs << 'lib' << 'spec'
t.spec_opts = ['--format', 'html:doc/spec_results.html', '--diff']
t.fail_on_error = false
end
end

View file

@ -0,0 +1,343 @@
require 'spec_helper'
require 'action_controller'
require 'view_helpers/view_example_group'
require 'will_paginate/view_helpers/action_view'
require 'will_paginate/collection'
ActionController::Routing::Routes.draw do |map|
map.connect 'dummy/page/:page', :controller => 'dummy'
map.connect 'dummy/dots/page.:page', :controller => 'dummy', :action => 'dots'
map.connect 'ibocorp/:page', :controller => 'ibocorp',
:requirements => { :page => /\d+/ },
:defaults => { :page => 1 }
map.connect ':controller/:action/:id'
end
describe WillPaginate::ViewHelpers::ActionView do
before(:each) do
@view = ActionView::Base.new
@view.controller = DummyController.new
@view.request = @view.controller.request
@template = '<%= will_paginate collection, options %>'
end
def request
@view.request
end
def render(locals)
@view.render(:inline => @template, :locals => locals)
end
## basic pagination ##
it "should render" do
paginate do |pagination|
assert_select 'a[href]', 3 do |elements|
validate_page_numbers [2,3,2], elements
assert_select elements.last, ':last-child', "Next &raquo;"
end
assert_select 'span', 1
assert_select 'span.disabled:first-child', '&laquo; Previous'
assert_select 'em', '1'
pagination.first.inner_text.should == '&laquo; Previous 1 2 3 Next &raquo;'
end
end
it "should render nothing when there is only 1 page" do
paginate(:per_page => 30).should be_empty
end
it "should paginate with options" do
paginate({ :page => 2 }, :class => 'will_paginate', :previous_label => 'Prev', :next_label => 'Next') do
assert_select 'a[href]', 4 do |elements|
validate_page_numbers [1,1,3,3], elements
# test rel attribute values:
assert_select elements[1], 'a', '1' do |link|
link.first['rel'].should == 'prev start'
end
assert_select elements.first, 'a', "Prev" do |link|
link.first['rel'].should == 'prev start'
end
assert_select elements.last, 'a', "Next" do |link|
link.first['rel'].should == 'next'
end
end
assert_select 'em', '2'
end
end
it "should paginate using a custom renderer class" do
paginate({}, :renderer => AdditionalLinkAttributesRenderer) do
assert_select 'a[default=true]', 3
end
end
it "should paginate using a custom renderer instance" do
renderer = WillPaginate::ViewHelpers::LinkRenderer.new
def renderer.gap() '<span class="my-gap">~~</span>' end
paginate({ :per_page => 2 }, :inner_window => 0, :outer_window => 0, :renderer => renderer) do
assert_select 'span.my-gap', '~~'
end
renderer = AdditionalLinkAttributesRenderer.new(:title => 'rendered')
paginate({}, :renderer => renderer) do
assert_select 'a[title=rendered]', 3
end
end
it "should have classnames on previous/next links" do
paginate do |pagination|
assert_select 'span.disabled.previous_page:first-child'
assert_select 'a.next_page[href]:last-child'
end
end
it "should warn about :prev_label being deprecated" do
lambda {
paginate({ :page => 2 }, :prev_label => 'Deprecated') do
assert_select 'a[href]:first-child', 'Deprecated'
end
}.should have_deprecation
end
it "should match expected markup" do
paginate
expected = <<-HTML
<div class="pagination"><span class="previous_page disabled">&laquo; Previous</span>
<em>1</em>
<a href="/foo/bar?page=2" rel="next">2</a>
<a href="/foo/bar?page=3">3</a>
<a href="/foo/bar?page=2" class="next_page" rel="next">Next &raquo;</a></div>
HTML
expected.strip!.gsub!(/\s{2,}/, ' ')
expected_dom = HTML::Document.new(expected).root
html_document.root.should == expected_dom
end
it "should output escaped URLs" do
paginate({:page => 1, :per_page => 1, :total_entries => 2},
:page_links => false, :params => { :tag => '<br>' })
assert_select 'a[href]', 1 do |links|
query = links.first['href'].split('?', 2)[1]
query.split('&amp;').sort.should == %w(page=2 tag=%3Cbr%3E)
end
end
## advanced options for pagination ##
it "should be able to render without container" do
paginate({}, :container => false)
assert_select 'div.pagination', 0, 'main DIV present when it shouldn\'t'
assert_select 'a[href]', 3
end
it "should be able to render without page links" do
paginate({ :page => 2 }, :page_links => false) do
assert_select 'a[href]', 2 do |elements|
validate_page_numbers [1,3], elements
end
end
end
it "should have magic HTML ID for the container" do
paginate do |div|
div.first['id'].should be_nil
end
# magic ID
paginate({}, :id => true) do |div|
div.first['id'].should == 'fixnums_pagination'
end
# explicit ID
paginate({}, :id => 'custom_id') do |div|
div.first['id'].should == 'custom_id'
end
end
## other helpers ##
it "should render a paginated section" do
@template = <<-ERB
<% paginated_section collection, options do %>
<%= content_tag :div, '', :id => "developers" %>
<% end %>
ERB
paginate
assert_select 'div.pagination', 2
assert_select 'div.pagination + div#developers', 1
end
## parameter handling in page links ##
it "should preserve parameters on GET" do
request.params :foo => { :bar => 'baz' }
paginate
assert_links_match /foo%5Bbar%5D=baz/
end
it "should not preserve parameters on POST" do
request.post
request.params :foo => 'bar'
paginate
assert_no_links_match /foo=bar/
end
it "should add additional parameters to links" do
paginate({}, :params => { :foo => 'bar' })
assert_links_match /foo=bar/
end
it "should add anchor parameter" do
paginate({}, :params => { :anchor => 'anchor' })
assert_links_match /#anchor$/
end
it "should remove arbitrary parameters" do
request.params :foo => 'bar'
paginate({}, :params => { :foo => nil })
assert_no_links_match /foo=bar/
end
it "should override default route parameters" do
paginate({}, :params => { :controller => 'baz', :action => 'list' })
assert_links_match %r{\Wbaz/list\W}
end
it "should paginate with custom page parameter" do
paginate({ :page => 2 }, :param_name => :developers_page) do
assert_select 'a[href]', 4 do |elements|
validate_page_numbers [1,1,3,3], elements, :developers_page
end
end
end
it "should paginate with complex custom page parameter" do
request.params :developers => { :page => 2 }
paginate({ :page => 2 }, :param_name => 'developers[page]') do
assert_select 'a[href]', 4 do |links|
assert_links_match /\?developers%5Bpage%5D=\d+$/, links
validate_page_numbers [1,1,3,3], links, 'developers[page]'
end
end
end
it "should paginate with custom route page parameter" do
request.symbolized_path_parameters.update :controller => 'dummy', :action => nil
paginate :per_page => 2 do
assert_select 'a[href]', 6 do |links|
assert_links_match %r{/page/(\d+)$}, links, [2, 3, 4, 5, 6, 2]
end
end
end
it "should paginate with custom route with dot separator page parameter" do
request.symbolized_path_parameters.update :controller => 'dummy', :action => 'dots'
paginate :per_page => 2 do
assert_select 'a[href]', 6 do |links|
assert_links_match %r{/page\.(\d+)$}, links, [2, 3, 4, 5, 6, 2]
end
end
end
it "should paginate with custom route and first page number implicit" do
request.symbolized_path_parameters.update :controller => 'ibocorp', :action => nil
paginate :page => 2, :per_page => 2 do
assert_select 'a[href]', 7 do |links|
assert_links_match %r{/ibocorp(?:/(\d+))?$}, links, [nil, nil, 3, 4, 5, 6, 3]
end
end
end
## internal hardcore stuff ##
it "should be able to guess the collection name" do
collection = mock
collection.expects(:total_pages).returns(1)
@template = '<%= will_paginate options %>'
@view.controller.controller_name = 'developers'
@view.assigns['developers'] = collection
paginate(nil)
end
it "should fail if the inferred collection is nil" do
@template = '<%= will_paginate options %>'
@view.controller.controller_name = 'developers'
lambda {
paginate(nil)
}.should raise_error(ArgumentError, /@developers/)
end
if ActionController::Base.respond_to? :rescue_responses
# only on Rails 2
it "should set rescue response hook" do
ActionController::Base.rescue_responses['WillPaginate::InvalidPage'].should == :not_found
end
end
end
class AdditionalLinkAttributesRenderer < WillPaginate::ViewHelpers::LinkRenderer
def initialize(link_attributes = nil)
super()
@additional_link_attributes = link_attributes || { :default => 'true' }
end
def link(text, target, attributes = {})
super(text, target, attributes.merge(@additional_link_attributes))
end
end
class DummyController
attr_reader :request
attr_accessor :controller_name
def initialize
@request = DummyRequest.new
@url = ActionController::UrlRewriter.new(@request, @request.params)
end
def params
@request.params
end
def url_for(params)
@url.rewrite(params)
end
end
class DummyRequest
attr_accessor :symbolized_path_parameters
def initialize
@get = true
@params = {}
@symbolized_path_parameters = { :controller => 'foo', :action => 'bar' }
end
def get?
@get
end
def post
@get = false
end
def relative_url_root
''
end
def params(more = nil)
@params.update(more) if more
@params
end
end

View file

@ -0,0 +1,64 @@
require 'spec_helper'
require 'will_paginate/view_helpers/base'
require 'will_paginate/array'
describe WillPaginate::ViewHelpers::Base do
include WillPaginate::ViewHelpers::Base
describe "will_paginate" do
it "should render" do
collection = WillPaginate::Collection.new(1, 2, 4)
renderer = mock 'Renderer'
renderer.expects(:prepare).with(collection, instance_of(Hash), self)
renderer.expects(:to_html).returns('<PAGES>')
will_paginate(collection, :renderer => renderer).should == '<PAGES>'
end
it "should return nil for single-page collections" do
collection = mock 'Collection', :total_pages => 1
will_paginate(collection).should be_nil
end
end
describe "page_entries_info" do
before :all do
@array = ('a'..'z').to_a
end
def info(params, options = {})
options[:html] ||= false unless options.key?(:html) and options[:html].nil?
collection = Hash === params ? @array.paginate(params) : params
page_entries_info collection, options
end
it "should display middle results and total count" do
info(:page => 2, :per_page => 5).should == "Displaying strings 6 - 10 of 26 in total"
end
it "should output HTML by default" do
info({ :page => 2, :per_page => 5 }, :html => nil).should ==
"Displaying strings <b>6&nbsp;-&nbsp;10</b> of <b>26</b> in total"
end
it "should display shortened end results" do
info(:page => 7, :per_page => 4).should include_phrase('strings 25 - 26')
end
it "should handle longer class names" do
collection = @array.paginate(:page => 2, :per_page => 5)
collection.first.stubs(:class).returns(mock('Class', :name => 'ProjectType'))
info(collection).should include_phrase('project types')
end
it "should adjust output for single-page collections" do
info(('a'..'d').to_a.paginate(:page => 1, :per_page => 5)).should == "Displaying all 4 strings"
info(['a'].paginate(:page => 1, :per_page => 5)).should == "Displaying 1 string"
end
it "should display 'no entries found' for empty collections" do
info([].paginate(:page => 1, :per_page => 5)).should == "No entries found"
end
end
end

View file

@ -0,0 +1,84 @@
require 'spec_helper'
require 'will_paginate/view_helpers/link_renderer_base'
require 'will_paginate/collection'
describe WillPaginate::ViewHelpers::LinkRendererBase do
before do
@renderer = WillPaginate::ViewHelpers::LinkRendererBase.new
end
it "should raise error when unprepared" do
lambda {
@renderer.send :param_name
}.should raise_error
end
it "should prepare with collection and options" do
prepare({}, :param_name => 'mypage')
@renderer.send(:current_page).should == 1
@renderer.send(:param_name).should == 'mypage'
end
it "should have total_pages accessor" do
prepare :total_pages => 42
lambda {
@renderer.send(:total_pages).should == 42
}.should_not have_deprecation
end
it "should clear old cached values when prepared" do
prepare({ :total_pages => 1 }, :param_name => 'foo')
@renderer.send(:total_pages).should == 1
@renderer.send(:param_name).should == 'foo'
# prepare with different object and options:
prepare({ :total_pages => 2 }, :param_name => 'bar')
@renderer.send(:total_pages).should == 2
@renderer.send(:param_name).should == 'bar'
end
it "should have pagination definition" do
prepare({ :total_pages => 1 }, :page_links => true)
@renderer.pagination.should == [:previous_page, 1, :next_page]
end
describe "visible page numbers" do
it "should calculate windowed visible links" do
prepare({ :page => 6, :total_pages => 11 }, :inner_window => 1, :outer_window => 1)
showing_pages 1, 2, :gap, 5, 6, 7, :gap, 10, 11
end
it "should eliminate small gaps" do
prepare({ :page => 6, :total_pages => 11 }, :inner_window => 2, :outer_window => 1)
# pages 4 and 8 appear instead of the gap
showing_pages 1..11
end
it "should support having no windows at all" do
prepare({ :page => 4, :total_pages => 7 }, :inner_window => 0, :outer_window => 0)
showing_pages 1, :gap, 4, :gap, 7
end
it "should adjust upper limit if lower is out of bounds" do
prepare({ :page => 1, :total_pages => 10 }, :inner_window => 2, :outer_window => 1)
showing_pages 1, 2, 3, 4, 5, :gap, 9, 10
end
it "should adjust lower limit if upper is out of bounds" do
prepare({ :page => 10, :total_pages => 10 }, :inner_window => 2, :outer_window => 1)
showing_pages 1, 2, :gap, 6, 7, 8, 9, 10
end
def showing_pages(*pages)
pages = pages.first.to_a if Array === pages.first or Range === pages.first
@renderer.send(:windowed_page_numbers).should == pages
end
end
protected
def prepare(collection_options, options = {})
@renderer.prepare(collection(collection_options), options)
end
end

View file

@ -0,0 +1,111 @@
unless $:.find { |p| p =~ %r{/html-scanner$} }
unless actionpack_path = $:.find { |p| p =~ %r{/actionpack(-[\d.]+)?/lib$} }
raise "cannot find ActionPack in load paths"
end
html_scanner_path = "#{actionpack_path}/action_controller/vendor/html-scanner"
$:.unshift(html_scanner_path)
end
require 'action_controller/assertions/selector_assertions'
class ViewExampleGroup < Spec::Example::ExampleGroup
include ActionController::Assertions::SelectorAssertions
def assert(value, message)
raise message unless value
end
def paginate(collection = {}, options = {}, &block)
if collection.instance_of? Hash
page_options = { :page => 1, :total_entries => 11, :per_page => 4 }.merge(collection)
collection = [1].paginate(page_options)
end
locals = { :collection => collection, :options => options }
@render_output = render(locals)
@html_document = nil
if block_given?
classname = options[:class] || WillPaginate::ViewHelpers.pagination_options[:class]
assert_select("div.#{classname}", 1, 'no main DIV', &block)
end
@render_output
end
def html_document
@html_document ||= HTML::Document.new(@render_output, true, false)
end
def response_from_page_or_rjs
html_document.root
end
def validate_page_numbers(expected, links, param_name = :page)
param_pattern = /\W#{CGI.escape(param_name.to_s)}=([^&]*)/
links.map { |e|
e['href'] =~ param_pattern
$1 ? $1.to_i : $1
}.should == expected
end
def assert_links_match(pattern, links = nil, numbers = nil)
links ||= assert_select 'div.pagination a[href]' do |elements|
elements
end
pages = [] if numbers
links.each do |el|
el['href'].should =~ pattern
if numbers
el['href'] =~ pattern
pages << ($1.nil?? nil : $1.to_i)
end
end
pages.should == numbers if numbers
end
def assert_no_links_match(pattern)
assert_select 'div.pagination a[href]' do |elements|
elements.each do |el|
el['href'] !~ pattern
end
end
end
def build_message(message, pattern, *args)
built_message = pattern.dup
for value in args
built_message.sub! '?', value.inspect
end
built_message
end
end
Spec::Example::ExampleGroupFactory.register(:view_helpers, ViewExampleGroup)
module HTML
Node.class_eval do
def inner_text
children.map(&:inner_text).join('')
end
end
Text.class_eval do
def inner_text
self.to_s
end
end
Tag.class_eval do
def inner_text
childless?? '' : super
end
end
end

View file

@ -0,0 +1,20 @@
Gem::Specification.new do |s|
s.name = 'will_paginate'
s.version = '2.5.0'
# s.date = '2008-10-27'
s.summary = "Most awesome pagination solution for every web app"
s.description = "The will_paginate library provides a simple, yet powerful and extensible API for pagination and rendering of page links in templates."
s.authors = ['Mislav Marohnić', 'PJ Hyett']
s.email = 'mislav.marohnic@gmail.com'
s.homepage = 'http://github.com/mislav/will_paginate/wikis'
s.has_rdoc = true
s.rdoc_options = ['--main', 'README.rdoc']
s.rdoc_options << '--inline-source' << '--charset=UTF-8'
s.extra_rdoc_files = ['README.rdoc', 'LICENSE', 'CHANGELOG.rdoc']
s.files = %w(CHANGELOG.rdoc LICENSE README.rdoc Rakefile examples examples/apple-circle.gif examples/index.haml examples/index.html examples/pagination.css examples/pagination.sass init.rb lib lib/will_paginate lib/will_paginate.rb lib/will_paginate/array.rb lib/will_paginate/collection.rb lib/will_paginate/core_ext.rb lib/will_paginate/deprecation.rb lib/will_paginate/finders lib/will_paginate/finders.rb lib/will_paginate/finders/active_record lib/will_paginate/finders/active_record.rb lib/will_paginate/finders/active_record/named_scope.rb lib/will_paginate/finders/active_record/named_scope_patch.rb lib/will_paginate/finders/active_resource.rb lib/will_paginate/finders/base.rb lib/will_paginate/finders/data_mapper.rb lib/will_paginate/version.rb lib/will_paginate/view_helpers lib/will_paginate/view_helpers.rb lib/will_paginate/view_helpers/action_view.rb lib/will_paginate/view_helpers/base.rb lib/will_paginate/view_helpers/link_renderer.rb lib/will_paginate/view_helpers/link_renderer_base.rb spec spec/collection_spec.rb spec/console spec/console_fixtures.rb spec/database.yml spec/finders spec/finders/active_record_spec.rb spec/finders/active_resource_spec.rb spec/finders/activerecord_test_connector.rb spec/finders_spec.rb spec/fixtures spec/fixtures/admin.rb spec/fixtures/developer.rb spec/fixtures/developers_projects.yml spec/fixtures/project.rb spec/fixtures/projects.yml spec/fixtures/replies.yml spec/fixtures/reply.rb spec/fixtures/schema.rb spec/fixtures/topic.rb spec/fixtures/topics.yml spec/fixtures/user.rb spec/fixtures/users.yml spec/rcov.opts spec/spec.opts spec/spec_helper.rb spec/tasks.rake spec/view_helpers spec/view_helpers/action_view_spec.rb spec/view_helpers/base_spec.rb spec/view_helpers/link_renderer_base_spec.rb spec/view_helpers/view_example_group.rb)
s.test_files = %w(spec/collection_spec.rb spec/console spec/console_fixtures.rb spec/database.yml spec/finders spec/finders/active_record_spec.rb spec/finders/active_resource_spec.rb spec/finders/activerecord_test_connector.rb spec/finders_spec.rb spec/fixtures spec/fixtures/admin.rb spec/fixtures/developer.rb spec/fixtures/developers_projects.yml spec/fixtures/project.rb spec/fixtures/projects.yml spec/fixtures/replies.yml spec/fixtures/reply.rb spec/fixtures/schema.rb spec/fixtures/topic.rb spec/fixtures/topics.yml spec/fixtures/user.rb spec/fixtures/users.yml spec/rcov.opts spec/spec.opts spec/spec_helper.rb spec/tasks.rake spec/view_helpers spec/view_helpers/action_view_spec.rb spec/view_helpers/base_spec.rb spec/view_helpers/link_renderer_base_spec.rb spec/view_helpers/view_example_group.rb)
end