after_update回调问题

时间:2010-07-04 11:11:19

标签: ruby-on-rails ruby activerecord

我正在尝试重新计算模型的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 个答案:

答案 0 :(得分: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]

在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

适合您的解决方案

在你的情况下,假设你想要整数结果(即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的建议,我将其移至保存之前发生的回调(我选择了之前_ 保存,因为我不知道为什么你需要在更新时计算这个百分比而不是一个创建,我觉得它应该在你验证数字是正确的之后发生 - 即在范围内,整数或小数或其他什么)
  • 因为它是在保存之前完成的,所以我们删除了在代码中保存的调用,这已经发生了
  • 因为我们没有明确地保存回调,所以我们不会冒无限循环的风险,因此不需要检查数字是否已更新。我们只是在每次储蓄时计算百分比。

答案 1 :(得分:2)

因为喜欢/不喜欢是整数值而整数/整数=整数。

所以你可以做两件事之一,转换为Float或改变你的操作顺序。

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

或者,保持一切整数

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

答案 2 :(得分:1)

我不确定这是你遇到的唯一问题,但在保存对象后会调用after_update

尝试更改之前的update_percentages - 代替before_updatebefore_validate。另外,删除self.save行 - 如果您使用其中一个回调,它将在稍后自动调用。