当方法存在时,Rails NoMethodError在循环中

时间:2010-04-16 16:07:29

标签: ruby-on-rails ruby methods

美好的一天。

我在生产环境中运行脚本遇到了一些问题,即使它在我的开发箱上工作得很好。我已经确认所有必需的宝石都是相同的版本。

我应该提一下脚本是用script / runner命令运行的。

这是我正在尝试做的超级浓缩版本,围绕着被打破的部分:


def currentDeal
 marketTime = self.convertToTimeZone(Time.new)
 deal = Deal.find(:first, :conditions => ["start_time  ? AND market_id = ? AND published = ?", marketTime, marketTime, self.id, 1])
 return deal
end

markets = Market.find(all)
markets.each do |market|
  deal = market.currentDeal
  puts deal.subject
end

现在convertToTimeZone是一个附加到模型的方法。所以,正如所述,这个代码在我的开发机器上运行得很好。但是,尝试在我的生产计算机上运行它会导致:


undefined method `subject' for nil:NilClass (NoMethodError)

但是,如果我进入生产箱的控制台并执行此操作:


def currentDeal
  marketTime = self.convertToTimeZone(Time.new)
  deal = Deal.find(:first, :conditions => ["start_time  ? AND market_id = ? AND published = ?", marketTime, marketTime, self.id, 1])
  return deal
end

market = Market.find(1)
deal = market.currentDeal
puts deal.subject

它返回正确的值,没问题。那是怎么回事?

这是在两台机器上的rails v 2.3.5上。

感谢您的帮助

2 个答案:

答案 0 :(得分:3)

您正在循环使用生产代码中的所有Market,但您的测试代码段仅查找一个。问题是您数据库中的Market之一currentDeal nil(没有与之关联的对象)。

改为在生产控制台上运行。

markets = Market.find(all)
markets.each do |market|
  deal = market.currentDeal
  if deal
    puts deal.subject
  else
    puts "NO currentDeal for Market with id: #{market.id}"
  end
end

这将在没有Market的情况下确切地告诉您哪个currentDeal记录正在爆炸。


所以问题是如何修复它?要么所有Market都要currentDeal,要么有时候没有,这没关系。如果Market应该总是有一个currentDeal,那么你需要调整你的验证,现在允许在没有currentDeal的情况下保存Market。但鉴于currentDeal是基于时间的事情,我认为有时没有安排任何交易,因此currentDeal将返回nil。

因此,更有可能的是,您需要允许当前交易为nil。您的测试代码不会这样做。它询问市场的交易,然后是交易的主题。如果市场返回nil交易,那么您会立即询问nil该主题,并且您获得了例外,因为nil没有名为subject的方法。一些简单的方法来保护代码:

deal = market.currentDeal

# simple if
if deal
  puts deal.subject
end

# rails try method returns nil if the receiver is nil
# or executes the method if the object supports it
puts deal.try(:subject)

# ternary
puts deal ? deal.subject : "NO DEAL!"

# conditional execution
puts deal && deal.subject

最后,红宝石小费。这种方法比它需要的更复杂。

def currentDeal
  marketTime = self.convertToTimeZone(Time.new)
  deal = Deal.find(:first, :conditions => ["start_time  ? AND market_id = ? AND published = ?", marketTime, marketTime, self.id, 1])
  return deal
end

Ruby将始终在方法中返回最后一个表达式的结果,并且基于条件的查找程序将清理该查询。

def currentDeal
  marketTime = self.convertToTimeZone(Time.new)
  Deal.find(:first, :conditions => ["start_time > ? AND market_id = ? AND published = ?", marketTime, marketTime, id, true])
end

但无论如何,这看起来更像一个协会。因此,您可能希望使用关联方法进一步清理它。

答案 1 :(得分:0)

显然,您正在调用nil.subject,因此Deal.find在生产代码中返回nil。您的测试用例仅查看一个特定的Market对象,但是一般情况通过Market对象循环。您的代码需要处理没有为Market对象找到currentDeal