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)}" %>