Reputation:
I use Rails 4.1.0 I want to sort profiles based on completeness so for this i have this code in my profile model
def completeness
percent = 10
percent += 15 if self.useravatar.present?
percent += 5 if self.summary.present?
percent += 5 if self.profile_languages.present?
percent += 10 if self.educations.present?
return percent
end
def self.sorted_by_completeness
Profile.all.sort_by(&:completeness).reverse
end
and this is my profiles index controller
def index
@profiles = Profile.paginate(page: params[:page], per_page: 10).sorted_by_completeness
end
But when I'm trying to access profile index page I'm getting this error undefined method
total_pages' for #` can someone tell me why i'm getting this error
Upvotes: 1
Views: 326
Reputation: 7744
Two problems with your approach:
In your view, you're probably using will_paginate(@projects)
which fails because that method isn't for arrays.
You're sorting the rows after they've been paginated. This means that however you sort them, the sorting will be applied to only one page at a time. Thus whatever solution you choose, you need to specify the order before you paginate the data.
A solution would be to obviously use pagination on your array. Something like:
require 'will_paginate/array' # to enable pagination on an array
@projects = Project.sorting_method.paginate(arguments)
However for a large number of projects, that will soon become impractical.
The best way IMO, to enable pagination, is to have a completeness
field in your projects
table that gets updated anytime it should. Then, you could very easily do the following:
@projects = Project.order("completeness DESC").paginate(arguments)
Another solution would be to use sub-queries in a SQL select statement to calculate each project's completeness. Something like:
lcq = "SELECT COUNT(*) FROM profile_languages WHERE
(profile_languages.project_id = projects.id)"
ecq = "SELECT COUNT(*) FROM educations WHERE
(educations.project_id = projects.id)"
@projects = \
Project.select(
"#{project.column_names.join(',')},
SELECT(
(CASE useravatar WHEN NULL THEN 0 ELSE 15 END) +
(CASE summary WHEN NULL THEN 0 ELSE 5 END) +
(CASE (#{lcq}) WHEN 0 THEN 0 ELSE 5 END) +
(CASE (#{ecq}) WHEN 0 THEN 0 ELSE 10 END)
) AS completeness").
order("completeness DESC").
paginate(arguments)
With some debugging, that should work. I don't like it though. A bunch of code that is hard to read and sub-queries that might slow things down a lot.
Upvotes: 1
Reputation: 3427
sort_by
will return Array
collection
to test open rails console and type
Profile.all.sort_by(&:completeness).reverse.class
for will_paginate to work with Array you need to require 'will_paginate/array'
so just change your profile_controller as
require 'will_paginate/array'
class ProfileController < ApplicationController
def index
@profiles = Profile.sorted_by_completeness.paginate(page: params[:page], per_page: 10)
end
end
Upvotes: 2
Reputation: 4251
def index
@profiles = Profile.sorted_by_completeness(params[:page])
end
In your model file:
def self.sorted_by_completeness(current_page)
Profile.all.sort_by(&:completeness).reverse.paginate(page: current_page)
end
TO define your per_page limit use below code:
#app/config/initializers/will_paginate.rb
WillPaginate.per_page = 10
Else if you want to define it in model level:
#profile.rb
class Profile < ActiveRecord::Base
self.per_page = 10
#your other model level logic goes here.
end
Upvotes: 0