Rails 强制 to_param 到 return 某些东西,即使不坚持
Rails force to_param to return something even when not persisted
我需要处理使用 URL 从非持久数据构造的电子邮件视图生成的特殊情况。
示例:假设我的用户可以创建 posts,这会触发 post 创建通知电子邮件,我想向用户发送一个假 post 创建的示例.为此,我使用 FactoryGirl.build(:post)
并将其传递给我的 PostMailer.notify_of_creation(@post)
在日常Rails生活中,我们通过传递模型本身作为参数来使用路线url_helpers
,路线生成器会自动将模型转换为其ID以用于路线URL 生成(在 article_path(@article)
中,路由助手将 @article
转换为 @article.id
以构建 /articles/:id
URL。
我相信它在 ActiveRecord 中是一样的,但无论如何在 Mongoid 中,如果模型没有持久化,这个转换就会失败(这有点好,因为它可以防止生成可能不对应的 URLs到实际数据)
所以在我的具体情况下,URL 生成崩溃,因为模型没有持久化:
<%= post_url(@post_not_persisted) %>
崩溃
ActionView::Template::Error: No route matches {:action=>"show", :controller=>"posts", :post_id=>#<Post _id: 59b3ea2aaba9cf202d4eecb6 ...
有没有办法只在非常特定的范围内绕过这个限制?否则我可以用 resource_path(@model.id.to_s)
或更好的 @model.class.name
替换我所有的 resource_path(@model)
但这感觉不是正确的情况...
编辑:
主要问题是
Foo.new.to_param # => nil
# whereas
Foo.new.id.to_s # => "59b528e8aba9cf74ce5d06c0"
我需要强制 to_param
到 return ID(或其他东西),即使模型没有持久化。现在我正在研究改进,看看我是否可以使用范围限定的 monkeypatch,但如果你有更好的想法,请成为我的客人:-)
module ForceToParamToUseIdRefinement
refine Foo do
def to_param
self.class.name + 'ID'
end
end
end
然而,在使用我的优化时,我似乎遇到了一个小范围问题,因为这并没有像预期的那样冒泡到 url_helpers。虽然在控制台中使用 te refinement 时它工作正常 (Foo.new.to_param # => 59b528e8aba9cf74ce5d06c0
)
我找到了一种使用动态方法覆盖的方法。我真的不喜欢它,但它完成了工作。我基本上是在修补我在测试期间使用的实例。
为了方便起见,我创建了一个 class 方法 example_model_accessor
,除了 setter 修补了 #to_param 之外,它的行为基本上类似于 attr_accessor
对象的方法
def example_model_accessor(model_name)
attr_reader model_name
define_method(:"#{model_name}=") do |instance|
def instance.to_param
self.class.name + 'ID'
end
instance_variable_set(:"@#{model_name}", instance)
end
end
然后在我的代码中我可以使用
class Testing
example_model_accessor :message
def generate_view_with_unpersisted_data
self.message = FactoryGirl.build(:message)
MessageMailer.created(message).deliver_now
end
end
# views/message_mailer/created.html.erb
...
<%= message_path(@message) %> <!-- Will work now and output "/messages/MessageID" ! -->
我需要处理使用 URL 从非持久数据构造的电子邮件视图生成的特殊情况。
示例:假设我的用户可以创建 posts,这会触发 post 创建通知电子邮件,我想向用户发送一个假 post 创建的示例.为此,我使用 FactoryGirl.build(:post)
并将其传递给我的 PostMailer.notify_of_creation(@post)
在日常Rails生活中,我们通过传递模型本身作为参数来使用路线url_helpers
,路线生成器会自动将模型转换为其ID以用于路线URL 生成(在 article_path(@article)
中,路由助手将 @article
转换为 @article.id
以构建 /articles/:id
URL。
我相信它在 ActiveRecord 中是一样的,但无论如何在 Mongoid 中,如果模型没有持久化,这个转换就会失败(这有点好,因为它可以防止生成可能不对应的 URLs到实际数据)
所以在我的具体情况下,URL 生成崩溃,因为模型没有持久化:
<%= post_url(@post_not_persisted) %>
崩溃
ActionView::Template::Error: No route matches {:action=>"show", :controller=>"posts", :post_id=>#<Post _id: 59b3ea2aaba9cf202d4eecb6 ...
有没有办法只在非常特定的范围内绕过这个限制?否则我可以用 resource_path(@model.id.to_s)
或更好的 @model.class.name
替换我所有的 resource_path(@model)
但这感觉不是正确的情况...
编辑:
主要问题是
Foo.new.to_param # => nil
# whereas
Foo.new.id.to_s # => "59b528e8aba9cf74ce5d06c0"
我需要强制 to_param
到 return ID(或其他东西),即使模型没有持久化。现在我正在研究改进,看看我是否可以使用范围限定的 monkeypatch,但如果你有更好的想法,请成为我的客人:-)
module ForceToParamToUseIdRefinement
refine Foo do
def to_param
self.class.name + 'ID'
end
end
end
然而,在使用我的优化时,我似乎遇到了一个小范围问题,因为这并没有像预期的那样冒泡到 url_helpers。虽然在控制台中使用 te refinement 时它工作正常 (Foo.new.to_param # => 59b528e8aba9cf74ce5d06c0
)
我找到了一种使用动态方法覆盖的方法。我真的不喜欢它,但它完成了工作。我基本上是在修补我在测试期间使用的实例。
为了方便起见,我创建了一个 class 方法 example_model_accessor
,除了 setter 修补了 #to_param 之外,它的行为基本上类似于 attr_accessor
对象的方法
def example_model_accessor(model_name)
attr_reader model_name
define_method(:"#{model_name}=") do |instance|
def instance.to_param
self.class.name + 'ID'
end
instance_variable_set(:"@#{model_name}", instance)
end
end
然后在我的代码中我可以使用
class Testing
example_model_accessor :message
def generate_view_with_unpersisted_data
self.message = FactoryGirl.build(:message)
MessageMailer.created(message).deliver_now
end
end
# views/message_mailer/created.html.erb
...
<%= message_path(@message) %> <!-- Will work now and output "/messages/MessageID" ! -->