проблемы с обратным вызовом after_update - PullRequest
0 голосов
/ 04 июля 2010

Я пытаюсь пересчитать проценты в обратном вызове after_update моей модели.

  def update_percentages
    if self.likes_changed? or self.dislikes_changed?
      total = self.likes + self.dislikes

      self.likes_percent = (self.likes / total) * 100
      self.dislikes_percent = (self.dislikes / total) * 100
      self.save
    end
  end

Это не работает.Процент всегда получается как 100 или 0, что полностью разрушает все.

Где я проскальзываю?Я гарантирую, что self.likes и self.dislikes увеличиваются правильно.

Ответы [ 3 ]

5 голосов
/ 04 июля 2010

Проблема

Когда вы делите целое число на целое число (или целочисленное деление ), большинство языков программирования, включая Ruby, предполагают, что вы хотите, чтобы ваш результат был целым числом.Это в основном связано с историей, поскольку при представлении чисел на нижнем уровне целое число сильно отличается от числа с десятичной точкой, а деление на целые числа происходит на намного быстрее.Таким образом, ваш процент, число от 0 до 1, имеет десятичную усеченность и поэтому становится либо 0, либо 1. При умножении на 100 становится либо 0, либо 100.

Общее решение

Если любое из чисел в делении не является целым числом, то целочисленное деление не будет выполнено.Альтернативой является число с десятичной точкой.Существует несколько типов чисел, подобных этому, но обычно их называют числами с плавающей запятой, а в Ruby наиболее типичное число с плавающей запятой относится к классу Float.

1.0.class.ancestors
  # => [Float, Precision, Numeric, Comparable, Object, Kernel]

1.class.ancestors
  # => [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]

В моделях Rails,float представлен классом Ruby Float, а десятичный - классом Ruby BigDecimal.Разница в том, что BigDecimals гораздо более точны (то есть могут быть использованы для денег).

Как правило, вы можете "типизировать" свой номер до числа с плавающей запятой, что означает, что вы больше не будете делать целочисленное деление.Затем, при необходимости, вы можете преобразовать его обратно в целое число после вычислений.

x = 20              # => 20
y = 30              # => 30
y.to_f              # => 30.0

x.class             # => Fixnum
y.class             # => Fixnum
y.to_f.class        # => Float

20 / 30             # => 0
20 / 30.0           # => 0.666666666666667

x / y               # => 0
x / y.to_f          # => 0.666666666666667

(x / y.to_f).round  # => 1

Решение для вас

В вашем случае, если вы хотите получить целочисленные результаты (т.е. 42 для 42%) Я думаю, что самый простой способ сделать это было бы умножить на 100, прежде чем делить.Это сдвигает вашу десятичную запятую настолько далеко вправо, насколько это вообще возможно, до деления, что означает, что ваш номер является настолько точным, насколько это когда-либо будет получено.

before_save :update_percentages
def update_percentages
  total = likes + dislikes
  self.likes_percent     =  100 * likes / total
  self.dislikes_percent  =  100 * dislikes / total
end

Примечания:

  • Я удалил неявный self, они нужны вам только при присваивании, чтобы устранить неоднозначность при создании локальной переменной, и когда у вас есть локальная переменная для устранения неоднозначности, что вы хотите вызывать метод вместо ссылки на переменную
  • По предложению egarcia, я переместил его в функцию обратного вызова, которая происходит до сохранения (я выбрал before_ save , потому что я не знаю, почему вам нужно рассчитывать этот процент при обновлении, а не при созданиии я чувствую, что это должно произойти после того, как вы проверите, что числа верны - то есть в пределах диапазона, и целые числа или десятичное число или что-то еще)
  • Поскольку это делается перед сохранением, мы удаляем вызов для сохранения вкод, который уже должен произойти
  • Поскольку мы не сохраняем явно в обратном вызове, мы не рискуемn бесконечный цикл, и, следовательно, не нужно проверять, были ли обновлены числа.Мы просто рассчитываем проценты каждый раз, когда мы сохраняем.
2 голосов
/ 04 июля 2010

Поскольку likes / dislikes является целочисленным значением, а integer / integer = integer.

, поэтому вы можете выполнить одно из двух действий: преобразовать в Float или изменить порядок операций.

self.likes_percent = (self.likes.to_f/total.to_f) * 100

Или сохранить все целые числа

self.likes_percent = (self.likes * 100)/total
1 голос
/ 04 июля 2010

Я не уверен, что это единственная проблема, которая у вас есть, но after_update вызывается после сохранения объекта.

Попробуйте изменить update_percentages раньше - вместо before_update или before_validate. Кроме того, удалите строку self.save - она ​​будет вызываться автоматически позже, если вы используете один из этих обратных вызовов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...