Being a bit cheeky one could argue that if a helper method is useful and safe to use outside of ActionView, then it should probably be refactored out into its own module - perhaps ActiveSupport.
Seeing ActionView or ActionController in the model code should generally make you shiver, I think. :)
@Ben, interesting, link_to does not work directly in a controller for me. What version of Rails are you using? And have you installed any additional plugins which might enable this?
Observers, like models, exist outside the scope of the current request. Perhaps you need a sweeper instead? That's like an observer but has access to URL helpers and other request related things.
@Per Velschow, good point. I could see the pluralize helper method being moved into ActiveSupport, but the interface would likely need to change (maybe being callable on integer or string) and I'm not entirely certain these kind of methods really belong on those classes.
But until that happens, I'm okay with referencing ActionController::Base in a model if it's simply to get the helper methods that work well in a model. Unless of course you're building a gem of some kind that needs to be used outside of the traditional Rails structure.
Interesting stuff. However, there's a good reason why you have to jump through hoops to use helper methods in non-view code: because helper methods are for presentation. MVC and all that. That may be a worthwhile tradeoff, but it's important to keep in mind.
@Ryan-
Is there a way to access the "current_user" from the model and/or from an observer? Or is that just a terrible practice? I find I'm often wanting to get the current_user's id in various models, and ALWAYS need it for event observers (where an event is logging the who, what, when of an action).
I have never seen that @template variable before. Never knew you could do that. Hopefully that will help when trying to render a link_to_remote inside a controller. I have pretty much quit a project cause I couldn't get it to work.
@James
user is a notion that exist only in the context of a request, therefore it is within controller scope.
To observe a model in this context you have to use a sweeper.
For audit trails use the fantastic acts_as_audited plugin
http://github.com/collectiveidea/acts_as_audited/tree/master
You will find the exact scenario of logging user action on models.
Another technique I used is to introduce instance variables on
retrieved model objects and assign current_user or action to them in the controller. This captures the idea that this is not the peristent object, but the object manipulated via an interface.
Once that is done, you can even implement audit or access control within the model class via callbacks and validations, respectively. Some of your validations can be conditioned on on whether these 'controller-related' instance variables are nonnil. If nonnil, then the model is manipulated via a controller (rather than, say a daemon/script/console).
Well, technically pluralize() is available through ActiveSupport today, though it doesn't switch strategies based on a count. That's pretty easy to add though:
How would you use your app helpers in a mailer body. Assuming I had a signup email, and I wanted to use a helper from my users_helper.rb inside my signup.html.erb. The docs say I can use action pack helpers but make no reference to using application helpers.
I found this cast extremely useful. There have been a number of times when I had to call a helper from the controller. Up till now I've been loading the helpers directly in, which is not ideal. I'm definitely going to be modifying my approach in the future.
Thanks for the great advice and keep up the good work.
@Anlek I hit the same problem with loading helpers into emails. After some googling it turns out that you can load additional helpers into action_mailer and ar_mailer. You just have to add a:
What if I want to create a Custom class in lib/ and have this class access the concat and capture View helpers. So that I can use this class in my views.
Example:
lib/my_class.rb
class MyClass
def some_method
concat 'something'
end
end
Instead of using @template.link_to() in your controller, you can use self.class.helpers.link_to() which is probably a bit safer than accessing the private API.
just passed this onto a colleague who was doing a little research on that. And he actually bought me lunch because I found it for him smile So let me rephrase that
I don't know why, but in my controller, I simply can do this:
flash[:notice] = "Successfully created #{link_to('product', @product)}."
and as far as I know i din't do anything fancy.
But I have another question: What about url_helpers in classes inside an observer? I didn't get it to work...
Ben
"url_helpers in classes inside an observer" should be "url_helpers inside an observer" :)
Being a bit cheeky one could argue that if a helper method is useful and safe to use outside of ActionView, then it should probably be refactored out into its own module - perhaps ActiveSupport.
Seeing ActionView or ActionController in the model code should generally make you shiver, I think. :)
@Ben, interesting, link_to does not work directly in a controller for me. What version of Rails are you using? And have you installed any additional plugins which might enable this?
Observers, like models, exist outside the scope of the current request. Perhaps you need a sweeper instead? That's like an observer but has access to URL helpers and other request related things.
@Per Velschow, good point. I could see the pluralize helper method being moved into ActiveSupport, but the interface would likely need to change (maybe being callable on integer or string) and I'm not entirely certain these kind of methods really belong on those classes.
But until that happens, I'm okay with referencing ActionController::Base in a model if it's simply to get the helper methods that work well in a model. Unless of course you're building a gem of some kind that needs to be used outside of the traditional Rails structure.
Interesting stuff. However, there's a good reason why you have to jump through hoops to use helper methods in non-view code: because helper methods are for presentation. MVC and all that. That may be a worthwhile tradeoff, but it's important to keep in mind.
Would be better to have the reference in a class base of our models ??
class BaseModel
def helpers
ActionController::Base.helpers
end
end
class Category < BaseModel
...
or is there a better way ?
Thanks
also, view's methods can be deligated:
delegate :number_with_precision, :to => 'ActionController::Base.helpers'
@Ryan: link_to is in actionview's namespace, so w/o extending it's not available in controller
Great as always Ryan!
One question: what would you do if you want to use a custom helper from application_helper.rb in a controller or model?
Nice hint. Cheers.
@Ryan-
Is there a way to access the "current_user" from the model and/or from an observer? Or is that just a terrible practice? I find I'm often wanting to get the current_user's id in various models, and ALWAYS need it for event observers (where an event is logging the who, what, when of an action).
Is the 'ActionController::Base.helpers' method only available in 2.1?
We're using Rails 2.0.2 and get a 'method not found' trying to use it.
I have never seen that @template variable before. Never knew you could do that. Hopefully that will help when trying to render a link_to_remote inside a controller. I have pretty much quit a project cause I couldn't get it to work.
Thanks again Ryan.
@James
user is a notion that exist only in the context of a request, therefore it is within controller scope.
To observe a model in this context you have to use a sweeper.
For audit trails use the fantastic acts_as_audited plugin
http://github.com/collectiveidea/acts_as_audited/tree/master
You will find the exact scenario of logging user action on models.
Another technique I used is to introduce instance variables on
retrieved model objects and assign current_user or action to them in the controller. This captures the idea that this is not the peristent object, but the object manipulated via an interface.
Once that is done, you can even implement audit or access control within the model class via callbacks and validations, respectively. Some of your validations can be conditioned on on whether these 'controller-related' instance variables are nonnil. If nonnil, then the model is manipulated via a controller (rather than, say a daemon/script/console).
Very good cast!
Well, technically pluralize() is available through ActiveSupport today, though it doesn't switch strategies based on a count. That's pretty easy to add though:
"product".send(products.count != 1 ? :pluralize : :to_s)
How would you use your app helpers in a mailer body. Assuming I had a signup email, and I wanted to use a helper from my users_helper.rb inside my signup.html.erb. The docs say I can use action pack helpers but make no reference to using application helpers.
Thanks for the great podcast!
Figured out my own question, see this link: http://www.railsonwave.com/railsonwave/2007/8/13/use-helper-functions-in-you-email-view
I found this cast extremely useful. There have been a number of times when I had to call a helper from the controller. Up till now I've been loading the helpers directly in, which is not ideal. I'm definitely going to be modifying my approach in the future.
Thanks for the great advice and keep up the good work.
@Anlek I hit the same problem with loading helpers into emails. After some googling it turns out that you can load additional helpers into action_mailer and ar_mailer. You just have to add a:
helper :name_of_helper
to the top of you email model class.
What if I want to create a Custom class in lib/ and have this class access the concat and capture View helpers. So that I can use this class in my views.
Example:
lib/my_class.rb
class MyClass
def some_method
concat 'something'
end
end
views/[...]/index.html.erb
[...]
<% MyClass.some_method %>
Thanks.
Oups, forgot a "self." in the Class method definition:
def self.some_method
[...]
I used the same trick to extend string with all ActionView helpers http://pragmatig.wordpress.com/2009/05/30/all-actionview-helpers-on-strings/
Instead of using @template.link_to() in your controller, you can use self.class.helpers.link_to() which is probably a bit safer than accessing the private API.
Thanks !!!!!
In rails 3 inside a controller the @template variable is nil...
just passed this onto a colleague who was doing a little research on that. And he actually bought me lunch because I found it for him smile So let me rephrase that
Great helper method! It will be useful for me in the future. Thanks a lot!
is this working in rails 3?
The
@template
instance variable is no longer available in Rails 3.Instead you can use this:
Hope this helps :)
Thanks! That helped
In rails 4 using "view_context" works. Thank you.
Nice!
For rails 3, see the most voted answers here:
http://stackoverflow.com/questions/341143/can-rails-routing-helpers-i-e-mymodel-pathmodel-be-used-in-models
I think it isn't long enough :)
This episode has been updated to Rails 5 as a blog post Helpers Outside Views in Rails 5