Проблема
Когда вы делите целое число на целое число (или целочисленное деление ), большинство языков программирования, включая 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 бесконечный цикл, и, следовательно, не нужно проверять, были ли обновлены числа.Мы просто рассчитываем проценты каждый раз, когда мы сохраняем.