RoR:nil的未定义方法`url_for’:NilClass

我有一个标准的Rails应用程序。

创建提示时,我想为每个对该提示感兴趣的用户创建一条消息。

这听起来很简单吧? 它应该是…

所以,我们从Tip Observer开始:

class TipObserver < ActiveRecord::Observer def after_save(tip) # after the tip is saved, we'll create some messages to inform the users users = User.interested_in(tip) # get the relevant users users.each do |u| m = Message.new m.recipient = u link_to_tip = tip_path(tip) m.body = "Hello #{u.name}, a new tip: #{link_to_tip}" m.save! end end end 

错误:

 tip_observer.rb:13:in `after_save': undefined method `tip_path' for # (NoMethodError) 

好的,所以TipObserver需要访问UrlWriter方法。 这应该是相当简单的修复,对吧?

 class TipObserver < ActiveRecord::Observer include ActionController::UrlWriter 

现在它运行(!)和输出:

 Hello dave18, a new tip: /tips/511 

伟大的工作!! 嗯,有点,我们真的希望它是一个可点击的链接。 再说一次,这应该很容易吗?

 link_to_tip = link_to tip.name, tip_path(tip) 

错误:

 tip_observer.rb:13:in `after_save': undefined method `link_to' for # (NoMethodError) 

好的,所以这次TipObserver需要访问UrlHelper方法。 这应该是相当简单的修复,对吧?

 class TipObserver < ActiveRecord::Observer include ActionController::UrlWriter include ActionView::Helpers::UrlHelper 

错误:

 whiny_nil.rb:52:in `method_missing': undefined method `url_for' for nil:NilClass (NoMethodError) 

好吧,似乎添加了干扰了url_for声明。 让我们以不同的顺序尝试包含:

 class TipObserver < ActiveRecord::Observer include ActionView::Helpers::UrlHelper include ActionController::UrlWriter 

错误:

 url_rewriter.rb:127:in `merge': can't convert String into Hash (TypeError) 

嗯,这没有明显的办法。 但在阅读了一些巧妙的木cl之后,他们建议扫墓人员与观察员一样,但可以访问url助手。 因此,我们将Observer转换为Sweeper并删除UrlHelper和UrlWriter。

 class TipObserver < ActionController::Caching::Sweeper observe Tip #include ActionView::Helpers::UrlHelper #include ActionController::UrlWriter 

那么,它允许它运行,但这是输出:

 Hello torey39, a new tip: 

所以,没有错误,但是没有生成url。 使用控制台进一步调查显示:

 tip_path => nil 

因此:

 tip_path(tip) => nil 

好吧我不知道如何解决这个问题,所以也许我们可以从不同的方向攻击这个问题。 如果我们将内容移动到erb模板中,并将Message.body呈现为视图 – 这有两个好处 – 首先将“View”内容放在正确的位置,这可能有助于我们避免这些* _path问题。

所以我们改变after_save方法:

 def after_save(tip) ... template_instance = ActionView::Base.new(Rails::Configuration.new.view_path) m.body = template_instance.render(:partial => "messages/tip", :locals => { :user=>user, :tip=>tip }) m.save! end 

错误:

 undefined method `url_for' for nil:NilClass (ActionView::TemplateError) 

很好,但现在我们又回到了这个血腥的url_for。 所以这一次它的ActionView就是抱怨。 让我们尝试解决这个问题:

 def after_save(tip) ... template_instance = ActionView::Base.new(Rails::Configuration.new.view_path) template_instance.extend ActionController::UrlWriter 

错误:

 undefined method `default_url_options' for ActionView::Base:Class 

太好了,无论我们做什么,我们都会遇到错误。 我已经尝试了很多方法在template_instance中分配default_url_options但没有成功。

到目前为止,这并不觉得非常“Railsy”,事实上它感觉非常困难。

所以我的问题是:

  • 我想在圆孔中找到一个方形钉吗? 如果是这样,我应该如何调整架构以提供此function? 我无法相信它不是其他网站中存在的东西。
  • 我应该放弃尝试使用Observer或Sweeper吗?
  • 我是否应该尝试通过MessagesController创建新消息,如果是这样,我如何直接从Observer / Sweeper中多次调用MessagesController?

任何提示建议或建议将非常感激地收到,我已经在这个砖墙上撞了几天,现在慢慢失去了生活的意志。

TIA

基思

你是对的,你的方法不是很Rails-y。 你试图以一种他们不设计的方式混合模型,控制器和视图方法,而且总是有点不稳定。

如果我开始走下去,我可能会放弃link_to问题并且(我承认它不是“Rails方式”)手动编码链接的HTML。 所以link_to_tip = link_to tip.name, tip_path(tip)变为link_to_tip = '#{tip.name} – 如果你是一个快速而肮脏的解决方案寻找一个;-)

但根据我的经验,Rails非常简洁,直到你想以非标准的方式做事。 然后它可以咬你:-)

问题是你在Message模型中编写和存储文本,而不应该存在。 消息模型应该belong_to Tips ,视图应该负责显示消息文本,包括提示的链接。 如果消息可以是除提示之外的其他内容,则可以在Message模型中进行多态关联,如下所示:

 belongs_to :source, :polymorphic => true 

Tip模型包括:

 has_many :messages, :as => :source 

然后你这样做(使用你的代码作为例子):

 m = Message.new m.source = tip m.save! 

然后,呈现消息的视图负责创建链接,如下所示:

 <%= "Hello #{u.name}, a new tip: #{link_to m.source.name, tip_path(m.source)}" %>