假设我有一个叫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) %>

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


(编辑:忘了我之前的废话…)

可能在某些情况下你会去到模型或者其他url。但我真的不认为这属于模型,视图(或者模型)听起来更合适。

About the routes, as far as I know the routes is for the actions in controllers (wich usually "magically" uses a view), not directly to views. The controller should handle all requests, the view should present the results and the model should handle the data and serve it to the view or controller. I've heard a lot of people here talking about routes to models (to the point I'm allmost starting to beleave it), but as I understand it: routes goes to controllers. Of course a lot of controllers are controllers for one model and is often called <modelname>sController (e.g. "UsersController" is the controller of the model "User").

如果您发现自己在一个视图中编写了大量令人讨厌的逻辑,请尝试将逻辑移到更合适的地方;请求和内部通信逻辑可能属于控制器,与数据相关的逻辑可能放在模型中(但不包括显示逻辑,包括链接标签等),而纯粹与显示相关的逻辑将放在帮助器中。


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

对于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。


在Rails 3及更高版本中:

Rails.application.routes.url_helpers

e.g.

Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")

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

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

  def url
    url_helpers.thing_path(self)
  end
end

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

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/