Can Rails Routing Helpers (i.e. mymodel_path(model)) be Used in Models?

Ruby on-RailsRails RoutingHelpermethods

Ruby on-Rails Problem Overview


Say I have a Rails Model called Thing. Thing has a url attribute that can optionally be set to a URL somewhere on the Internet. In view code, I need logic that does the following:

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

This conditional logic in the view is ugly. Of course, I could build a helper function, which would change the view to this:

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

That solves the verbosity problem, but I would really prefer having the functionality in the model itself. In which case, the view code would be:

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

This, obviously, would require a link method on the model. Here's what it would need to contain:

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

To the point of the question, thing_path() is an undefined method inside Model code. I'm assuming it's possible to "pull in" some helper methods into the model, but how? And is there a real reason that routing only operates at the controller and view layers of the app? I can think of lots of cases where model code may need to deal with URLs (integrating with external systems, etc).

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

In Rails 3 and higher:

Rails.application.routes.url_helpers

e.g.

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

Solution 2 - Ruby on-Rails

I've found the answer regarding how to do this myself. Inside the model code, just put:

For Rails <= 2:

include ActionController::UrlWriter

For Rails 3:

include Rails.application.routes.url_helpers

This magically makes thing_path(self) return the URL for the current thing, or other_model_path(self.association_to_other_model) return some other URL.

Solution 3 - Ruby on-Rails

You may also find the following approach cleaner than including every method:

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

  def url
    url_helpers.thing_path(self)
  end
end

Solution 4 - Ruby on-Rails

Any logic having to do with what is displayed in the view should be delegated to a helper method, as methods in the model are strictly for handling data.

Here is what you could do:

# 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) %>

Solution 5 - Ruby on-Rails

I really like following clean solution.

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

It's from http://hawkins.io/2012/03/generating_urls_whenever_and_wherever_you_want/

Solution 6 - Ruby on-Rails

While there might be a way I would tend to keep that kind of logic out of the Model. I agree that you shouldn't put that in the view (keep it skinny) but unless the model is returning a url as a piece of data to the controller, the routing stuff should be in the controller.

Solution 7 - Ruby on-Rails

(Edit: Forget my previous babble...)

Ok, there might be situations where you would go either to the model or to some other url... But I don't really think this belongs in the model, the view (or maybe the model) sounds more apropriate.

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").

If you find yourself writing nasty amounts of logic in a view, try to move the logic somewhere more appropriate; request and internal communication logic probably belongs in the controller, data related logic may be placed in the model (but not display logic, which includes link tags etc.) and logic that is purely display related would be placed in a helper.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAaron LongwellView Question on Stackoverflow
Solution 1 - Ruby on-RailsPaul HorsfallView Answer on Stackoverflow
Solution 2 - Ruby on-RailsAaron LongwellView Answer on Stackoverflow
Solution 3 - Ruby on-RailsmatthuhigginsView Answer on Stackoverflow
Solution 4 - Ruby on-RailsJosh DelsmanView Answer on Stackoverflow
Solution 5 - Ruby on-RailsSwar ShahView Answer on Stackoverflow
Solution 6 - Ruby on-Railsrmontgomery429View Answer on Stackoverflow
Solution 7 - Ruby on-RailsStein G. StrindhaugView Answer on Stackoverflow