Why can private helper methods still be accessed in views? - ruby-on-rails

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

Related

access control tripping me up

ok, i'm about at that point in my ruby career where this should be tripping me up.
I have a model called distribution.rb where I have the follwoing protected method:
def update_email_sent_on_date
if self.send_to_changed?
self.date_email_delivered = DateTime.now
end
end
I then call this method from my controller:
distribution.update_email_sent_on_date
however, I'm getting this error:
NoMethodError (protected method `update_email_sent_on_date' called for #<EmailDistribution:0x131a1be90>):
the distribution object is indeed an EmailDistribution (a subclass of distribution where the method is defined). I thought this would work. In any case, I also tried moving the method to the subclass EmailDistribution but no luck. Same error message.
I'd also like to step back and say that what I'm trying to do overall is store the timestamp of when another field in the distribution model is updated. If there's a simpler way, please enlighten me.
I think you're getting tripped up because you are using the protected declaration when you actually want the private declaration.
The protected term in ruby acts differently in other conventional languages.
In Ruby, private visibility is what protected was in Java. Private methods in Ruby are accessible from children. This is a sensible design, since in Java, when method was private, it rendered it useless for children classes: making it a rule, that all methods should be "protected" by default, and never private. However, you can't have truly private methods in Ruby; you can't completely hide a method.
The difference between protected and private is subtle. If a method is protected, it may be called by any instance of the defining class or its subclasses. If a method is private, it may be called only within the context of the calling object---it is never possible to access another object instance's private methods directly, even if the object is of the same class as the caller. For protected methods, they are accessible from objects of the same class (or children).
This is a slightly clearer explanation IMHO, from the book Eloquent Ruby by Russ Olsen:
Note that in Ruby, private methods are callable from subclasses. Think about it: You don't need an explicit object reference to call a superclass method from a subclass.
The rules for protected methods are looser and a bit more complex: Any instance of a class can call a protected method on any other instance of the class.
Lastly, it's good to note that in ruby you can always call private or protected methods regardless of whether they are accessible by using the send method. So if you were in a pinch and just needed to work you could just call it like this and then worry about the private/protected declarations later:
distribution.send(:update_email_sent_on_date)
Read this for more a better explanation..

What is the mechanism behind view helpers?

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.

Symfony: trying to overwriting a method of a symfony class

i want to overwrite a method of
symfony/lib/plugins/sfDoctrinePlugin/lib/form/sfFormDoctrine.class.php.
I think a good way could be writing again the method in the form class
where i need that method.
In that case if i need that method in other form class should i write
again the new method, so i would break the rule DRY...
So is there any better way?
Regards
Javi
You should be using BaseFormDoctrine if this is a Doctrine specific method or BaseForm if you want the method to apply to all forms. These form classes are provided specifically for this purpose.
Piggybacking on Colonel Sponz's answer, I've done this many times by extending the class I want to override. By extending the class, you don't have to duplicate anything. In the method(s) you want to customize, just add your customized code and then call parent::method_name() to execute the same method of the super class. You get all the benefits of both. Calls to methods that don't exist in the subclass will execute against the super class.
It should be noted that this strategy is basic OOP stuff and isn't limited to Symfony or even PHP.
You could create a new class that inherits from sfFormDoctrine that redeclares the methods you need and then use your new class in place of sfFormDoctrine wherever you need that method.

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.

Use helper methods within a controller

I defined a helper abc() in annotations_helper.rb. What do I have to do such that I can use this method in annotations_controller.rb?
In general helpers are supposed to be "view helpers" and not called from controllers.
You probably want to put something like that in application_controller.rb
This is usually not a good practice to use a helper in your controllers. You should try to move the logic inside a model or if the logic is too generic, you should move that to lib/some_lib.rb and include that in your model to use.
However check out this blog post if you really want to do this. Don't forget to read the comments.

Resources