View helper link_to in Model class

Ruby on-RailsRuby on-Rails-3

Ruby on-Rails Problem Overview


Using Rails 3, is there a way to use link_to helper, or any helper for that matter, inside model?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

There are some reasons that you may need link_to in a model. Yes, @andy, it's a violation of MVC, but that doesn't mean you should get points for not answering the question.

@schwabsauce, it's easier than that. The first line isn't even strictly necessary if you do it in an initializer or something. Same thing works for .sanitize and .raw and a whole load of other awesome functions.

ActionView::Base.send(:include, Rails.application.routes.url_helpers)
ActionController::Base.helpers.link_to(whatever)

If you want to use autopaths you may have to do this inside your link_to:

Rails.application.routes.url_helpers.page_path(@page)

Solution 2 - Ruby on-Rails

Be very careful following the advice outlined in Chuck's post if you're doing this in Rails 3.2.1 . It would seem as though that approach is not a safe way to go about including the link_to helper in non-view classes in Rails 3.2.1. There is a safer way (that works for us in any case) outlined below.

When we used the approach in Chuck's post in one of our classes, it ended up having very troubling and difficult to debug consequences. It ended up causing side effects / bugs that only turned up in very specific (and rare) circumstances.

The problem, as far as we can tell, is that this line:

ActionView::Base.send(:include, Rails.application.routes.url_helpers)

Is telling ActionView::Base to include the Rails.application.routes.url_helpers, which ActionView::Base apparently already does on its own. Having it include the url_helpers a second time, seems to cause re-initialization of the routes state (@_routes in classes that have included the ActionDispatch::Routing::UrlFor module).

This leads to seemingly random and unexplained "undefined method 'url_for' for nil:NilClass" exceptions in views that attempt to call, directly or indirectly, the url_for method after ActionView::Base has included the url_helpers the second time.

The solution that worked for us was instead of telling ActionView::Base to include the url_helpers again, just include the UrlHelper module yourself wherever you might need it.

Then when you need to use link_to and have access to the path you can simply do this (assuming login_path is valid for your app):

include ActionView::Helpers::UrlHelper
...
link = link_to('here', Rails.application.routes.url_helpers.login_path)

It took us a very long time and quite a lot of head scratching to track down the bugs caused by the double-include and I just wanted to warn others to be careful when tweaking the behavior of the Rails base classes.

Solution 3 - Ruby on-Rails

I got this to work with the following inclusions:

include ActionView::Helpers::UrlHelper
include ActionController::UrlFor
include Rails.application.routes.url_helpers

cattr_accessor :controller
def controller; self.class.controller; end
def request; controller.request; end

Then in my controller I populated the attribute (creating a controller from scratch requires a significant amount of data in the arguments hash).

Lead.controller = self

Solution 4 - Ruby on-Rails

link_to helper, MVC violation

What Andy said,

if you're generating HTML in the model you probably need to take a big long look at what you're doing and why.

URL helpers

URLs on the other hand often come in handy outside of view-controller code, in all kinds of service/form/api/... classes for example, even in models if you have to.

Yes, Rails.application.routes.url_helpers is a module, but that doesn't mean you should just include it wherever or funny stuff will start happening as Gary put it:

https://www.destroyallsoftware.com/blog/2011/one-base-class-to-rule-them-all

What you can do is:

    delegate :url_helpers, :to => 'Rails.application.routes'

and then use, for example

    url_helpers.home_url

Solution 5 - Ruby on-Rails

If you want to use any module stuff anywhere and don't mind going against all Ruby norms, throw this hackery into a service:

module View
  extend self

  class HelperIncluder
    include ActionView::Helpers::UrlHelper
  end

  def link_to(*args)
    HelperIncluder.new.link_to(*args)
  end
end

And now:

View.link_to('a', 'b') => "<a href=\"b\">a</a>"

Solution 6 - Ruby on-Rails

Not without hackery.

If you think you need link_to in a model, you're likely violating some principle of the http://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller">Model-View-Controller architecture.

A model should be a place for data and business logic, but generating links is almost certainly a job for the controller or view (or, Rails specifically, in a helper class).

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
QuestionKruleView Question on Stackoverflow
Solution 1 - Ruby on-RailsChuck VoseView Answer on Stackoverflow
Solution 2 - Ruby on-Railsuser724498View Answer on Stackoverflow
Solution 3 - Ruby on-RailsschwabsauceView Answer on Stackoverflow
Solution 4 - Ruby on-RailsbbozoView Answer on Stackoverflow
Solution 5 - Ruby on-RailsJacquenView Answer on Stackoverflow
Solution 6 - Ruby on-RailsAndy LindemanView Answer on Stackoverflow