Поскольку ваш метод rank_sum
не взаимодействует напрямую с экземпляром модели Video
(например, одним конкретным видео), а вместо этого просто выполняет некоторые вычисления, я бы сделал его методом класса. Это также делает так, чтобы ваш метод не перезаписывал собственный метод rank_sum
Active Record, который он создал на основе столбцов в вашей базе данных.
Изменив некоторые имена для большей наглядности и очистив оператор return
в вашем первом методе, я бы попробовал что-то вроде этого:
class Video < ActiveRecord::Base
before_update :update_rank_sum
def self.calculate_rank_sum(score, created_at)
order = Math.log10(([score.abs,1].max))
if score > 0
sign = 1
elsif score < 0
sign = -1
else
sign = 0
end
seconds = created_at - DateTime.new(1970,1,1)
long_num = order + sign * seconds / 45000
(long_num * 10**7).round.to_f / (10**7)
end
def update_rank_sum
self.rank_sum = Video.calculate_rank_sum(self.vote_sum, self.created_at)
end
end
Это будет пересчитывать столбец rank_sum
при каждом сохранении записи. Если вы хотите пересчитать его только в случае изменения определенного поля, вы можете реализовать эту логику в обратном вызове update_rank_sum
. Например, чтобы вычислить новый rank_sum
, только если vote_sum
был изменен:
def update_rank_sum
if self.vote_sum_changed?
self.rank_sum = Video.calculate_rank_sum(self.vote_sum, self.created_at)
end
true
end
Вы можете заменить vote_sum
в vote_sum_changed?
на любой столбец, который хотите проверить.
[Редактировать] Вам может потребоваться вернуть значение не false
или nil
в обратном вызове, чтобы убедиться, что запись все еще сохраняется (если вы возвращаете false из обратного вызова before_
, это отменяет сохранение). Я обновил последний блок кода.