rails何时使用|| =设置实例变量

时间:2016-02-25 21:54:00

标签: ruby-on-rails ruby conditional instance-variables

我正在阅读一篇文章并遇到了第一个示例代码。在模型中,设置实例变量以避免不必要的查询。我也在其中一个railscast中看到了这个(第二个例子)。另一方面,我在更多的文章中读到如果我使用这种模式,那么我的应用程序可能不会是线程安全的,所以我无法利用我的puma网络服务器。

可以告诉我何时/何地应该使用这种模式?

第一个例子:

def first_order_date
  if first_order
    first_order.created_at.to_date
  else
    NullDate.new 'orders'
  end
end

private

def first_order
  @first_order ||= orders.ascend_by_created_at.first
end

第二个例子

def shipping_price
  if total_weight == 0
    0.00
  elsif total_weight <= 3
    8.00
  elsif total_weight <= 5
    10.00
  else
    12.00
  end
end

def total_weight
  @total_weight ||= line_items.to_a.sum(&:weight)
end

更新的问题

第一个例子

正如我所看到的那样,'first_order_date'总是在一个对象(https://robots.thoughtbot.com/rails-refactoring-example-introduce-null-object)上调用,所以我并不完全看到如何避免额外的查询。我确定我错了,但据我所知,它可能只是

def first_order_date
  if orders.ascend_by_created_at.first
    first_order.created_at.to_date
  else
    NullDate.new 'orders'
  end
end

或者也可以在其他地方使用@first_order

第二个例子

原始问题中的代码与此不相同?

def shipping_price
  total_weight = line_items.to_a.sum(&:weight)
  if total_weight == 0
    0.00
  elsif total_weight <= 3
    8.00
  elsif total_weight <= 5
    10.00
  else
    12.00
  end
end

我在这里看到他们通过定义total_weight所取得的成就,但为什么在我的示例中使用实例变量会更好?

2 个答案:

答案 0 :(得分:6)

短篇小说是:你的代码在Puma上应该没问题。

关于Puma环境中的线程安全性,您需要担心的是更改可能跨线程共享的内容(这通常意味着类级别的内容,而不是实例级别的内容 - 我不会&#39 ;我认为Puma将在它的线程中共享对象的实例 - 而你并没有这样做。

您引用的||=技术称为“memoization&#39;”。您应该阅读https://bearmetal.eu/theden/how-do-i-know-whether-my-rails-app-is-thread-safe-or-not/上的完整文章,特别是有关记忆的部分。

回答你评论中的问题:

  1. 为什么还不足以在shipping_price方法的第一行定义total_weight = line_items.to_a.sum(&:weight)?我认为它只会运行一次查询
  2. 好的,所以如果shipping_price方法只针对该类的每个实例调用一次,那么你就是对了 - 不需要进行记忆。但是,如果多次调用每个实例,那么每次都必须执行line_items.to_a.sum(&:weight)来计算总数。

    所以,让我们说你在同一个实例上连续3次调用shipping_price。然后没有记忆,它将必须执行line_items.to_a.sum(&:weight) 3次。但是通过记忆,它只需要执行line_items.to_a.sum(&:weight)一次,而接下来的两次只需要检索@total_weight实例变量的值

    1. 你在哪里使用&#39; memoization&#39;在你的rails应用程序?
    2. 嗯......我不确定如果不写一个很长的答案并解释很多背景等,我就能给出一个好的答案。但短篇小说是:每当有一个方法,适合以下所有

        每个实例可能会多次调用
      • 做一些耗时的事情(例如,查询数据库或其他东西)
      • 该方法的结果可以安全地缓存,因为它不可能在调用方法的不同时间之间发生变化(基于每个实例)

      一个很好的类比可能是:如果有人问你时间,你检查你的手表(即耗时的动作)。如果他们再次问你时间,1秒钟之后,你就不需要再次检查你的手表了 - 你基本上说'#34;我刚刚检查过,它已经是9:00 am&#34;。 (这有点像你,并且记住时间 - 节省你必须检查你的手表,因为结果自你上次询问以来没有改变。)

答案 1 :(得分:2)

在这种情况下,它用于避免在答案相同时重复执行代码。

想象一下这个版本:

def shipping_price
  if line_items.to_a.sum(&:weight) == 0
    0.00
  elsif line_items.to_a.sum(&:weight) <= 3
    8.00
  elsif line_items.to_a.sum(&:weight) <= 5
    10.00
  else
    12.00
  end
end

对于一件简单的事情来说,这是一件很重的事情,不是吗? ||=模式用于缓存结果。