#132
Oct 20, 2008

Helpers Outside Views

Have you ever wanted to access helper methods outside of the view layer? In this episode I show you a couple ways to do just that.
Download (10.4 MB, 6:14)
alternative download for iPod & Apple TV (8 MB, 6:14)

Full Episode Source Code

# models/category.rb
def description
  "This category has #{helpers.pluralize(products.count, 'product')}."
end

def helpers
  ActionController::Base.helpers
end

# products_controller.rb
def create
  @product = Product.new(params[:product])
  if @product.save
    flash[:notice] = "Successfully created #{@template.link_to('product', @product)}."
    redirect_to products_url
  else
    render :action => 'new'
  end
end

RSS Feed for Episode Comments 17 comments

1. Ben Oct 20, 2008 at 00:41

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


2. Ben Oct 20, 2008 at 00:43

"url_helpers in classes inside an observer" should be "url_helpers inside an observer" :)


3. Per Velschow Oct 20, 2008 at 03:46

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


4. Ryan Bates Oct 20, 2008 at 07:23

@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.


5. Mark Wilden Oct 20, 2008 at 09:44

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.


6. aprendizbasico Oct 20, 2008 at 09:54

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


7. Ol.keene Oct 20, 2008 at 11:02

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


8. Bob Walsh Oct 20, 2008 at 11:21

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?


9. AC Oct 20, 2008 at 13:40

Nice hint. Cheers.


10. James Oct 20, 2008 at 14:04

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


11. Darryl Oct 20, 2008 at 15:14

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.


12. taelor Oct 20, 2008 at 16:32

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.


13. vtron Oct 21, 2008 at 06:13

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

 


14. Lopre Oct 21, 2008 at 07:31

Very good cast!


15. James Edward Gray II Oct 22, 2008 at 14:16

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)


16. Anlek Oct 23, 2008 at 07:22

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!


17. Anlek Oct 23, 2008 at 08:53

Figured out my own question, see this link: http://www.railsonwave.com/railsonwave/2007/8/13/use-helper-functions-in-you-email-view


18. Forrest Zeisler Oct 28, 2008 at 10:28

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.


19. Forrest Zeisler Oct 28, 2008 at 10:32

@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.


20. Guest Apr 26, 2009 at 02:33

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.


21. Guest Apr 26, 2009 at 02:36

Oups, forgot a "self." in the Class method definition:
  def self.some_method
  [...]


22. grosser May 30, 2009 at 01:25

I used the same trick to extend string with all ActionView helpers http://pragmatig.wordpress.com/2009/05/30/all-actionview-helpers-on-strings/


23. Michael Sheakoski Jun 11, 2009 at 09:11

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.


24. cocozz Mar 12, 2010 at 01:52

Thanks !!!!!


25. ed hardyswimwear Jul 20, 2010 at 00:06

23213


26. Pioz Jul 21, 2010 at 07:44

In rails 3 inside a controller the @template variable is nil...


27. 90x workout Aug 12, 2010 at 09:26

I have checked your site and I invite others please check it because the information that is available in your site can spell bound any one.and the images that you have shown in this site also very attractive. There is one more site that I have visited, provide me information that is really unique and the service that they offer also I have experienced, really I enjoy this sevice so I request others please visite this and I want to tell you that after enjoying this service you’ll enjoy it again and again its .


28. wholesale new era caps Aug 20, 2010 at 20:55

Perhaps this is one of the most interesting blogs that I have ever seen. Interesting article, Funny comment. Keep it up!


29. louis vuitton shoes Aug 26, 2010 at 21:17

Thanks for sharing your article. I really enjoyed it. I put a link to my site to here so other people can read it. My readers have about the same interets


30. Wholesale Electronics Aug 27, 2010 at 00:30

Discount Wholesale Electronics, Wholesale Cell Phones, Electronic Gadgets and More from the Best Dropship Wholesaler


31. snow boots Aug 30, 2010 at 21:23

if you want to use a custom helper from application_helper.rb in a controller or model?


32. louis vuitton sunglasses Sep 01, 2010 at 21:30

I agree with your Blog and I will be back to check it more in the future so please keep up your work. I love your content & the way that you write. It looks like you’ve been doing this for a while now, how long have you been blogging for?

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source