diff --git a/vendor/plugins/will_paginate/.autotest b/vendor/plugins/will_paginate/.autotest
new file mode 100644
index 0000000..ade45eb
--- /dev/null
+++ b/vendor/plugins/will_paginate/.autotest
@@ -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
diff --git a/vendor/plugins/will_paginate/.gitignore b/vendor/plugins/will_paginate/.gitignore
new file mode 100644
index 0000000..2b437b9
--- /dev/null
+++ b/vendor/plugins/will_paginate/.gitignore
@@ -0,0 +1,4 @@
+/doc
+/rails
+*.gem
+/coverage
diff --git a/vendor/plugins/will_paginate/.manifest b/vendor/plugins/will_paginate/.manifest
new file mode 100644
index 0000000..aca08ee
--- /dev/null
+++ b/vendor/plugins/will_paginate/.manifest
@@ -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
\ No newline at end of file
diff --git a/vendor/plugins/will_paginate/CHANGELOG.rdoc b/vendor/plugins/will_paginate/CHANGELOG.rdoc
new file mode 100644
index 0000000..829c0bb
--- /dev/null
+++ b/vendor/plugins/will_paginate/CHANGELOG.rdoc
@@ -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
+ "…".
+* 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
diff --git a/vendor/plugins/will_paginate/LICENSE b/vendor/plugins/will_paginate/LICENSE
new file mode 100644
index 0000000..96a48cb
--- /dev/null
+++ b/vendor/plugins/will_paginate/LICENSE
@@ -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.
diff --git a/vendor/plugins/will_paginate/README.rdoc b/vendor/plugins/will_paginate/README.rdoc
new file mode 100644
index 0000000..a402a7b
--- /dev/null
+++ b/vendor/plugins/will_paginate/README.rdoc
@@ -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 plugin in your Rails application anymore. In
+Rails 2.1, 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 all machines that you are deploying to.
+
+There are extensive {installation
+instructions}[http://github.com/mislav/will_paginate/wikis/installation] on {the
+wiki}[http://github.com/mislav/will_paginate/wikis].
+
+
+== 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
+per_page 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 30 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:
+
+
+ <% for post in @posts -%>
+
Render `post` in some nice way.
+ <% end -%>
+
+
+
Now let's render us some pagination!
+ <%= 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
+"examples/index.html" 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].
\ No newline at end of file
diff --git a/vendor/plugins/will_paginate/Rakefile b/vendor/plugins/will_paginate/Rakefile
new file mode 100644
index 0000000..81672f6
--- /dev/null
+++ b/vendor/plugins/will_paginate/Rakefile
@@ -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
\ No newline at end of file
diff --git a/vendor/plugins/will_paginate/examples/apple-circle.gif b/vendor/plugins/will_paginate/examples/apple-circle.gif
new file mode 100644
index 0000000..df8cbf7
Binary files /dev/null and b/vendor/plugins/will_paginate/examples/apple-circle.gif differ
diff --git a/vendor/plugins/will_paginate/examples/index.haml b/vendor/plugins/will_paginate/examples/index.haml
new file mode 100644
index 0000000..fb41ac8
--- /dev/null
+++ b/vendor/plugins/will_paginate/examples/index.haml
@@ -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 = '« Previous123456789…2930Next »'
+- pagination_no_page_links = '« PreviousNext »'
+
+%body
+ %h1 Samples of pagination styling for will_paginate
+ %p
+ Find these styles in "examples/pagination.css" of will_paginate 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 (ewww!)
+ %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= '%s' % %[<%= will_paginate @posts, :page_links => false %>].gsub('<', '<').gsub('>', '>')
+
+ %h2 Digg-style, extra content
+ .digg_pagination
+ .page_info Displaying entries 1 - 6 of 180 in total
+ = pagination
+ %p Code that renders this:
+ %pre= '%s' % %[
+
diff --git a/vendor/plugins/will_paginate/examples/pagination.css b/vendor/plugins/will_paginate/examples/pagination.css
new file mode 100644
index 0000000..b55e977
--- /dev/null
+++ b/vendor/plugins/will_paginate/examples/pagination.css
@@ -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; }
diff --git a/vendor/plugins/will_paginate/examples/pagination.sass b/vendor/plugins/will_paginate/examples/pagination.sass
new file mode 100644
index 0000000..737a97b
--- /dev/null
+++ b/vendor/plugins/will_paginate/examples/pagination.sass
@@ -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
diff --git a/vendor/plugins/will_paginate/init.rb b/vendor/plugins/will_paginate/init.rb
new file mode 100644
index 0000000..838d30e
--- /dev/null
+++ b/vendor/plugins/will_paginate/init.rb
@@ -0,0 +1 @@
+require 'will_paginate'
diff --git a/vendor/plugins/will_paginate/lib/will_paginate.rb b/vendor/plugins/will_paginate/lib/will_paginate.rb
new file mode 100644
index 0000000..ee81c5d
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate.rb
@@ -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 named_scope broke
+ # has_many, has_many :through or has_and_belongs_to_many associations in
+ # your app. By passing +false+, you can still use named_scope 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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/array.rb b/vendor/plugins/will_paginate/lib/will_paginate/array.rb
new file mode 100644
index 0000000..1076760
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/array.rb
@@ -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:
+ # * :page - current page, defaults to 1
+ # * :per_page - limit of items per page, defaults to 30
+ # * :total_entries - total number of items in the array, defaults to
+ # array.length (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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/collection.rb b/vendor/plugins/will_paginate/lib/will_paginate/collection.rb
new file mode 100644
index 0000000..89d992f
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/collection.rb
@@ -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 WillPaginate::Collection#out_of_bounds? 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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb b/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb
new file mode 100644
index 0000000..4601f00
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/deprecation.rb b/vendor/plugins/will_paginate/lib/will_paginate/deprecation.rb
new file mode 100644
index 0000000..2c44d1e
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/deprecation.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/finders.rb b/vendor/plugins/will_paginate/lib/will_paginate/finders.rb
new file mode 100644
index 0000000..ca41f5b
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/finders.rb
@@ -0,0 +1,9 @@
+require 'will_paginate/core_ext'
+
+module WillPaginate
+ # Database logic for different ORMs
+ #
+ # See WillPaginate::Finders::Base
+ module Finders
+ end
+end
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/finders/active_record.rb b/vendor/plugins/will_paginate/lib/will_paginate/finders/active_record.rb
new file mode 100644
index 0000000..84c99ff
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/finders/active_record.rb
@@ -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 :page 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 :all 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 :order parameter
+ #
+ # In ActiveRecord finders, :order parameter specifies columns for
+ # the ORDER BY clause in SQL. It is important to have it, since
+ # pagination only makes sense with ordered sets. Without the ORDER
+ # BY clause, databases aren't required to do consistent ordering when
+ # performing SELECT 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 :total_entries. 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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/finders/active_record/named_scope.rb b/vendor/plugins/will_paginate/lib/will_paginate/finders/active_record/named_scope.rb
new file mode 100644
index 0000000..21fc168
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/finders/active_record/named_scope.rb
@@ -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:
+ # * all, which is similar to a find(:all) query, and
+ # * scoped, which allows for the creation of anonymous scopes, on the fly: Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)
+ #
+ # 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 :conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions.
+ #
+ # 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 named_scope define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
+ # in effect, represents the query Shirt.find(:all, :conditions => {:color => 'red'}).
+ #
+ # Unlike Shirt.find(...), however, the object returned by Shirt.red is not an Array; it resembles the association object
+ # constructed by a has_many declaration. For instance, you can invoke Shirt.red.find(:first), Shirt.red.count,
+ # Shirt.red.find(:all, :conditions => {:size => 'small'}). Also, just
+ # as with the association objects, name scopes acts like an Array, implementing Enumerable; Shirt.red.each(&block),
+ # Shirt.red.first, and Shirt.red.inject(memo, &block) all behave as if Shirt.red really were an Array.
+ #
+ # These named scopes are composable. For instance, Shirt.red.dry_clean_only will produce all shirts that are both red and dry clean only.
+ # Nested finds and calculations also work with these compositions: Shirt.red.dry_clean_only.count returns the number of garments
+ # for which these criteria obtain. Similarly with Shirt.red.dry_clean_only.average(:thread_count).
+ #
+ # All scopes are available as class methods on the ActiveRecord::Base descendent upon which the scopes were defined. But they are also available to
+ # has_many associations. If,
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :shirts
+ # end
+ #
+ # then elton.shirts.red.dry_clean_only 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, Shirt.colored('puce') finds all puce shirts.
+ #
+ # Named scopes can also have extensions, just as with has_many 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
+ # proxy_options 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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/finders/active_record/named_scope_patch.rb b/vendor/plugins/will_paginate/lib/will_paginate/finders/active_record/named_scope_patch.rb
new file mode 100644
index 0000000..bdc1997
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/finders/active_record/named_scope_patch.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/finders/active_resource.rb b/vendor/plugins/will_paginate/lib/will_paginate/finders/active_resource.rb
new file mode 100644
index 0000000..9ba0236
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/finders/active_resource.rb
@@ -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
\ No newline at end of file
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/finders/base.rb b/vendor/plugins/will_paginate/lib/will_paginate/finders/base.rb
new file mode 100644
index 0000000..f643244
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/finders/base.rb
@@ -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
+ # * :page -- REQUIRED, but defaults to 1 if false or nil
+ # * :per_page -- defaults to CurrentModel.per_page (which is 30 if not overridden)
+ # * :total_entries -- use only if you manually count total entries
+ # * :count -- additional options that are passed on to +count+
+ # * :finder -- 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 :page (default is 1). Default
+ # :order is "id", 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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/finders/data_mapper.rb b/vendor/plugins/will_paginate/lib/will_paginate/finders/data_mapper.rb
new file mode 100644
index 0000000..c31c5fb
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/finders/data_mapper.rb
@@ -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)
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/version.rb b/vendor/plugins/will_paginate/lib/will_paginate/version.rb
new file mode 100644
index 0000000..ba92b54
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/version.rb
@@ -0,0 +1,9 @@
+module WillPaginate #:nodoc:
+ module VERSION #:nodoc:
+ MAJOR = 2
+ MINOR = 5
+ TINY = 0
+
+ STRING = [MAJOR, MINOR, TINY].join('.')
+ end
+end
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb
new file mode 100644
index 0000000..9917cc5
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb
@@ -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 => '« Previous',
+ :next_label => 'Next »',
+ :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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/action_view.rb b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/action_view.rb
new file mode 100644
index 0000000..51e8525
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/action_view.rb
@@ -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 %>
+ #
+ # <% for post in @posts %>
+ #
...
+ # <% end %>
+ #
+ # <% end %>
+ #
+ # will result in:
+ #
+ #
...
+ #
+ # ...
+ #
+ #
...
+ #
+ # Arguments are passed to a will_paginate call, so the same options
+ # apply. Don't use the :id 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
\ No newline at end of file
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/base.rb b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/base.rb
new file mode 100644
index 0000000..c5a0ecb
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/base.rb
@@ -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
+ # * :class -- CSS class name for the generated DIV (default: "pagination")
+ # * :previous_label -- default: "« Previous"
+ # * :next_label -- default: "Next »"
+ # * :inner_window -- how many links are shown around the current page (default: 4)
+ # * :outer_window -- how many links are around the first and the last page (default: 1)
+ # * :separator -- string separator for page HTML elements (default: single space)
+ # * :param_name -- parameter name for page number in URLs (default: :page)
+ # * :params -- additional parameters when generating pagination links
+ # (eg. :controller => "foo", :action => nil)
+ # * :renderer -- class name, class or instance of a link renderer (default:
+ # WillPaginate::LinkRenderer)
+ # * :page_links -- when false, only previous/next links are rendered (default: true)
+ # * :container -- toggles rendering of the DIV container for pagination links, set to
+ # false only when you are rendering your own pagination markup (default: true)
+ # * :id -- 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:
+ #
+ #
...
+ #
+ # ==== 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 @posts
+ # variable. Example:
+ #
+ # <%= will_paginate :id => true %>
+ #
+ # ... will result in @post collection getting paginated:
+ #
+ #
...
+ #
+ 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 :entry_name 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
+ # String#pluralize method from ActiveSupport. If it isn't
+ # loaded, specify plural with :plural_name 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 :html => false 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 = ''
+ eb = ''
+ sp = ' '
+ 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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/link_renderer.rb b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/link_renderer.rb
new file mode 100644
index 0000000..305155d
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/link_renderer.rb
@@ -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
+ '…'
+ 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 :params 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
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/link_renderer_base.rb b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/link_renderer_base.rb
new file mode 100644
index 0000000..26f0f72
--- /dev/null
+++ b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers/link_renderer_base.rb
@@ -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 :inner_window and
+ # :outer_window 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
diff --git a/vendor/plugins/will_paginate/spec/collection_spec.rb b/vendor/plugins/will_paginate/spec/collection_spec.rb
new file mode 100644
index 0000000..4d71dc9
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/collection_spec.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/console b/vendor/plugins/will_paginate/spec/console
new file mode 100755
index 0000000..0d3a360
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/console
@@ -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"
diff --git a/vendor/plugins/will_paginate/spec/console_fixtures.rb b/vendor/plugins/will_paginate/spec/console_fixtures.rb
new file mode 100644
index 0000000..1f0853f
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/console_fixtures.rb
@@ -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)
+
+
diff --git a/vendor/plugins/will_paginate/spec/database.yml b/vendor/plugins/will_paginate/spec/database.yml
new file mode 100644
index 0000000..7ef1e73
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/database.yml
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/finders/active_record_spec.rb b/vendor/plugins/will_paginate/spec/finders/active_record_spec.rb
new file mode 100644
index 0000000..48866e1
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/finders/active_record_spec.rb
@@ -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
\ No newline at end of file
diff --git a/vendor/plugins/will_paginate/spec/finders/active_resource_spec.rb b/vendor/plugins/will_paginate/spec/finders/active_resource_spec.rb
new file mode 100644
index 0000000..e00bb12
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/finders/active_resource_spec.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/finders/activerecord_test_connector.rb b/vendor/plugins/will_paginate/spec/finders/activerecord_test_connector.rb
new file mode 100644
index 0000000..fdbb233
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/finders/activerecord_test_connector.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/finders_spec.rb b/vendor/plugins/will_paginate/spec/finders_spec.rb
new file mode 100644
index 0000000..0782fbe
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/finders_spec.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/fixtures/admin.rb b/vendor/plugins/will_paginate/spec/fixtures/admin.rb
new file mode 100644
index 0000000..1d5e7f3
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/admin.rb
@@ -0,0 +1,3 @@
+class Admin < User
+ has_many :companies, :finder_sql => 'SELECT * FROM companies'
+end
diff --git a/vendor/plugins/will_paginate/spec/fixtures/developer.rb b/vendor/plugins/will_paginate/spec/fixtures/developer.rb
new file mode 100644
index 0000000..7105355
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/developer.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/fixtures/developers_projects.yml b/vendor/plugins/will_paginate/spec/fixtures/developers_projects.yml
new file mode 100644
index 0000000..cee359c
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/developers_projects.yml
@@ -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
\ No newline at end of file
diff --git a/vendor/plugins/will_paginate/spec/fixtures/project.rb b/vendor/plugins/will_paginate/spec/fixtures/project.rb
new file mode 100644
index 0000000..0f85ef5
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/project.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/fixtures/projects.yml b/vendor/plugins/will_paginate/spec/fixtures/projects.yml
new file mode 100644
index 0000000..74f3c32
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/projects.yml
@@ -0,0 +1,6 @@
+active_record:
+ id: 1
+ name: Active Record
+action_controller:
+ id: 2
+ name: Active Controller
diff --git a/vendor/plugins/will_paginate/spec/fixtures/replies.yml b/vendor/plugins/will_paginate/spec/fixtures/replies.yml
new file mode 100644
index 0000000..9a83c00
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/replies.yml
@@ -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) %>
diff --git a/vendor/plugins/will_paginate/spec/fixtures/reply.rb b/vendor/plugins/will_paginate/spec/fixtures/reply.rb
new file mode 100644
index 0000000..ecaf3c1
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/reply.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/fixtures/schema.rb b/vendor/plugins/will_paginate/spec/fixtures/schema.rb
new file mode 100644
index 0000000..8831aad
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/schema.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/fixtures/topic.rb b/vendor/plugins/will_paginate/spec/fixtures/topic.rb
new file mode 100644
index 0000000..77be0dd
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/topic.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/fixtures/topics.yml b/vendor/plugins/will_paginate/spec/fixtures/topics.yml
new file mode 100644
index 0000000..0a26904
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/topics.yml
@@ -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) %>
diff --git a/vendor/plugins/will_paginate/spec/fixtures/user.rb b/vendor/plugins/will_paginate/spec/fixtures/user.rb
new file mode 100644
index 0000000..4a57cf0
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/user.rb
@@ -0,0 +1,2 @@
+class User < ActiveRecord::Base
+end
diff --git a/vendor/plugins/will_paginate/spec/fixtures/users.yml b/vendor/plugins/will_paginate/spec/fixtures/users.yml
new file mode 100644
index 0000000..ed2c03a
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/fixtures/users.yml
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/rcov.opts b/vendor/plugins/will_paginate/spec/rcov.opts
new file mode 100644
index 0000000..6b17c32
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/rcov.opts
@@ -0,0 +1,2 @@
+--exclude ^\/,^spec\/,core_ext.rb,deprecation.rb
+--no-validator-links
\ No newline at end of file
diff --git a/vendor/plugins/will_paginate/spec/spec.opts b/vendor/plugins/will_paginate/spec/spec.opts
new file mode 100644
index 0000000..14f5f13
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/spec.opts
@@ -0,0 +1,2 @@
+--colour
+--reverse
diff --git a/vendor/plugins/will_paginate/spec/spec_helper.rb b/vendor/plugins/will_paginate/spec/spec_helper.rb
new file mode 100644
index 0000000..46a26e5
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/spec_helper.rb
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/tasks.rake b/vendor/plugins/will_paginate/spec/tasks.rake
new file mode 100644
index 0000000..cb04366
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/tasks.rake
@@ -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
diff --git a/vendor/plugins/will_paginate/spec/view_helpers/action_view_spec.rb b/vendor/plugins/will_paginate/spec/view_helpers/action_view_spec.rb
new file mode 100644
index 0000000..8b34a1b
--- /dev/null
+++ b/vendor/plugins/will_paginate/spec/view_helpers/action_view_spec.rb
@@ -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 »"
+ end
+ assert_select 'span', 1
+ assert_select 'span.disabled:first-child', '« Previous'
+ assert_select 'em', '1'
+ pagination.first.inner_text.should == '« Previous 1 2 3 Next »'
+ 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() '~~' 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
+