Skip to content

Commit

Permalink
Merge branch 'master' of [email protected]:pjg/simply_paginate
Browse files Browse the repository at this point in the history
  • Loading branch information
pjg committed May 20, 2009
2 parents 256321c + 0330727 commit 078b4dd
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
test/log/*
*.swp
78 changes: 76 additions & 2 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,26 @@ and PJ Hyett's work on will_paginate (http://github.com/mislav/will_paginate).
It is a very trimmed down version of will_paginate plus a few additions to suit my needs.



Requirements
============

Rails version 2.2.2

Gem dependencies:
redgreen (for testing) [OPTIONAL]
sqlite3-ruby (for testing)



Installation
============

git submodule add git://github.com/pjg/simply_paginate.git vendor/plugins/simply_paginate
git commit -m "Added simply_paginate plugin as a submodule."



Example usage
=============

Expand All @@ -27,7 +40,7 @@ app/controllers/comments_controller.rb
:page => params[:page],
:include => {:document},
:conditions => {:is_hidden => false)},
:order => 'comments.created_on DESC')
:order => 'comments.id DESC')
end


Expand Down Expand Up @@ -65,5 +78,66 @@ app/controllers/search_controller.rb
end


It does work correctly with named scopes, for example the following will yield expected results:

Comment.visible.ordered.paginate(:page => 2, :per_page => 5)


It also works with Arrays:

[1, 2, 3, 4 ,5].paginate(:page => 2, :per_page => 2)
=> [3, 4]



CAVEATS
=======

Unfortunately, there are some. Comment.all returns an array (instead of ActiveRecord::NamedScope::Scope), so doing one of the following:

Comment.all.paginate(:per_page => 2)
Comment.find(:all.paginate(:per_page => 2)
>> Comment Load (26.5ms) SELECT * FROM `comments`

is highly inefficient as it will first fetch all recrods from the database and only then paginate the returned Array. Use this instead:

Comment.paginate(:per_page => 2)
>> SQL (0.1ms) SELECT count(*) AS count_all FROM `comments`
>> Comment Load (0.3ms) SELECT * FROM `comments` LIMIT 0, 2


I don't recommend using it with named scopes which use the :include parameter. For example, given:

model Comment
named_scope :with_document, :include => :document
end

Don't do the following (as it will generate an ugly and very resource hungry count query):

Comment.with_document.paginate(:page => params[:page], :per_page => 50)
>> SQL (85.7ms) SELECT count(DISTINCT `comments`.id) AS count_all FROM `comments` LEFT OUTER JOIN `documents` ON `documents`.id = `comments`.document_id
>> Comment Load (0.1ms) SELECT * FROM `comments` LIMIT 0, 50
...

Instead do it old school:

Comment.paginate(:include => :document, :page => params[:page], :per_page => 50)
>> SQL (0.2ms) SELECT count(*) AS count_all FROM `comments`
>> Comment Load (0.1ms) SELECT * FROM `comments` LIMIT 0, 50
...

The difference in performance is striking (in part it is due to the fact, that MySQL keeps table count at hand).



TESTING
=======

If you'd like to run tests, you must have sqlite3 installed:

aptitude install sqlite3 libsqlite3-dev
gem install sqlite3-ruby



Copyright (c) 2008 Paweł Gościcki, released under the MIT license
Copyright (c) 2008-2009 Paweł Gościcki, released under the MIT license
7 changes: 7 additions & 0 deletions lib/array.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Array.class_eval do
def paginate(options = {})
SimplyPaginate::Collection.create(options[:page] || 1, options[:per_page] || 30, options[:total_entries] || self.length) {|pager|
pager.replace self[pager.offset, pager.per_page].to_a
}
end
end
12 changes: 8 additions & 4 deletions lib/finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ def paginate(options = {})

# paginating finder
SimplyPaginate::Collection.create(page, per_page, total_entries) do |pager|
pager.replace find(:all, :select => options[:select],
:joins => options[:joins], :include => options[:include],
:conditions => options[:conditions], :order => options[:order],
:offset => pager.offset, :limit => per_page)
pager.replace find(:all,
:select => options[:select],
:joins => options[:joins],
:include => options[:include],
:conditions => options[:conditions],
:order => options[:order],
:offset => pager.offset,
:limit => per_page)
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion lib/simply_paginate.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'array'
require 'collection'
require 'view_helpers'
require 'finder'
require 'view_helpers'

ActiveRecord::Base.send :include, SimplyPaginate::Finder
ActionView::Base.send :include, SimplyPaginate::ViewHelpers
3 changes: 3 additions & 0 deletions test/config/database.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sqlite3:
:adapter: sqlite3
:database: ":memory:"
5 changes: 5 additions & 0 deletions test/db/schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ActiveRecord::Schema.define(:version => 1) do
create_table "rectangles", :force => true do |t|
t.string "name", :limit => 30
end
end
3 changes: 3 additions & 0 deletions test/fixtures/rectangle_model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Rectangle < ActiveRecord::Base
named_scope :ordered, :order => 'name'
end
14 changes: 14 additions & 0 deletions test/fixtures/rectangles.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
rectangle_one:
name: 'my name is 1'

rectangle_two:
name: 'my name is 2'

rectangle_three:
name: 'my name is 3'

rectangle_four:
name: 'my name is 4'

rectangle_five:
name: 'my name is 5'
10 changes: 10 additions & 0 deletions test/fixtures/rectangles_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class RectanglesController < ApplicationController
# clear all filters defined in application.rb
skip_filter filter_chain

# second page of paginated results
def index
@rectangles = Rectangle.paginate(:per_page => 2, :page => 2)
render :inline => "<%= pagination_for @rectangles %>", :layout => false
end
end
34 changes: 34 additions & 0 deletions test/functional/pagination_for_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require File.dirname(__FILE__) + '/../test_helper'
require 'action_controller'
require 'action_controller/test_process'

require File.dirname(__FILE__) + '/../fixtures/rectangle_model.rb'
require File.dirname(__FILE__) + '/../fixtures/rectangles_controller.rb'

class UsersControllerTest < ActionController::TestCase

include SimplyAuthenticate::Helpers

def setup
@controller = RectanglesController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new

ActionController::Routing::Routes.draw do |map|
map.root :controller => 'rectangles'
end
end

# test pagination links on second page of paginated results
def test_pagination_for
get :index
assert_response :success
assert_select 'p.pagination'
assert_select 'p.pagination a', :text => /«/
assert_select 'p.pagination a', :text => /»/
assert_select 'p.pagination a', :text => /1/
assert_select 'p.pagination a', :text => /3/
assert_select 'p.pagination strong', :text => /2/
end

end
52 changes: 52 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
ENV['RAILS_ENV'] = 'test'
ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'

require 'test/unit'
require 'rubygems'

# Optional gems
begin
require 'redgreen'
rescue LoadError
end

# Load Rails
require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))

# Setup the database
config = YAML::load(IO.read(File.dirname(__FILE__) + '/config/database.yml'))

Dir.mkdir(File.dirname(__FILE__) + '/log') if !File.exists?(File.dirname(__FILE__) + '/log')
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/log/test.log')

db_adapter =
begin
require 'sqlite3'
'sqlite3'
end

if db_adapter.nil?
raise "Could not select the database adapter. Please install Sqlite3 (gem install sqlite3-ruby)."
end


ActiveRecord::Base.establish_connection(config[db_adapter])

# Load the test database schema
load(File.dirname(__FILE__) + '/db/schema.rb')

# Load the plugin
require File.dirname(__FILE__) + '/../init.rb'

# Setup fixtures
require 'active_support/test_case'
require 'active_record/fixtures'

# Load the plugin's fixtures
Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures"

class Test::Unit::TestCase
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = true
fixtures :all
end
38 changes: 38 additions & 0 deletions test/unit/paginate_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require File.dirname(__FILE__) + '/../test_helper'

require File.dirname(__FILE__) + '/../fixtures/rectangle_model.rb'

class PaginateTest < Test::Unit::TestCase

def test_array_pagination
numbers = [1, 2, 3, 4, 5]

assert_equal SimplyPaginate::Collection, numbers.paginate.class
assert_equal numbers.size, numbers.paginate.total_entries

paginated = numbers.paginate(:page => 2, :per_page => 2)
assert_equal numbers[2..3], paginated
assert_equal 2, paginated.offset
assert_equal 1, paginated.previous_page
assert_equal 3, paginated.next_page
assert_equal 3, paginated.current_results_first
assert_equal 4, paginated.current_results_last

assert_equal numbers[0..1], numbers.paginate(:page => 1, :per_page => 2)
assert_equal numbers[0..3], numbers.paginate(:page => 1, :per_page => 4)
assert_equal numbers[3..4], numbers.paginate(:page => 2, :per_page => 3)
end

def test_active_record_pagination
rectangles = Rectangle.ordered

assert_equal rectangles[0..0], Rectangle.paginate(:page => 1, :per_page => 1, :order => 'name')
assert_equal rectangles[0..2], Rectangle.paginate(:page => 1, :per_page => 3, :order => 'name')
assert_equal rectangles[2..3], Rectangle.paginate(:page => 2, :per_page => 2, :order => 'name')
assert_equal rectangles[0..3], Rectangle.paginate(:page => 1, :per_page => 4, :order => 'name')
assert_equal rectangles[4..4], Rectangle.paginate(:page => 2, :per_page => 4, :order => 'name')

assert_equal rectangles, Rectangle.paginate(:per_page => 100, :order => 'name')
assert_equal rectangles, Rectangle.ordered.paginate(:per_page => 100)
end
end

0 comments on commit 078b4dd

Please sign in to comment.