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

Sea*_*yar 0 ruby conditional ruby-on-rails 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
Run Code Online (Sandbox Code Playgroud)

第二个例子

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
Run Code Online (Sandbox Code Playgroud)

更新的问题

第一个例子

正如我所看到的那样,'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
Run Code Online (Sandbox Code Playgroud)

或者也可以使用@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
Run Code Online (Sandbox Code Playgroud)

我在这里看到他们通过定义实现了什么total_weight,但为什么在我的示例中使用实例变量更好?

jos*_*ing 6

简短的故事是:你的代码在Puma上应该没问题.

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

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

要回答您的评论中的问题:

  1. 为什么total_weight = line_items.to_a.sum(&:weight)在shipping_price方法的第一行中定义不够?我认为它只会运行一次查询

好的,所以如果shipping_price方法只在每个类的实例中被调用一次,那么你是对的 - 不需要进行memoization.但是,如果每个实例多次调用它,那么每次都必须执行它line_items.to_a.sum(&:weight)来计算总数.

因此,假设您shipping_price在同一个实例上出于某种原因连续三次调用.然后没有记忆,它将不得不执行line_items.to_a.sum(&:weight)3次.但是通过memoization,它只需要执行line_items.to_a.sum(&:weight)一次,而接下来的两次它只需要检索@total_weight实例变量的值

  1. 你在哪里使用rails memits'memoization'?

嗯......我不确定如果不写一个很长的答案并解释很多背景等,我可以给出一个很好的答案.但短篇小说是:只要有一种方法适合以下所有方法:

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

一个很好的类比可能是:如果有人问你时间,你检查你的手表(即耗时的动作).如果他们再次问你时间,1秒钟之后,你不需要再次检查你的手表了 - 你基本上说"我刚刚检查过,现在是早上9点".(这有点像你在记忆时间 - 节省你必须检查你的手表,因为自从你上次询问后结果不会改变).