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.
Related
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
I'm relatively new to rails, but I've made a few basic CRUD apps. However, this time, I'm making something different, and I'm not sure how to organize it.
It's essentially a one page app. The user fills out a form, and the code does some calculations based on those values. I have all the code written, but it's all just sitting in the controller. I'm assuming that this isn't correct.
There are two parts:
Using an external API, 2 constant arrays are generated. I need these variables to be global, or at least accessible for the calculator function.
I have a function that takes some inputs from the form that also calls other functions. A simplified version is below. I could put all the code into one function if that's necessary. I have them separate just so that the code is more readable.
def calc(input)
func1(input)
func2(input)
# do more stuff
return answer #I need to show this in the view
end
def func1(a)
end
def func2(b)
end
So, where should I put each part of this code?
To make your controllers thin, you can keep business logic at Service Objects.
Just create "services" directory at "app", add there some class like "user_searcher.rb".
And call it in the controller, passing all necessary data.
Such technique will help you to isolate business logic and incapsulate it in separate class.
BTW read this article http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
I think, from what I understand of you question, is this code should be placed in the helper classes. If you have dedicated class for this calculation, you can use class attributes to access array to access anywhere in the class or declare them constant, in case they are constant.
I don't think making global is a good practice, just because this is needed in some other function, instead return that variable and pass them as parameter where they are needed.
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?
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.
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.