我有一个标准的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 #<TipObserver:0xb75ca17c> (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 #<TipObserver:0xb75f7708> (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)
嗯,没有明显的办法解决这个问题。但在阅读了一些clever-clogs suggestion that Sweepers are the same as Observers之后却可以访问网址助手。
因此,我们将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问题。 / p>
所以我们改变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
很好,无论我们做什么,我们都会遇到错误。我尝试了很多方法在default_url_options
内分配template_instance
但没有成功。
到目前为止,这并不觉得非常“Railsy”,事实上它感觉非常困难。
所以我的问题是:
任何提示建议或建议都会非常感激地收到,我已经在这个砖墙上撞了好几天了,然后慢慢失去了生活的意愿。
TIA
基思
答案 0 :(得分:2)
你是对的,你的方法不是很好Rails-y。你试图以一种他们不设计的方式混合模型,控制器和视图方法,而且总是有点摇摇欲坠。
如果我开始走下去,我可能会放弃link_to
问题并且(我承认它不是“Rails方式”)手动编码链接的HTML。因此link_to_tip = link_to tip.name, tip_path(tip)
成为link_to_tip = '<a href="#{tip_path(tip)}">#{tip.name}</a>
- 如果您正在寻找一个快速而肮脏的解决方案; - )
但根据我的经验,Rails非常整洁,直到你想以非标准的方式做事。然后它可以咬你: - )
问题是你在Message模型中编写和存储文本,而不应该存在。消息模型应belong_to Tips
,视图应负责显示消息文本,包括提示链接。如果消息可以是除提示之外的其他内容,则可以在消息模型中建立多态关联,如下所示:
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)}" %>