require 'spec_helper' require 'will_paginate/active_record' require File.expand_path('../activerecord_test_connector', __FILE__) ActiverecordTestConnector.setup abort unless ActiverecordTestConnector.able_to_connect describe WillPaginate::ActiveRecord do extend ActiverecordTestConnector::FixtureSetup fixtures :topics, :replies, :users, :projects, :developers_projects it "should integrate with ActiveRecord::Base" do ActiveRecord::Base.should respond_to(:paginate) end it "should paginate" do lambda { users = User.paginate(:page => 1, :per_page => 5).to_a users.length.should == 5 }.should run_queries(2) end it "should fail when encountering unknown params" do lambda { User.paginate :foo => 'bar', :page => 1, :per_page => 4 }.should raise_error(ArgumentError) end describe "relation" do it "should return a relation" do rel = nil lambda { rel = Developer.paginate(:page => 1) rel.per_page.should == 10 rel.current_page.should == 1 }.should run_queries(0) lambda { rel.total_pages.should == 2 }.should run_queries(1) end it "should keep per-class per_page number" do rel = Developer.order('id').paginate(:page => 1) rel.per_page.should == 10 end it "should be able to change per_page number" do rel = Developer.order('id').paginate(:page => 1).limit(5) rel.per_page.should == 5 end it "remembers pagination in sub-relations" do rel = Topic.paginate(:page => 2, :per_page => 3) lambda { rel.total_entries.should == 4 }.should run_queries(1) rel = rel.mentions_activerecord rel.current_page.should == 2 rel.per_page.should == 3 lambda { rel.total_entries.should == 1 }.should run_queries(1) end it "supports the page() method" do rel = Developer.page('1').order('id') rel.current_page.should == 1 rel.per_page.should == 10 rel.offset.should == 0 rel = rel.limit(5).page(2) rel.per_page.should == 5 rel.offset.should == 5 end it "raises on invalid page number" do lambda { Developer.page('foo') }.should raise_error(ArgumentError) end it "supports first limit() then page()" do rel = Developer.limit(3).page(3) rel.offset.should == 6 end it "supports first page() then limit()" do rel = Developer.page(3).limit(3) rel.offset.should == 6 end it "supports #first" do rel = Developer.order('id').page(2).per_page(4) rel.first.should == users(:dev_5) rel.first(2).should == users(:dev_5, :dev_6) end it "supports #last" do rel = Developer.order('id').page(2).per_page(4) rel.last.should == users(:dev_8) rel.last(2).should == users(:dev_7, :dev_8) rel.page(3).last.should == users(:poor_jamis) end end describe "counting" do it "should guess the total count" do lambda { topics = Topic.paginate :page => 2, :per_page => 3 topics.total_entries.should == 4 }.should run_queries(1) end it "should guess that there are no records" do lambda { topics = Topic.where(:project_id => 999).paginate :page => 1, :per_page => 3 topics.total_entries.should == 0 }.should run_queries(1) end it "forgets count in sub-relations" do lambda { topics = Topic.paginate :page => 1, :per_page => 3 topics.total_entries.should == 4 topics.where('1 = 1').total_entries.should == 4 }.should run_queries(2) end it "supports empty? method" do topics = Topic.paginate :page => 1, :per_page => 3 lambda { topics.should_not be_empty }.should run_queries(1) end it "support empty? for grouped queries" do topics = Topic.group(:project_id).paginate :page => 1, :per_page => 3 lambda { topics.should_not be_empty }.should run_queries(1) end it "supports `size` for grouped queries" do topics = Topic.group(:project_id).paginate :page => 1, :per_page => 3 lambda { topics.size.should == {nil=>2, 1=>2} }.should run_queries(1) end it "overrides total_entries count with a fixed value" do lambda { topics = Topic.paginate :page => 1, :per_page => 3, :total_entries => 999 topics.total_entries.should == 999 # value is kept even in sub-relations topics.where('1 = 1').total_entries.should == 999 }.should run_queries(0) end it "supports a non-int for total_entries" do topics = Topic.paginate :page => 1, :per_page => 3, :total_entries => "999" topics.total_entries.should == 999 end it "removes :include for count" do lambda { developers = Developer.paginate(:page => 1, :per_page => 1).includes(:projects) developers.total_entries.should == 11 $query_sql.last.should_not =~ /\bJOIN\b/ }.should run_queries(1) end it "keeps :include for count when they are referenced in :conditions" do developers = Developer.paginate(:page => 1, :per_page => 1).includes(:projects) with_condition = developers.where('projects.id > 1') with_condition = with_condition.references(:projects) if with_condition.respond_to?(:references) with_condition.total_entries.should == 1 $query_sql.last.should =~ /\bJOIN\b/ end it "should count with group" do Developer.group(:salary).page(1).total_entries.should == 4 end it "should count with select" do Topic.select('title, content').page(1).total_entries.should == 4 end it "removes :reorder for count with group" do Project.group(:id).reorder(:id).page(1).total_entries $query_sql.last.should_not =~ /\ORDER\b/ end it "should not have zero total_pages when the result set is empty" do Developer.where("1 = 2").page(1).total_pages.should == 1 end end it "should not ignore :select parameter when it says DISTINCT" do users = User.select('DISTINCT salary').paginate :page => 2 users.total_entries.should == 5 end describe "paginate_by_sql" do it "should respond" do User.should respond_to(:paginate_by_sql) end it "should paginate" do lambda { sql = "select content from topics where content like '%futurama%'" topics = Topic.paginate_by_sql sql, :page => 1, :per_page => 1 topics.total_entries.should == 1 topics.first.attributes.has_key?('title').should be_false }.should run_queries(2) end it "should respect total_entries setting" do lambda { sql = "select content from topics" topics = Topic.paginate_by_sql sql, :page => 1, :per_page => 1, :total_entries => 999 topics.total_entries.should == 999 }.should run_queries(1) end it "defaults to page 1" do sql = "select content from topics" topics = Topic.paginate_by_sql sql, :page => nil, :per_page => 1 topics.current_page.should == 1 topics.size.should == 1 end it "should strip the order when counting" do expected = topics(:ar) lambda { sql = "select id, title, content from topics order by topics.title" topics = Topic.paginate_by_sql sql, :page => 1, :per_page => 2 topics.first.should == expected }.should run_queries(2) $query_sql.last.should include('COUNT') $query_sql.last.should_not include('order by topics.title') end it "shouldn't change the original query string" do query = 'select * from topics where 1 = 2' original_query = query.dup Topic.paginate_by_sql(query, :page => 1) query.should == original_query end end it "doesn't mangle options" do options = { :page => 1 } options.expects(:delete).never options_before = options.dup Topic.paginate(options) options.should == options_before end it "should get first page of Topics with a single query" do lambda { result = Topic.paginate :page => nil result.to_a # trigger loading of records 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 describe "associations" do 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 = ignore_deprecation { dhh.projects.includes(:topics).order('projects.name').paginate(:page => 1) } result.to_a.should == expected_name_ordered result.total_entries.should == 2 }.should run_queries(2) # with explicit order result = dhh.projects.paginate(:page => 1).reorder('projects.id') result.should == expected_id_ordered result.total_entries.should == 2 lambda { dhh.projects.order('projects.id').limit(4).to_a }.should_not raise_error result = dhh.projects.paginate(:page => 1, :per_page => 4).reorder('projects.id') 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).reorder('replies.id ASC').should == expected.reverse end it "should paginate through association extension" do project = Project.order('id').first expected = [replies(:brave)] lambda { result = project.replies.only_recent.paginate(: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.where('developers_projects.project_id = 1').joins(join_sql).paginate(:page => 1) result.to_a # trigger loading of records 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.where('developers_projects.project_id = 1').joins(join_sql).paginate(:page => 1) result.should == expected result.total_entries.should == 2 }.should run_queries(1) end it "should paginate with group" do result = nil lambda { result = Developer.select('salary').order('salary').group('salary'). paginate(:page => 1, :per_page => 10).to_a }.should run_queries(1) expected = users(:david, :jamis, :dev_10, :poor_jamis).map(&:salary).sort result.map(&:salary).should == expected end it "should not paginate with dynamic finder" do lambda { Developer.paginate_by_salary(100000, :page => 1, :per_page => 5) }.should raise_error(NoMethodError) end describe "scopes" 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 = ignore_deprecation { 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 not paginate an array of IDs" do lambda { Developer.paginate((1..8).to_a, :per_page => 3, :page => 2, :order => 'id') }.should raise_error(ArgumentError) end it "errors out for invalid values" do |variable| lambda { # page that results in an offset larger than BIGINT Project.page(307445734561825862) }.should raise_error(WillPaginate::InvalidPage, "invalid offset: 9223372036854775830") end end