Pagination breaks large datasets into manageable chunks, and both Kaminari and will_paginate do that job well for Rails. Migrating from one to the other touches models, controllers, views, and anywhere pagination metadata gets serialized into an API response. Here’s the full path.
Basic setup
Replace Kaminari with will_paginate in the Gemfile:
1
2
3
4
5
6
7
8
# Gemfile# Remove or comment out# gem 'kaminari'# Add will_paginategem'will_paginate'# Optional: Add Bootstrap stylinggem'will_paginate-bootstrap-style'# if using Bootstrap
1
bundle install
Model changes
Basic pagination configuration
1
2
3
4
5
6
7
8
9
# Before (Kaminari)classPost<ApplicationRecordpaginates_per25end# After (will_paginate)classPost<ApplicationRecordself.per_page=25end
Maximum page size limits
Kaminari’s max_paginates_per has no direct equivalent in will_paginate; a small concern reproduces it:
# app/models/concerns/pagination_limiter.rbmodulePaginationLimiterextendActiveSupport::Concernclass_methodsdodefmax_per_page@max_per_page||=100enddefmax_per_page=(value)@max_per_page=valueendendincludeddodefself.paginate(options={})options[:per_page]=[options.fetch(:per_page,self.per_page).to_i,max_per_page].minsuper(options)endendend# In your modelclassPost<ApplicationRecordincludePaginationLimiterself.max_per_page=50self.per_page=25end
Controller updates
1
2
3
4
5
6
7
8
9
# Before (Kaminari)defindex@posts=Post.page(params[:page]).per(25)end# After (will_paginate)defindex@posts=Post.paginate(page: params[:page],per_page: 25)end
For paginating a plain array:
1
2
3
4
5
6
7
8
9
# Before (Kaminari)@array=Kaminari.paginate_array(my_array).page(params[:page]).per(25)# After (will_paginate)@array=WillPaginate::Collection.create(params[:page]||1,25)do|pager|result=my_array[pager.offset,pager.per_page]||[]pager.replace(result)pager.total_entries=my_array.lengthend
View modifications
1
2
3
4
5
<%# Before (Kaminari) %><%=paginate@posts%><%# After (will_paginate) %><%=will_paginate@posts%>
A custom Kaminari template needs a matching will_paginate renderer. Here’s a Bootstrap-style example:
# Before (Kaminari)@empty=Model.none.page(1)@empty.total_count# => 0# After (will_paginate)@empty=Model.none.paginate(page: 1)@empty.total_entries# => 0
Testing considerations
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# spec/support/pagination_helper.rbmodulePaginationHelperdefexpect_pagination(collection,options={})expect(collection).torespond_to(:total_entries)expect(collection).torespond_to(:current_page)expect(collection.current_page).toeq(options[:page]||1)expect(collection.per_page).toeq(options[:per_page]||25)endend# In your testsRSpec.describePostsController,type: :controllerdoincludePaginationHelperit"paginates the posts"doget:index,params: {page: 2,per_page: 10}expect_pagination(assigns(:posts),page: 2,per_page: 10)endend
Chase down every occurrence of these before considering the migration done; they’re the failures that don’t show up until a page renders one count short or a spec compares against a method that no longer exists.