after_update回调问题

Mat*_*w H 0 ruby activerecord ruby-on-rails

我试图在我的模型的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
Run Code Online (Sandbox Code Playgroud)

这不起作用.百分比总是以100或0出现,这完全破坏了一切.

我在哪里滑倒?我保证self.likes和self.dislikes正在递增.

Jos*_*eek 5

问题

当您将整数除以整数(也称为整数除法)时,大多数编程语言(包括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]
Run Code Online (Sandbox Code Playgroud)

在Rails的模型中,浮点数用Ruby Float类表示,而decimal用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
Run Code Online (Sandbox Code Playgroud)

为您解决方案

在你的情况下,假设你想要整数结果(即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
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我删除了隐式self你只需要它们来分配以消除创建局部变量的歧义,并且当你有一个局部变量来消除歧义时你想要调用方法而不是引用变量
  • 正如egarcia所建议的那样,我把它移动到了一个在保存之前发生的回调(我选择了之前的保存,因为我不知道为什么你需要在更新时计算这个百分比而不是创建,我觉得它应该发生验证数字是否正确后 - 即在范围内,整数或小数或其他)
  • 因为它是在保存之前完成的,所以我们删除了在代码中保存的调用,这已经发生了
  • 因为我们没有明确地保存回调,所以我们不会冒无限循环的风险,因此不需要检查数字是否已更新.我们只是在每次储蓄时计算百分比.