Running an expensive method in the view VS in the controller - Rails - ruby-on-rails

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

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

Why controller action should call one model method other than an initial find or new?

I have almost all of the "shared" statements in functions in my model. The problem is that I am getting the following error, when I need to use more then one of these functions in my controller:
Controller action should call one model method other than an initial
find or new
and the IDE goes deeper explaining that:
This inspection warns if a controller action contains more than one
model method call, after the initial .find or .new. It’s recommended
that you implement all business logic inside the model class, and use
a single method to access it.
Is this mean that all of the logic should be put in more complex model functions? I have thought that the work of the controller is to call model functions and passes the results to the view.
If I put back the model functions code back to the controller, everything will work, but I will get a code duplication in all my controller actions.
So, what is the right approach here?
The warning message indeed means that the logic should be put in a single model function, but not necessarily more complex ones. To avoid model duplication and/or the "fat model" problem, you may need to introduce additional classes that the model relies on.
Yes, the work of the control is to call model functions, but only as a thin veneer, per this inspection guideline of one model function per controller action aside from an initial create/find.
I'm not sure I understand your comment about getting code duplication in your controller if you move functions back up, since you can always introduce shared functions at the controller level. But again, that's not the recommended approach of "thin controller" and "reasonably thin model" with supporting classes as required.

Why use instance variables to "connect" controllers with views?

This is a conceptual question and I haven't been able to find the answer in SO, so here I go:
Why instance variables are used to connect controllers and views? Don't we have two different objects of two different classes (Controller vs Views). So, when the view is rendered we are in a different context, but we are using instance variables of another object? Isn't this breaking encapsulation in somehow?
How does Rails manage to do that matching from one object to another? Does it clone all the instances variables of the controller to the view?
In a sense, you could say that it is breaking encapsulation. I have found that if you are not careful, it is easy to get your business/presentation logic mixed together in Rails. It usually starts when I am writing a view template, and discover that I need some value which I didn't pass from the controller. So I go back, and tweak the controller to suit what I need in the view. After one tweak, and another, and another, you look at the controller method, and it is setting all kinds of instance variables which don't make sense unless you look at the view to see what they are for. So you end up in a situation where you need to look at both controller and view to understand either, rather than being able to take one or the other in isolation.
I think that using instance variables (together with the Binding trick) is simply a way to pass whatever values you need from controller to view, without having to declare parameters in advance (as you would when defining a method). No declarations means less code to write, and less to change when you want to refactor and reorganize things.
Rails uses eval and Binding to pass controller instance variables to views. See this presentation from Dave Thomas, there's a small example at minute 46' that explains how this is done.

Is it bad design to base control flow/conditionals around an object's class?

I'm currently working on a Rails project, and have found times where it's easiest to do
if object.class == Foo
...
else if object.class == Bar
...
else
...
I started doing this in views where I needed to display different objects in different ways, but have found myself using it in other places now, such as in functions that take objects as arguments. I'm not precisely sure why, but I feel like this is not good practice.
If it's not good practice, why so?
If it's totally fine, when are times that one might want to use this specifically?
Thanks!
Not sure why that works for you at all. When you need to test whether object is instance of class Foo you should use
object.is_a? Foo
But it's not a good practice in Ruby anyway. It'd much better to use polymorphism whenever it's possible. For example, if somewhere in the code you can have object of two different classes and you need to display them differently you can define display method in both classes. After that you can call object.display and object will be displayed using method defined in the corresponding class.
Advantage of that approach is that when you need to add support for the third class or a whole bunch of new classes all you'll need to do is define display method in every one of them. But nothing will change in places where you actually using this method.
It's better to express type specific behavior using subtyping.
Let the objects know how they are displays. Create a method Display() and pass all you need from outside as parameter. Let "Foo" know to display foo and "Bar" know how to display bar.
There are many articles on replacing conditionals with polymorphism.
It’s not a good idea for several reasons. One of them is duck typing – once you start explicitly checking for object class in the code, you can no longer simply pass an instance of a different class that conforms to a similar interface as the original object. This makes proxying, mocking and other common design tricks harder. (The point can be also generalized as breaking encapsulation. It can be argued that the object’s class is an implementation detail that you as a consumer should not be interested in. Broken encapsulation ≈ tight coupling ≈ pain.)
Another reason is extensibility. When you have a giant switch over the object type and want to add one more case, you have to alter the switch code. If this code is embedded in a library, for example, the library users can’t simply extend the library’s behaviour without altering the library code. Ideally all behaviour of an object should be a part of the object itself, so that you can add new behaviour just by adding more object types.
If you need to display different objects in a different way, can’t you simply make the drawing code a part of the object?

why is using if-else blocks inside views dreaded? - MVC

