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

发布于 2024-08-29 08:01:48 字数 1111 浏览 6 评论 0原文

大家好。

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

我应该提到该脚本旨在使用 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 上。

感谢您的帮助

Good day all.

I'm running into a bit of a problem getting a script running on my production environment, even though it works just fine on my dev box. I've verified that all the requisite gems and such are the same version.

I should mention that the script is intended to be run with the script/runner command.

Here is a super-condensed version of what I'm trying to do, centered around the part that's broken:


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

Now convertToTimeZone is a method attached to the model. So, this code works just fine on my dev machine, as stated. However, attempting to run it on my production machine results in:


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

If, however, I go into the console on the production box and do this:


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

It returns the correct value, no problem. So what is going on?

This is on rails v 2.3.5, on both machines.

Thanks for any help

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

中性美 2024-09-05 08:01:48

您正在循环遍历生产代码上的所有 Market,但您的测试代码段仅寻找一个。问题是数据库中的一个 MarketcurrentDealnil(它没有与之关联的对象)。

请在您的生产控制台上运行此命令。

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,要么有时没有,这也没关系。如果市场应该始终有 currentDeal,那么您需要调整验证,现在允许在没有 currentDeal 的情况下保存市场。但考虑到 currentDeal 是基于时间的事情,我会认为有时没有安排交易,因此 currentDeal 将返回 nil。

因此,更有可能的是,您需要允许当前交易为nil。您的测试代码不会执行此操作。它向市场询问交易,然后向市场询问其主题的交易。如果市场返回一个 nil 交易,那么您立即询问 nil 它的主题,并且您会得到异常,因为 nil 没有名为的方法主题。 nil 保护代码的一些简单方法:

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

最后,一个 ruby​​ 技巧。这种方法比需要的更复杂。

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

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

You are looping though all Markets on your production code, but your test snippet is only looking for one. The problem is that one of your Markets in your database has a currentDeal of nil (it has no object associated with it).

Run this on your production console instead.

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

This will tell you exactly which Market record is exploding without a currentDeal.


So the question is how to fix it? Either all Markets are expected to have a currentDeal, or sometimes they don't and that's ok. If Market's should always have a currentDeal, then you need to adjust your validations to now allow a Market to be saved without a currentDeal. But given that the currentDeal is a time based thing, I would be that there is times when no deal is scheduled and therefore currentDeal will return nil.

So, more likely, you need to allow for the current deal to be nil. Your test code doesn't do this. It asks the market for the deal, and then the deal for it's subject. If the market return a nil deal, then you immediately ask nil for it's subject and you get the exception because nil does not have a method named subject. A few simple ways to nil protect you code:

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

Lastly, a ruby tip. This method is more complicated than it needs to be.

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 will always return the last expression's result in a method, and a has based conditions finder will clean up that query quite a bit.

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

But this looks more like an association anyway. So you may want to use the association methods to clean this up further.

深海不蓝 2024-09-05 08:01:48

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

Clearly you are calling nil.subject, so Deal.find is returning nil in the production code. Your test case is only looking at one specific Market object, but the general case loops through Market objects. Your code needs to handle not finding a currentDeal for a Market object

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文