Just curious if anybody knows what Ruby technique is used to accomplish the following in the Rails framework.
If I don’t write, say, an index method on a Rails controller, Rails will still render the index view file if the URL matches that route. That makes sense, because my controller inherits from a parent class, which must have its own index method.
However, if I do define an index method, and only tell it to set an instance variable, it still renders the appropriate view. For example:
def index
@weasels = Weasel.all
# If I omit this line, Rails renders the index anyway.
# If this behavior is defined in the parent class's index method,
# it seems that by overriding the method, my index wouldn't do it.
# I'm not explicitly calling super to get the method from
# ActionController::Base, but maybe Rails is doing something like that?
render :index
end
In pure Ruby, I would expect to have to call super to get that behavior.
I assume that Rails uses some kind of metaprogramming technique to guarantee that my controller methods will call super. If so, can anyone explain it? Can you point to the source code that does this?
Update
As Mladen Jablanović has pointed out, my initial mental model was wrong; the controller method doesn’t normally render the view; instead, both the controller method and the view rendering are called by some framework code. This is apparent because I can make a controller method with any name – for example, search – and the search view will get rendered. So clearly I’m not overriding a parent method in that case, and some framework code is parsing the controller method name and looking for the matching view.
Still, the framework must be able to detect whether or not the controller method has already called render. So that’s another small mystery to me.
On the controller, there is a method called
render_for_text. This takes a string and sets the result as the response body. Even if you don’t render as text, rendering a view file simply reads the file’s contents, evaluates it, and passes it to therender_for_textmethod. The method then stores sets an instance variable called@performed_rendertotrue, which tells rails that the controller has already rendered a view.There is then a method called
performed?that indicates whether or not the render action has been called. It does this by checking if@performed_renderor@performed_redirectis true.With the above information, the very first line of the render method should make sense now, and hopefully answer your question:
raise DoubleRenderError, "Can only render or redirect once per action" if performed?