classic_pagination 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,135 @@
1
+ module ActionView
2
+ module Helpers
3
+ # Provides methods for linking to ActionController::Pagination objects using a simple generator API. You can optionally
4
+ # also build your links manually using ActionView::Helpers::AssetHelper#link_to like so:
5
+ #
6
+ # <%= link_to "Previous page", { :page => paginator.current.previous } if paginator.current.previous %>
7
+ # <%= link_to "Next page", { :page => paginator.current.next } if paginator.current.next %>
8
+ module PaginationHelper
9
+ unless const_defined?(:DEFAULT_OPTIONS)
10
+ DEFAULT_OPTIONS = {
11
+ :name => :page,
12
+ :window_size => 2,
13
+ :always_show_anchors => true,
14
+ :link_to_current_page => false,
15
+ :params => {}
16
+ }
17
+ end
18
+
19
+ # Creates a basic HTML link bar for the given +paginator+. Links will be created
20
+ # for the next and/or previous page and for a number of other pages around the current
21
+ # pages position. The +html_options+ hash is passed to +link_to+ when the links are created.
22
+ #
23
+ # ==== Options
24
+ # <tt>:name</tt>:: the routing name for this paginator
25
+ # (defaults to +page+)
26
+ # <tt>:prefix</tt>:: prefix for pagination links
27
+ # (i.e. Older Pages: 1 2 3 4)
28
+ # <tt>:suffix</tt>:: suffix for pagination links
29
+ # (i.e. 1 2 3 4 <- Older Pages)
30
+ # <tt>:window_size</tt>:: the number of pages to show around
31
+ # the current page (defaults to <tt>2</tt>)
32
+ # <tt>:always_show_anchors</tt>:: whether or not the first and last
33
+ # pages should always be shown
34
+ # (defaults to +true+)
35
+ # <tt>:link_to_current_page</tt>:: whether or not the current page
36
+ # should be linked to (defaults to
37
+ # +false+)
38
+ # <tt>:params</tt>:: any additional routing parameters
39
+ # for page URLs
40
+ #
41
+ # ==== Examples
42
+ # # We'll assume we have a paginator setup in @person_pages...
43
+ #
44
+ # pagination_links(@person_pages)
45
+ # # => 1 <a href="/?page=2/">2</a> <a href="/?page=3/">3</a> ... <a href="/?page=10/">10</a>
46
+ #
47
+ # pagination_links(@person_pages, :link_to_current_page => true)
48
+ # # => <a href="/?page=1/">1</a> <a href="/?page=2/">2</a> <a href="/?page=3/">3</a> ... <a href="/?page=10/">10</a>
49
+ #
50
+ # pagination_links(@person_pages, :always_show_anchors => false)
51
+ # # => 1 <a href="/?page=2/">2</a> <a href="/?page=3/">3</a>
52
+ #
53
+ # pagination_links(@person_pages, :window_size => 1)
54
+ # # => 1 <a href="/?page=2/">2</a> ... <a href="/?page=10/">10</a>
55
+ #
56
+ # pagination_links(@person_pages, :params => { :viewer => "flash" })
57
+ # # => 1 <a href="/?page=2&amp;viewer=flash/">2</a> <a href="/?page=3&amp;viewer=flash/">3</a> ...
58
+ # # <a href="/?page=10&amp;viewer=flash/">10</a>
59
+ def pagination_links(paginator, options={}, html_options={})
60
+ name = options[:name] || DEFAULT_OPTIONS[:name]
61
+ params = (options[:params] || DEFAULT_OPTIONS[:params]).clone
62
+
63
+ prefix = options[:prefix] || ''
64
+ suffix = options[:suffix] || ''
65
+
66
+ pagination_links_each(paginator, options, prefix, suffix) do |n|
67
+ params[name] = n
68
+ link_to(n.to_s, params, html_options)
69
+ end
70
+ end
71
+
72
+ # Iterate through the pages of a given +paginator+, invoking a
73
+ # block for each page number that needs to be rendered as a link.
74
+ #
75
+ # ==== Options
76
+ # <tt>:window_size</tt>:: the number of pages to show around
77
+ # the current page (defaults to +2+)
78
+ # <tt>:always_show_anchors</tt>:: whether or not the first and last
79
+ # pages should always be shown
80
+ # (defaults to +true+)
81
+ # <tt>:link_to_current_page</tt>:: whether or not the current page
82
+ # should be linked to (defaults to
83
+ # +false+)
84
+ #
85
+ # ==== Example
86
+ # # Turn paginated links into an Ajax call
87
+ # pagination_links_each(paginator, page_options) do |link|
88
+ # options = { :url => {:action => 'list'}, :update => 'results' }
89
+ # html_options = { :href => url_for(:action => 'list') }
90
+ #
91
+ # link_to_remote(link.to_s, options, html_options)
92
+ # end
93
+ def pagination_links_each(paginator, options, prefix = nil, suffix = nil)
94
+ options = DEFAULT_OPTIONS.merge(options)
95
+ link_to_current_page = options[:link_to_current_page]
96
+ always_show_anchors = options[:always_show_anchors]
97
+
98
+ current_page = paginator.current_page
99
+ window_pages = current_page.window(options[:window_size]).pages
100
+ return if window_pages.length <= 1 unless link_to_current_page
101
+
102
+ first, last = paginator.first, paginator.last
103
+
104
+ html = ''
105
+
106
+ html << prefix if prefix
107
+
108
+ if always_show_anchors and not (wp_first = window_pages[0]).first?
109
+ html << yield(first.number)
110
+ html << ' ... ' if wp_first.number - first.number > 1
111
+ html << ' '
112
+ end
113
+
114
+ window_pages.each do |page|
115
+ if current_page == page && !link_to_current_page
116
+ html << page.number.to_s
117
+ else
118
+ html << yield(page.number)
119
+ end
120
+ html << ' '
121
+ end
122
+
123
+ if always_show_anchors and not (wp_last = window_pages[-1]).last?
124
+ html << ' ... ' if last.number - wp_last.number > 1
125
+ html << yield(last.number)
126
+ end
127
+
128
+ html << suffix if suffix
129
+
130
+ html
131
+ end
132
+
133
+ end # PaginationHelper
134
+ end # Helpers
135
+ end # ActionView
@@ -0,0 +1,24 @@
1
+ thirty_seven_signals:
2
+ id: 1
3
+ name: 37Signals
4
+ rating: 4
5
+
6
+ TextDrive:
7
+ id: 2
8
+ name: TextDrive
9
+ rating: 4
10
+
11
+ PlanetArgon:
12
+ id: 3
13
+ name: Planet Argon
14
+ rating: 4
15
+
16
+ Google:
17
+ id: 4
18
+ name: Google
19
+ rating: 4
20
+
21
+ Ionist:
22
+ id: 5
23
+ name: Ioni.st
24
+ rating: 4
@@ -0,0 +1,9 @@
1
+ class Company < ActiveRecord::Base
2
+ attr_protected :rating
3
+ set_sequence_name :companies_nonstd_seq
4
+
5
+ validates_presence_of :name
6
+ def validate
7
+ errors.add('rating', 'rating should not be 2') if rating == 2
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ class Developer < ActiveRecord::Base
2
+ has_and_belongs_to_many :projects
3
+ end
4
+
5
+ class DeVeLoPeR < ActiveRecord::Base
6
+ set_table_name "developers"
7
+ end
@@ -0,0 +1,21 @@
1
+ david:
2
+ id: 1
3
+ name: David
4
+ salary: 80000
5
+
6
+ jamis:
7
+ id: 2
8
+ name: Jamis
9
+ salary: 150000
10
+
11
+ <% for digit in 3..10 %>
12
+ dev_<%= digit %>:
13
+ id: <%= digit %>
14
+ name: fixture_<%= digit %>
15
+ salary: 100000
16
+ <% end %>
17
+
18
+ poor_jamis:
19
+ id: 11
20
+ name: Jamis
21
+ salary: 9000
@@ -0,0 +1,13 @@
1
+ david_action_controller:
2
+ developer_id: 1
3
+ project_id: 2
4
+ joined_on: 2004-10-10
5
+
6
+ david_active_record:
7
+ developer_id: 1
8
+ project_id: 1
9
+ joined_on: 2004-10-10
10
+
11
+ jamis_active_record:
12
+ developer_id: 2
13
+ project_id: 1
@@ -0,0 +1,3 @@
1
+ class Project < ActiveRecord::Base
2
+ has_and_belongs_to_many :developers, :uniq => true
3
+ end
@@ -0,0 +1,7 @@
1
+ action_controller:
2
+ id: 2
3
+ name: Active Controller
4
+
5
+ active_record:
6
+ id: 1
7
+ name: Active Record
@@ -0,0 +1,13 @@
1
+ witty_retort:
2
+ id: 1
3
+ topic_id: 1
4
+ content: Birdman is better!
5
+ created_at: <%= 6.hours.ago.to_s(:db) %>
6
+ updated_at: nil
7
+
8
+ another:
9
+ id: 2
10
+ topic_id: 2
11
+ content: Nuh uh!
12
+ created_at: <%= 1.hour.ago.to_s(:db) %>
13
+ updated_at: nil
@@ -0,0 +1,5 @@
1
+ class Reply < ActiveRecord::Base
2
+ belongs_to :topic, :include => [:replies]
3
+
4
+ validates_presence_of :content
5
+ end
@@ -0,0 +1,42 @@
1
+ CREATE TABLE 'companies' (
2
+ 'id' INTEGER PRIMARY KEY NOT NULL,
3
+ 'name' TEXT DEFAULT NULL,
4
+ 'rating' INTEGER DEFAULT 1
5
+ );
6
+
7
+ CREATE TABLE 'replies' (
8
+ 'id' INTEGER PRIMARY KEY NOT NULL,
9
+ 'content' text,
10
+ 'created_at' datetime,
11
+ 'updated_at' datetime,
12
+ 'topic_id' integer
13
+ );
14
+
15
+ CREATE TABLE 'topics' (
16
+ 'id' INTEGER PRIMARY KEY NOT NULL,
17
+ 'title' varchar(255),
18
+ 'subtitle' varchar(255),
19
+ 'content' text,
20
+ 'created_at' datetime,
21
+ 'updated_at' datetime
22
+ );
23
+
24
+ CREATE TABLE 'developers' (
25
+ 'id' INTEGER PRIMARY KEY NOT NULL,
26
+ 'name' TEXT DEFAULT NULL,
27
+ 'salary' INTEGER DEFAULT 70000,
28
+ 'created_at' DATETIME DEFAULT NULL,
29
+ 'updated_at' DATETIME DEFAULT NULL
30
+ );
31
+
32
+ CREATE TABLE 'projects' (
33
+ 'id' INTEGER PRIMARY KEY NOT NULL,
34
+ 'name' TEXT DEFAULT NULL
35
+ );
36
+
37
+ CREATE TABLE 'developers_projects' (
38
+ 'developer_id' INTEGER NOT NULL,
39
+ 'project_id' INTEGER NOT NULL,
40
+ 'joined_on' DATE DEFAULT NULL,
41
+ 'access_level' INTEGER DEFAULT 1
42
+ );
@@ -0,0 +1,3 @@
1
+ class Topic < ActiveRecord::Base
2
+ has_many :replies, :include => [:user], :dependent => :destroy
3
+ end
@@ -0,0 +1,22 @@
1
+ futurama:
2
+ id: 1
3
+ title: Isnt futurama awesome?
4
+ subtitle: It really is, isnt it.
5
+ content: I like futurama
6
+ created_at: <%= 1.day.ago.to_s(:db) %>
7
+ updated_at:
8
+
9
+ harvey_birdman:
10
+ id: 2
11
+ title: Harvey Birdman is the king of all men
12
+ subtitle: yup
13
+ content: It really is
14
+ created_at: <%= 2.hours.ago.to_s(:db) %>
15
+ updated_at:
16
+
17
+ rails:
18
+ id: 3
19
+ title: Rails is nice
20
+ subtitle: It makes me happy
21
+ content: except when I have to hack internals to fix pagination. even then really.
22
+ created_at: <%= 20.minutes.ago.to_s(:db) %>
data/test/helper.rb ADDED
@@ -0,0 +1,117 @@
1
+ require 'test/unit'
2
+
3
+ unless defined?(ActiveRecord)
4
+ plugin_root = File.join(File.dirname(__FILE__), '..')
5
+
6
+ # first look for a symlink to a copy of the framework
7
+ if framework_root = ["#{plugin_root}/rails", "#{plugin_root}/../../rails"].find { |p| File.directory? p }
8
+ puts "found framework root: #{framework_root}"
9
+ # this allows for a plugin to be tested outside an app
10
+ $:.unshift "#{framework_root}/activesupport/lib", "#{framework_root}/activerecord/lib", "#{framework_root}/actionpack/lib"
11
+ else
12
+ # is the plugin installed in an application?
13
+ app_root = plugin_root + '/../../..'
14
+
15
+ if File.directory? app_root + '/config'
16
+ puts 'using config/boot.rb'
17
+ ENV['RAILS_ENV'] = 'test'
18
+ require File.expand_path(app_root + '/config/boot')
19
+ else
20
+ # simply use installed gems if available
21
+ puts 'using rubygems'
22
+ require 'rubygems'
23
+ gem 'actionpack'; gem 'activerecord'
24
+ end
25
+ end
26
+
27
+ %w(action_pack active_record action_controller active_record/fixtures action_controller/test_process).each {|f| require f}
28
+
29
+ Dependencies.load_paths.unshift "#{plugin_root}/lib"
30
+ end
31
+
32
+ # Define the connector
33
+ class ActiveRecordTestConnector
34
+ cattr_accessor :able_to_connect
35
+ cattr_accessor :connected
36
+
37
+ # Set our defaults
38
+ self.connected = false
39
+ self.able_to_connect = true
40
+
41
+ class << self
42
+ def setup
43
+ unless self.connected || !self.able_to_connect
44
+ setup_connection
45
+ load_schema
46
+ require_fixture_models
47
+ self.connected = true
48
+ end
49
+ rescue Exception => e # errors from ActiveRecord setup
50
+ $stderr.puts "\nSkipping ActiveRecord assertion tests: #{e}"
51
+ #$stderr.puts " #{e.backtrace.join("\n ")}\n"
52
+ self.able_to_connect = false
53
+ end
54
+
55
+ private
56
+
57
+ def setup_connection
58
+ if Object.const_defined?(:ActiveRecord)
59
+ defaults = { :database => ':memory:' }
60
+ begin
61
+ options = defaults.merge :adapter => 'sqlite3', :timeout => 500
62
+ ActiveRecord::Base.establish_connection(options)
63
+ ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' => options }
64
+ ActiveRecord::Base.connection
65
+ rescue Exception # errors from establishing a connection
66
+ $stderr.puts 'SQLite 3 unavailable; trying SQLite 2.'
67
+ options = defaults.merge :adapter => 'sqlite'
68
+ ActiveRecord::Base.establish_connection(options)
69
+ ActiveRecord::Base.configurations = { 'sqlite2_ar_integration' => options }
70
+ ActiveRecord::Base.connection
71
+ end
72
+
73
+ Object.send(:const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')) unless Object.const_defined?(:QUOTED_TYPE)
74
+ else
75
+ raise "Can't setup connection since ActiveRecord isn't loaded."
76
+ end
77
+ end
78
+
79
+ # Load actionpack sqlite tables
80
+ def load_schema
81
+ File.read(File.dirname(__FILE__) + "/fixtures/schema.sql").split(';').each do |sql|
82
+ ActiveRecord::Base.connection.execute(sql) unless sql.blank?
83
+ end
84
+ end
85
+
86
+ def require_fixture_models
87
+ Dir.glob(File.dirname(__FILE__) + "/fixtures/*.rb").each {|f| require f}
88
+ end
89
+ end
90
+ end
91
+
92
+ # Test case for inheritance
93
+ class ActiveRecordTestCase < Test::Unit::TestCase
94
+ # Set our fixture path
95
+ if ActiveRecordTestConnector.able_to_connect
96
+ self.fixture_path = "#{File.dirname(__FILE__)}/fixtures/"
97
+ self.use_transactional_fixtures = false
98
+ end
99
+
100
+ def self.fixtures(*args)
101
+ super if ActiveRecordTestConnector.connected
102
+ end
103
+
104
+ def run(*args)
105
+ super if ActiveRecordTestConnector.connected
106
+ end
107
+
108
+ # Default so Test::Unit::TestCase doesn't complain
109
+ def test_truth
110
+ end
111
+ end
112
+
113
+ ActiveRecordTestConnector.setup
114
+ ActionController::Routing::Routes.reload rescue nil
115
+ ActionController::Routing::Routes.draw do |map|
116
+ map.connect ':controller/:action/:id'
117
+ end
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require File.dirname(__FILE__) + '/../init'
3
+
4
+ class PaginationHelperTest < Test::Unit::TestCase
5
+ include ActionController::Pagination
6
+ include ActionView::Helpers::PaginationHelper
7
+ include ActionView::Helpers::UrlHelper
8
+ include ActionView::Helpers::TagHelper
9
+
10
+ def setup
11
+ @controller = Class.new do
12
+ attr_accessor :url, :request
13
+ def url_for(options, *parameters_for_method_reference)
14
+ url
15
+ end
16
+ end
17
+ @controller = @controller.new
18
+ @controller.url = "http://www.example.com"
19
+ end
20
+
21
+ def test_pagination_links
22
+ total, per_page, page = 30, 10, 1
23
+ output = pagination_links Paginator.new(@controller, total, per_page, page)
24
+ assert_equal "1 <a href=\"http://www.example.com\">2</a> <a href=\"http://www.example.com\">3</a> ", output
25
+ end
26
+
27
+ def test_pagination_links_with_prefix
28
+ total, per_page, page = 30, 10, 1
29
+ output = pagination_links Paginator.new(@controller, total, per_page, page), :prefix => 'Newer '
30
+ assert_equal "Newer 1 <a href=\"http://www.example.com\">2</a> <a href=\"http://www.example.com\">3</a> ", output
31
+ end
32
+
33
+ def test_pagination_links_with_suffix
34
+ total, per_page, page = 30, 10, 1
35
+ output = pagination_links Paginator.new(@controller, total, per_page, page), :suffix => 'Older'
36
+ assert_equal "1 <a href=\"http://www.example.com\">2</a> <a href=\"http://www.example.com\">3</a> Older", output
37
+ end
38
+ end
@@ -0,0 +1,177 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require File.dirname(__FILE__) + '/../init'
3
+
4
+ class PaginationTest < ActiveRecordTestCase
5
+ fixtures :topics, :replies, :developers, :projects, :developers_projects
6
+
7
+ class PaginationController < ActionController::Base
8
+ if respond_to? :view_paths=
9
+ self.view_paths = [ "#{File.dirname(__FILE__)}/../fixtures/" ]
10
+ else
11
+ self.template_root = [ "#{File.dirname(__FILE__)}/../fixtures/" ]
12
+ end
13
+
14
+ def simple_paginate
15
+ @topic_pages, @topics = paginate(:topics)
16
+ render :nothing => true
17
+ end
18
+
19
+ def paginate_with_per_page
20
+ @topic_pages, @topics = paginate(:topics, :per_page => 1)
21
+ render :nothing => true
22
+ end
23
+
24
+ def paginate_with_order
25
+ @topic_pages, @topics = paginate(:topics, :order => 'created_at asc')
26
+ render :nothing => true
27
+ end
28
+
29
+ def paginate_with_order_by
30
+ @topic_pages, @topics = paginate(:topics, :order_by => 'created_at asc')
31
+ render :nothing => true
32
+ end
33
+
34
+ def paginate_with_include_and_order
35
+ @topic_pages, @topics = paginate(:topics, :include => :replies, :order => 'replies.created_at asc, topics.created_at asc')
36
+ render :nothing => true
37
+ end
38
+
39
+ def paginate_with_conditions
40
+ @topic_pages, @topics = paginate(:topics, :conditions => ["created_at > ?", 30.minutes.ago])
41
+ render :nothing => true
42
+ end
43
+
44
+ def paginate_with_class_name
45
+ @developer_pages, @developers = paginate(:developers, :class_name => "DeVeLoPeR")
46
+ render :nothing => true
47
+ end
48
+
49
+ def paginate_with_singular_name
50
+ @developer_pages, @developers = paginate()
51
+ render :nothing => true
52
+ end
53
+
54
+ def paginate_with_joins
55
+ @developer_pages, @developers = paginate(:developers,
56
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
57
+ :conditions => 'project_id=1')
58
+ render :nothing => true
59
+ end
60
+
61
+ def paginate_with_join
62
+ @developer_pages, @developers = paginate(:developers,
63
+ :join => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
64
+ :conditions => 'project_id=1')
65
+ render :nothing => true
66
+ end
67
+
68
+ def paginate_with_join_and_count
69
+ @developer_pages, @developers = paginate(:developers,
70
+ :join => 'd LEFT JOIN developers_projects ON d.id = developers_projects.developer_id',
71
+ :conditions => 'project_id=1',
72
+ :count => "d.id")
73
+ render :nothing => true
74
+ end
75
+
76
+ def paginate_with_join_and_group
77
+ @developer_pages, @developers = paginate(:developers,
78
+ :join => 'INNER JOIN developers_projects ON developers.id = developers_projects.developer_id',
79
+ :group => 'developers.id')
80
+ render :nothing => true
81
+ end
82
+
83
+ def rescue_errors(e) raise e end
84
+
85
+ def rescue_action(e) raise end
86
+
87
+ end
88
+
89
+ def setup
90
+ @controller = PaginationController.new
91
+ @request = ActionController::TestRequest.new
92
+ @response = ActionController::TestResponse.new
93
+ super
94
+ end
95
+
96
+ # Single Action Pagination Tests
97
+
98
+ def test_simple_paginate
99
+ get :simple_paginate
100
+ assert_equal 1, assigns(:topic_pages).page_count
101
+ assert_equal 3, assigns(:topics).size
102
+ end
103
+
104
+ def test_paginate_with_per_page
105
+ get :paginate_with_per_page
106
+ assert_equal 1, assigns(:topics).size
107
+ assert_equal 3, assigns(:topic_pages).page_count
108
+ end
109
+
110
+ def test_paginate_with_order
111
+ get :paginate_with_order
112
+ expected = [topics(:futurama),
113
+ topics(:harvey_birdman),
114
+ topics(:rails)]
115
+ assert_equal expected, assigns(:topics)
116
+ assert_equal 1, assigns(:topic_pages).page_count
117
+ end
118
+
119
+ def test_paginate_with_order_by
120
+ get :paginate_with_order
121
+ expected = assigns(:topics)
122
+ get :paginate_with_order_by
123
+ assert_equal expected, assigns(:topics)
124
+ assert_equal 1, assigns(:topic_pages).page_count
125
+ end
126
+
127
+ def test_paginate_with_conditions
128
+ get :paginate_with_conditions
129
+ expected = [topics(:rails)]
130
+ assert_equal expected, assigns(:topics)
131
+ assert_equal 1, assigns(:topic_pages).page_count
132
+ end
133
+
134
+ def test_paginate_with_class_name
135
+ get :paginate_with_class_name
136
+
137
+ assert assigns(:developers).size > 0
138
+ assert_equal DeVeLoPeR, assigns(:developers).first.class
139
+ end
140
+
141
+ def test_paginate_with_joins
142
+ get :paginate_with_joins
143
+ assert_equal 2, assigns(:developers).size
144
+ developer_names = assigns(:developers).map { |d| d.name }
145
+ assert developer_names.include?('David')
146
+ assert developer_names.include?('Jamis')
147
+ end
148
+
149
+ def test_paginate_with_join_and_conditions
150
+ get :paginate_with_joins
151
+ expected = assigns(:developers)
152
+ get :paginate_with_join
153
+ assert_equal expected, assigns(:developers)
154
+ end
155
+
156
+ def test_paginate_with_join_and_count
157
+ get :paginate_with_joins
158
+ expected = assigns(:developers)
159
+ get :paginate_with_join_and_count
160
+ assert_equal expected, assigns(:developers)
161
+ end
162
+
163
+ def test_paginate_with_include_and_order
164
+ get :paginate_with_include_and_order
165
+ expected = Topic.find(:all, :include => 'replies', :order => 'replies.created_at asc, topics.created_at asc', :limit => 10)
166
+ assert_equal expected, assigns(:topics)
167
+ end
168
+
169
+ def test_paginate_with_join_and_group
170
+ get :paginate_with_join_and_group
171
+ assert_equal 2, assigns(:developers).size
172
+ assert_equal 2, assigns(:developer_pages).item_count
173
+ developer_names = assigns(:developers).map { |d| d.name }
174
+ assert developer_names.include?('David')
175
+ assert developer_names.include?('Jamis')
176
+ end
177
+ end