View helper link_to in Model class
Using Rails 3, is there a way to use link_to helper, or any helper for that matter, inside model开发者_如何学Python?
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)
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.
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
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
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>"
Not without hackery.
If you think you need link_to
in a model, you're likely violating some principle of the 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).
精彩评论