What areas get affected by it?
code readability? maintainability? performance? security? any other?
my views have been using something like
if(Model.Showthis) {<div id = "showthis">...</div>} }
and does doing something like the following have security implications? --
<%if (HttpContext.Current.User.Identity.IsAuthenticated && item.Poster.UserName == HttpContext.Current.User.Identity.Name)%>
<%{%>
...
<%}%>
yes I read "if" considered harmful in ASP.NET MVC View (.aspx) files? too, but it didn't exactly specify what areas get affected by it. I mean I wanted to make sure there are no security and performance implications, which the link didn't answer exactly
I think the stigma persists from the transition from ASP to ASP.NET Forms. MVC is a different beast. With MVC3 and Razor it should help break the stigma and allow for more readable, easier to write Views.
That said, Domain and Business logic should never be in your template. But I see no problem, and frequently make use of conditional statements and loops to render UI in my Views.
I don't see an issue with it as long as the branching logic is purely for UI concerns. With that in mind I would change your second if to:
<%if (item.Poster.UserName == Model.CurrentUserName)%>
<%{%>
...
<%}%>
And set your model up so that the CurrentUserName property looks like this:
public string CurrentUserName
{
get
{
return HttpContext.Current.User.Identity.IsAuthenticated
? HttpContext.Current.User.Identity.Name
: String.Empty;
}
}
Or even better if you're going to be checking author against current user a lot in your system, offload that property to a helper class that can be reused from multiple models. Basically I wanted to get the implementation detail of what the current user's name is out of the view.
There's nothing wrong with if else statements in your view template. What you want to avoid is business or model logic in side your view template. If the conditional is directly related to user interface, then it belongs in the view.
Yes, it can be a bit difficult to read because of the mix of HTML and C#, but so long as your view logic is in your view, and your model logic is in your model, you are properly maintaining separation of concerns.
With your second sample; it might make more sense to only have your Poster available to the View if the correct user is logged in in the first place; this seems like more of a Controller thing than a View thing to me.
A lot of people see if statements in the View and cringe, because it reminds them of classic ASP. But the problem with classic ASP was never that there was code and HTML in the same place; it was that there was business logic and presentation logic in the same place.
Edit Also, perhaps duplicate of "if" considered harmful in ASP.NET MVC View (.aspx) files?.
Why are they bad?
As many people have already suggested it's because it can be a warning sign that business logic is leaking into your views which is a bad thing. Or that you are trying to do too much in your view. Often it's easier to setup the necessary data in your controller and just pass everything required (including any computed values) and have your view render it straight out. The flatter and simpler your view model is the better.
I don't think there is anything wrong with using if/else's for the true purpose of conditionally rendering UI. For example, if a user is logged in render this partial if they aren't than render something else.
I think a lot people get taken back and draw a hard and fast 'it's bad practice' because it can easily lead to tag soup. This phenomena has already been highlighted as a carry over from the asp.net webforms engine. Whilst still a good thing to be aware of, it is likely less of an issue with the razor engine as it's much cleaner and requires less syntax to achieve the same thing. The automatic switching in and out of C#/VB code to html and back is very simple with only an # required to re-enter a code block. I don't think this will create tag soup in the same way that angled brackets did <% %>. The amount of noise in the latter was what most people objected to (and rightly so).
Lastly, circumstances depending, if you find you are starting to get a lot of if/else logic in your view sometimes it is cleaner to put that into a HTML helper. I can't find the reference but I remember Rob Connery once saying that if you find yourself starting to write if statements in your view than it's a candidate for a html helper.
Is there performance of security concerns?
I don't think there is going to be any noticable performance differences or security concerns with what you are asking. But considering an authentication check like that is likely to be used in multiple views I would put it into a Html helper.
If-else blocks are not bad specifically. Code in views is bad because you are mixing HTML markup with code. ASP.NET MVC is bad for separating static page design from dynamic UI code. Web Forms is better at separating static and programmatic UI elements.

Rails- Using a set of functions across the view , controller and model

i have a function that converts an array to a hash which i would like to use across all the model, controller and view files in the rails app.
Does this violate some core design principle, or am i missing something really obvious?
UPDATE: This is actually a software engineering question. I want to understand why some "convenient" things are not allowed in rails, and i suspect it is precisely because they do not want us to do it
This is likely actually a bad practice. It'd likely be better to instead always work with arrays and hashes in your controllers and models and if necessary convert them in the view to the alternative.
That is, if the data is natively represented as a array throughout your application work with it that way and if required to be a hash in the view either convert it first and assign it or convert it in the view using a helper.
View global helpers go in: helpers/application_helper.rb
If you must call a helper from a controller you can still define it there and I believe you can do:
def Something
....
hashData = #template.helper(arrayData)
end
Calling helpers in a model is REALLY not a good idea, there's no point.
As a final note, encapsulating this logic in a library would likely be ideal, your controllers can call a library & your view helpers can as well.
I think you are: views should not need that method. The controller ought to do it and pass it along to the view for display. The controller or, better yet, service layer might apply that method to a model object, but there's little reason for a model object to know about it.

Resources