假设我有一个叫Thing的Rails模型。Thing有一个url属性,可以选择将其设置为Internet上某个地方的url。在视图代码中,我需要这样做的逻辑:

<% if thing.url.blank? %>
<%= link_to('Text', thing_path(thing)) %>
<% else %>
<%= link_to('Text', thing.url) %>
<% end %>

视图中的条件逻辑是丑陋的。当然,我可以构建一个helper函数,它会将视图更改为:

<%= thing_link('Text', thing) %>

这解决了冗长的问题,但我更喜欢在模型本身中拥有功能。在这种情况下,视图代码将是:

<%= link_to('Text', thing.link) %>

这显然需要模型上的链接方法。以下是它需要包含的内容:

def link
  (self.url.blank?) ? thing_path(self) : self.url
end

就问题而言,thing_path()在Model代码中是一个未定义的方法。我假设可以将一些辅助方法“拉入”到模型中,但是怎么做呢?路由只在应用程序的控制器和视图层运行,这有什么真正的原因吗?我能想到很多模型代码可能需要处理url的情况(与外部系统集成等)。


当前回答

任何与视图中显示的内容相关的逻辑都应该委托给helper方法,因为模型中的方法严格用于处理数据。

以下是你可以做的:

# In the helper...

def link_to_thing(text, thing)
  (thing.url?) ? link_to(text, thing_path(thing)) : link_to(text, thing.url)
end

# In the view...

<%= link_to_thing("text", @thing) %>

其他回答

任何与视图中显示的内容相关的逻辑都应该委托给helper方法,因为模型中的方法严格用于处理数据。

以下是你可以做的:

# In the helper...

def link_to_thing(text, thing)
  (thing.url?) ? link_to(text, thing_path(thing)) : link_to(text, thing.url)
end

# In the view...

<%= link_to_thing("text", @thing) %>

我真的很喜欢清洁解决方案。

class Router
  include Rails.application.routes.url_helpers

  def self.default_url_options
    ActionMailer::Base.default_url_options
  end
end

router = Router.new
router.posts_url  # http://localhost:3000/posts
router.posts_path # /posts

来自http://hawkins.io/2012/03/generating_urls_whenever_and_wherever_you_want/

关于如何自己做这件事,我已经找到了答案。在模型代码中,只需放入:

对于Rails <= 2:

include ActionController::UrlWriter

对于Rails 3:

include Rails.application.routes.url_helpers

这神奇地使thing_path(self)返回当前事物的URL,或other_model_path(self.association_to_other_model)返回其他URL。

你可能还会发现以下方法比包含所有方法更简洁:

class Thing
  delegate :url_helpers, to: 'Rails.application.routes' 

  def url
    url_helpers.thing_path(self)
  end
end

虽然可能有一种方法,但我倾向于将这种逻辑排除在模型之外。我同意你不应该把它放在视图中(保持简洁),但除非模型将url作为数据块返回给控制器,路由的东西应该在控制器中。