我有一个有人在几年前写过的ruby / rails / hobo系统,我需要移植到最新版本的ruby / rails / hobo。似乎ruby并不关心向后兼容性,因此以前在旧应用程序中工作的代码不再起作用了:
在observation.rb模型文件中,旧应用程序具有:
belongs_to :survey
has_one :site, :through => :survey
def create_permitted?
acting_user == self.survey.contact or acting_user.administrator?
end
survey.rb模型文件有:
belongs_to :contact, :class_name => 'User', :creator => true
不幸的是,observe.rb中的代码在新的ruby / rails / hobo下无效,它给了我错误:
NoMethodError in Observations#index
Showing controller: observations; dryml-tag: index-page where line #1 raised:
undefined method `contact' for nil:NilClass
Extracted source (around line #1):
0
Rails.root: /home/simon/ruby/frogwatch2
Application Trace | Framework Trace | Full Trace
app/models/observation.rb:48:in `create_permitted?'
如何更改“create_permitted”方法?我发现ruby / rails / hobo的文档非常糟糕(这很公平,因为它是免费软件)。此外,我甚至不知道如何开始在谷歌搜索这个(我已经尝试了几天)。
请帮忙! :)
答案 0 :(得分:2)
除了对Rails文档的不同观点之外,您在调查中调用contact
,在这种情况下不存在,导致调用nil.contact
。
另一种方法是在调用contact
之前检查是否存在调查,例如以这种方式。
def create_permitted?
acting_user == (survey && survey.contact) or acting_user.administrator?
end
答案 1 :(得分:1)
您收到错误,因为参考列(survey_id
)可能包含null
或无效的参考ID。
如果允许null
或无效引用,则更改代码以处理它
( self.survey and acting_user == self.survey.contact ) or acting_user.administrator?
答案 2 :(得分:1)
我要回应其他两位所说的话。您尝试引用的调查是nil
,而nil
没有名为contact
的方法。我将提供一个稍微不同的解决方案:
def create_permitted?
acting_user == survey.try(:contact) or acting_user.administrator?
end
#try
方法存在于nil
和survey
上。它基本上将方法调用包装在rescue
中。从概念上讲,它看起来像:
def try(method_name, *args)
self.send(method_name, args) rescue nil
end
这可能会减少您必须编写的代码量以捕获可能不存在关系的条件,从而阻止NoMethodError
异常。
#try
是Object
的Rails核心扩展的一部分。实际上,它不像我上面那样工作,因为调用Object#try
引起的异常仍然会像通常那样发生。相反,它通过调用Object
来扩展send
。它通过返回NilClass
来扩展nil
,因此它不会尝试将任何方法发送到NilClass
,从而阻止NoMethodError
。正如tadman在评论中指出的那样,一个包罗万象的异常处理程序通常不是一个好主意。
<强>更新强>
更好的解决方案,也就是我忘记的解决方案,是使用delegate
。
例如:
class User < ActiveRecord::Base
delegate :contact, to: :survey, prefix: true, allow_nil: true
end
然后你会打电话给user.survey_contact
,如果调查是nil
,则会优雅地失败。