What is the mechanism behind view helpers? - ruby-on-rails

I wonder what approach Rails uses to find the correct method of a view helper. I recognized while calling a view helper method in a partial that the view helper must not belong to the same view nor must it have a similar name, the method is always found. If more than one view helper has the same method, there is some logic behind to find the "nearest" helper and use this method. Is that mechanism somewhere documented (or blogged about in detail)?

My guess would be:
The view class automatically includes the helper modules in the order of increasing priority("near"ness). The "nearest" helper module is included last, and its methods override any previously defined methods with the same name.

Related

Which class, specifically, do view helpers get mixed into?

Everything I read says that view helpers get mixed into views, but which class, specifically do they get mixed into?
References:
http://guides.rubyonrails.org/getting_started.html#view-helpers
Why can private helper methods still be accessed in views?
Do helper classes get mixed into the controller?
The controller has a view_context, which is an instance of view_context_class, which is (by default) an anonymous subclass of ActionView::Base created by ActionView::Base.prepare. The helpers are mixed in to these view context classes.
The view context is also the place where the controller instance variables "magically" become instance variables in the view.

Why can private helper methods still be accessed in views?

Just another "why is it that way" question: I noticed that private helper methods still can be accessed within views. Why's that? And is there a way to prevent this (e.g. when having helper methods that should only be called from within another helper)?
Helpers are modules that get mixed in to the views. This means that public, protected and private methods in the helper become public, protected and private methods on the views.
I don't think that you can actually hide the helper methods from the view. You'd need to do something like have a helper class which you instantiate in the helper and then delegate calls to that - sounds like it could get messy fast though. :)

Help! My application_helper.rb is getting unwieldily

My application_helper.rb is getting pretty big. In my defense, it contains only HTML-generating methods, and none of those methods perform any sort of business logic. None of the methods are specific to any page or controller, and most are unrelated to each other.
What's the typical solution to this problem? Suck it up? Create additional *_helper.rb files to absorb some of application_helper's methods?
Usually we have helpers for controllers (one helper per controller, were you would place helper methods used only on that controller's templates) and you can also create helpers based on your own organization.
If you have many methods related to polls, you could create a PollsHelper and place all methods in there, even if there isn't a controller called PollsController. Also, Rails always loads all helpers and includes them in your views, so you can name them whatever you like (as long as they're inside the helpers folder and they end with _helper).
Typically I organise by type - e.g. button_and_link_helpers, conditional_helpers etc. I just create them as modules and include them in the controllers that need them.
This has the benefit of not loading loads of unused functions for every view

If I have a function that I use both in the controller and in the views, where should I put it?

I try to be very good about keeping my view code and my controller code separate, but occasionally I run into situations where I need to use the same function in the controller and in the views. Where should I put this function so that I can access it from both the controller and the view?
You can put it in a controller and make it available as a helper. If you need it to be available between multiple controllers and their views put in the application controller or other inherited controller:
helper_method :shared_function
According to your situation, for example if the function return a standard Variable value that don't require any controls, you can call it directly from the view, on the contrary, if you have a function that return for example an array that requires controls it's judicious to call it from the the model before you show what you want on the view.
I actually think a module is the best way to share code amongst controllers. Helpers are good if you want to share code amongst views. Helpers are basically glorified modules, so if you don't need view level access, I suggest placing a module in your lib folder.
If the code is really a set of utilities that doesn't need access to object state, I would consider putting it in a module to be called separately.
If the code needs state and is used in a subset of all controllers that are not very closely related, put it in a module and include it in necessary controllers.

call a helper from another view

I know that if I want to call a helper of another controller, I can do something like:
helper :other_controllers
But I was wondering why I can't do something like OtherControllersHelper.method inside the view?
Due to the way that Rails loads your modules, you cannot do this without modification.
Rails includes the associated helper models into the ActionView::Base instance used to render a template. ActionController::Helpers#helper (used in the example above) adds more helper modules to the list of those to be included. The helper methods that are used in views are written as instance methods. Modules in Ruby do not provide any good ways of getting at instance methods without using a constructor. Which is one of the big things that separates modules from classes.
To access your helpers from another controller with just OtherControllersHelper.method, you will need to redefine method as a class method. However, redefining those methods as class methods would make them inaccessible from your views.
You could duplicate all instance methods in your helpers as class methods, but that's definitely not a better solution that adding helper :other_controllers. There are ways to define wrappers pragmatically, but again, it's not the best way to handle the situation.
If you've got a lot of helpers that are likely to be used in multiple controllers/views maybe you're better off putting them somewhere else. Somewhere like app/helpers/application_helper.rb. Or another helper module that could be loaded only in the controllers that need it.

Resources