Running an expensive method in the view VS in the controller - Rails

Viewed 75

Running the risk of being tagged as "too broad", but this is an authentic doubt.

Say I have @my_model.complex_calculation_result to show in a view.

What are the pros and cons of:

1 - Calculating the value on the controller and send it to the view

Controller:

@result = @my_model.complex_calculation_result # caching the value in the controller

View:

<%= @result %>

2 - Calculating it directly in the view

<%= @my_model.complex_calculation_result %>

I know the last alternative represents less code and I have one less instance variable hanging around.

But are there performance diferences?

Guess 1 - the view already takes more memory to render it all, so the calculation can take longer from inside the view if it is memory expensive.

Any light shed on this and comments will be highly appreciated. :)

2 Answers

While I'm not answering the performance part of your question, you are breaking the Rails MVC principle when going the 2nd way. The View is not meant to perform any (especially complex) calculations on Model data.

TL;DR

In case of memory I think the only one possibility to 'slow it down' is when you exceed your memory and starting to use swap file. I haven't researched rails so deep, but if we take your thoughts into account and think that average controller execution takes X memory and average view takes 1.3 * X (absolutely random coefficient from my head), then chances to get into swap from view will be slightly bigger, then from the controller. If my thoughts are correct, then controller is better in technical side.

On conceptual side. Views are just for rendering results and in your case I would definitely move this heavy method outside the view. You are concerned about 'additional instance variable' and your concerns are correct...

My team follows Sandy Metz's rules. One of the state:

Pass only one single instance interface(variable) to the view. If you need multiple instance variables, then wrap the whole logic with Facade pattern and let this facade provide all the interfaces you need.

So... I would setup a facade and wrap this heavy method into one of it's property.

So we have 2-0 to put it into controllers.

PS: I have strong concerns about lazy loading and so on.... I think that facade method will be calculated only after it's real call (not during instance initialization), so for real your slow logic will still get be called only in view (during first real refer to this method). I think you can prevent this by making all the calculations inside Facade constructor initialize and put the result into instance variable and then refer from view, this variable with result.... But...

I am not even sure, that variable will be calculated before first real usage (it's possible that there are also such mechanism for optimization to not execute, what is not yet used. As ActiveRecord does such thing during methods chaining. No real SQL executed unless you really refer to one of the object). So I have concerns, that even moving this method into Facade constructor can still end up with it being calculated only in view. But you can check it with logs/debuggers to be sure

Related