4

在 Rails 应用程序中,我有以下形式的模型代码:

def do_stuff(resource)
  models = Model.where(resource: resource)
  operated_at = DateTime.now

  models.each { |model| some_operation(model, operated_at) }

  some_other_operation models, operated_at
end

def some_operation(model, operated_at)
  model.date_time_field = operated_at
  model.save
end

def some_other_operation(models, operated_at)
  models.each do |model|
    if model.date_time_field < operated_at
      # do something
    end    
  end
end

在这种情况下,将始终执行 some_other_operation 的“做某事”块。更奇怪的是,如果我在控制台中设置它而没有所有定义的功能,则比较会按预期工作。例子:

> model = Model.first
> time_var = DateTime.now
> model.last_imported_at = time_var
=> Fri, 25 Oct 2013 21:14:06 +0000
> model.save
=> true
> model.last_imported_at < time_var
=> false
> model.last_imported_at == time_var
=> true

最后,如果,some_other_operation我改为通过以下方式进行比较:

if model.date_time_field.to_i < operated_at.to_i
  # do something
end

仅在预期时才到达“做某事”块。我怀疑这是因为该to_i方法将丢弃在 DateTime 对象上定义的几分之一秒,并且该operated_at变量实际上被重新定义DateTime.now为每个方法的范围。如果是这种情况,那么我想我的问题是如何强制operated_at不为每个范围重新定义?

4

4 回答 4

3

可能的线索;保存和重新加载的行为会截断seconds_fractionDateTime 的一部分。日期字段成为 的一个实例ActiveSupport::TimeWithZone。仅保存而不重新加载不会这样做;真正的 DateTime 对象仍然存在。

2.0.0-p195 :001 > dt            =  DateTest.create
2.0.0-p195 :002 > right_now     =  DateTime.now
2.0.0-p195 :004 > dt.created_at =  right_now
2.0.0-p195 :005 > dt.created_at == right_now
 => true 

2.0.0-p195 :006 > dt.save
2.0.0-p195 :007 > dt.created_at == right_now
 => true 

2.0.0-p195 :008 > dt = DateTest.find(dt.id)
2.0.0-p195 :009 > dt.created_at == right_now
 => false 

编辑:当然调用models.each会在那里加载模型,然后因为延迟加载行为。道具给另一个回答者。作为一个实验,尝试设置modelsModel.where(resource: resource).to_a.

于 2013-10-25T22:10:12.313 回答
1

您在上面定义了三个单独的方法,其中包含三个单独的operated_at 局部变量。局部变量仅限于定义它们的方法的范围。

您需要定义实例变量,这些变量在整个类中都存在。例如,您可以:

def Model
  attr_accessor :operated_at

  def do_stuff(resource)
    models = Model.where(resource: resource)
    operated_at = DateTime.now

    models.each { |model| some_operation(model, operated_at) }

    some_other_operation models, operated_at
  end

  def some_operation(model, operated_at)
    model.date_time_field = operated_at
    model.save
  end

  def some_other_operation(models, operated_at)
    models.each do |model|
      if model.date_time_field < operated_at
        # do something
      end    
    end
  end
end  

这将使您能够在所有类方法中访问操作的_at。

于 2013-10-25T22:11:53.187 回答
0

我认为这个问题是由于您使用的惰性作用域:models = Model.where(resource: resource)

models 是一个代理集合,将在某个时候由 rails 解决,并且可能会在您不确切知道的情况下重新评估。

因此,当您更改属性并且reload在检查属性之前没有对象时,它可能不是最新的。

于 2013-10-25T21:43:48.513 回答
0

DateTime.now 不适用于与数据库日期时间对象进行比较。

例子:

  1. 如果您的服务器在太平洋时区运行,您将期望获得

    DateTime.now --> 2013 年 10 月 25 日星期五 15:28:21 -0800

(10 月的夏令时应该是 UTC 的 -7 小时,但不知何故 datetime.now 总是给你 -8 来自 UTC)

  1. 我想如果你使用 Time.zone.now 而不是 DateTime.now 会给你正确的比较。

    Time.zone.now --> 2013 年 10 月 25 日星期五 15:27:17 PDT -07:00

于 2013-10-25T22:29:46.607 回答