I note with displeasure that helper methods are not available in their respective controller. This has two problems: 1. It''s confusing, because the XyzController class has it right there in black and white: include XyzHelper. 2. It''s a pain, because I want to share methods between my controller and my view. I fully accept that by wishing for (2) I may be violating good Rails practice, in which case I ask for advice. In my case, there''s a helper method called "link", which produces HTML links to various places based on the logged-on state of the user. Thus it needs to call methods like logged_on? But methods in the controller need to call logged_on? as well. I don''t want to duplicate code between the helper and the controller. Can anyone suggest anything? Thanks, Gavin
David Heinemeier Hansson
2004-Nov-13 18:21 UTC
Re: Helper methods not available in controller
> 1. It''s confusing, because the XyzController class has it right there > in black and white: include XyzHelper.It is confusing, it will be much less so once http://dev.rubyonrails.org/trac.cgi/ticket/170 has been applied. And it will be before the release of the next version.> 2. It''s a pain, because I want to share methods between my controller > and my view. > > I fully accept that by wishing for (2) I may be violating good Rails > practice, in which case I ask for advice. > > In my case, there''s a helper method called "link", which produces HTML > links to various places based on the logged-on state of the user. > Thus it needs to call methods like logged_on? But methods in the > controller need to call logged_on? as well.You can create a protected/private method in the AbstractApplicationController (or in a specific controller if it''s a restricted need) and then access this method from the helper. Like so: class AbstractApplicationController < ActionController::Base private def logged_on? # returns true/false end end class ApplicationHelper def logged_on? @controller.send(:logged_on?) end end This is how UrlHelper uses url_for and link_to. So you basically just delegate from the Helper to the controller. I''ll think about and take suggestions on how this might be different. Keep in mind that we don''t want Helper methods available in the controllers as public methods (that could then be called through the URL). But I actually kinda like the delegation approach as you often need almost the same but not quite in controller and helper. And it enforces the reminder that there should be separation. Basically, this is the "everything is possible, but bad things should be ugly" approach from Matz. -- David Heinemeier Hansson, http://www.basecamphq.com/ -- Web-based Project Management http://www.rubyonrails.org/ -- Web-application framework for Ruby http://macromates.com/ -- TextMate: Code and markup editor (OS X) http://www.loudthinking.com/ -- Broadcasting Brain
On Sunday, November 14, 2004, 5:21:34 AM, David wrote:> You can create a protected/private method in the > AbstractApplicationController (or in a specific controller if it''s a > restricted need) and then access this method from the helper. Like so:> class AbstractApplicationController < ActionController::Base > private > def logged_on? > # returns true/false > end > end> class ApplicationHelper > def logged_on? > @controller.send(:logged_on?) > end > endThanks for the suggestion.> This is how UrlHelper uses url_for and link_to. So you basically just > delegate from the Helper to the controller.> I''ll think about and take suggestions on how this might be different.That could be code-generated, like so: module ApplicationHelper controller_delegate :logged_on? end I''m sure there''s a better name than controller_delegate. Hmmm... then again, where would controller_delegate be defined? ApplicationHelper is just a bare module. This is an alternative, I s''pose: class UserController < AbstractApplicationController private def logged_on? # return true/false end helper_method :logged_on? end I think I like that better, actually.> Keep in mind that we don''t want Helper methods available in the > controllers as public methods (that could then be called through the > URL). But I actually kinda like the delegation approach as you often > need almost the same but not quite in controller and helper.I haven''t found that I need subtly different behaviour, but that''s a lack of experience.> And it enforces the reminder that there should be separation.Yes, that''s a good thing.> Basically, this is the "everything is possible, but bad things should > be ugly" approach from Matz.So is it a code smell to need the same thing in the two places? I''m sure there are other ways I could structure my app. I''m just trying to do what seems simple for now. My solution at the moment is a separate module, UserLogic, which is included in UserController and UserHelper. It works, but I''d rather avoid it. It''s nice to be able to see all the guts in one place, and supporting methods such as these feel like they belong in the controller. It feels right to be able to export some controller methods to the helper. That''s even better than all methods being available. Gavin
David Heinemeier Hansson wrote:> I''ll think about and take suggestions on how this might be different. > Keep in mind that we don''t want Helper methods available in the > controllers as public methods (that could then be called through the > URL). But I actually kinda like the delegation approach as you often > need almost the same but not quite in controller and helper. And it > enforces the reminder that there should be separation.I appreciate this separation. To make Ticket #170 even clearer, the helper method could be named view_helper. However, if explicit delegation is bothersome to enough people, we could implicitly delegate missing view methods to the controller: class ActionView::Base private def method_missing(symbol, *args, &block) controller.send(symbol, *args, &block) end end This would reduce Gavin''s link example to AbstractApplicationController#link which would be accessible from every view (unless #link is defined in a view helper!) That said, I still prefer explicit delegation using helpers. Best regards, jeremy
David Heinemeier Hansson
2004-Nov-14 10:31 UTC
Re: Helper methods not available in controller
> I appreciate this separation. To make Ticket #170 even clearer, the > helper method could be named view_helper.#170 is hot stuff. I don''t think we need to spell it out further than "helper", though. That''s already the term associated with "stuff to make views easier".> That said, I still prefer explicit delegation using helpers.Me too. Automatic delegation might just be a tad to magical. And I''m usually a pretty big fan of magic ;) -- David Heinemeier Hansson, http://www.basecamphq.com/ -- Web-based Project Management http://www.rubyonrails.org/ -- Web-application framework for Ruby http://macromates.com/ -- TextMate: Code and markup editor (OS X) http://www.loudthinking.com/ -- Broadcasting Brain
David Heinemeier Hansson
2004-Nov-14 11:06 UTC
Re: Helper methods not available in controller
> class UserController < AbstractApplicationController > private > def logged_on? > # return true/false > end > helper_method :logged_on? > end > > I think I like that better, actually.That''s not a bad idea! I like that and would accept a patch to such.>> Basically, this is the "everything is possible, but bad things should >> be ugly" approach from Matz. > > So is it a code smell to need the same thing in the two places? I''m > sure there are other ways I could structure my app. I''m just trying > to do what seems simple for now.I think it''s a mild smell. If you need logic in two places, maybe it could be better served further down the stack, such as in the model. Some times the "repetition" is justified, though. I think logged_in? could very well be that.> My solution at the moment is a separate module, UserLogic, which is > included in UserController and UserHelper. It works, but I''d rather > avoid it. It''s nice to be able to see all the guts in one place, and > supporting methods such as these feel like they belong in the > controller.I''d prefer the approach of having the logic in the controller and then delegating from the helper instead of introducing a Third Place and delegating to that from both controller and helper.> It feels right to be able to export some controller methods to the > helper. That''s even better than all methods being available.I agree. "helper_method" is cool, I''d support that. -- David Heinemeier Hansson, http://www.basecamphq.com/ -- Web-based Project Management http://www.rubyonrails.org/ -- Web-application framework for Ruby http://macromates.com/ -- TextMate: Code and markup editor (OS X) http://www.loudthinking.com/ -- Broadcasting Brain
On Sunday, November 14, 2004, 10:06:36 PM, David wrote:>> class UserController < AbstractApplicationController >> private >> def logged_on? >> # return true/false >> end >> helper_method :logged_on? >> end >> >> I think I like that better, actually.> That''s not a bad idea! I like that and would accept a patch to such.Cool. Time to cvs checkout... Gavin