Ruby: Variables scope questions - ruby-on-rails

Hey Rails newbie here.
I used to have a lot of stuff going on in one of my controllers. Someone told me that its good practice to have "fat models and thin controllers" So I was moving some things over to the model.
In my controller's show method I used to have some # variables that I would use in my view. Now I have those variables in a method in my model. Will I still be able to access those in my view? If so do I have to make any adjustments?
Thanks

You will have to create an instance of your model in the controller as an # variable. You can then call the methods from that inside the view.
e.g. imagine you used to have some long bunch of logic in your controller which calculated a credit score for a customer culminating in
#credit_score = credit_score
and you've now moved this into a credit_score method on the Customer model.
You now just need
#customer = Customer.find...
in the controller
and you can the use <%= #customer.credit_score %> within the view.
This is what people mean by fat models and thin controllers. If you'd like some more advice then it's best to update the question with some specifics from your app.

The common practice is to define the variables of this kind in controllers:
#object = Model.new
to later use it in form_for or something like that. Some people use Model.new directly in views instead. That's somewhat unusual but still makes sense, especially knowing that Rails just loops through all of the instance variables in your controller every time to make them available in your views

Related

Can two or more inter-related models have one common controller in rails

am a newbie in rails and I want your help. I want to build a simple project in rails that includes more than one model that are inter-related. So in this case, is it possible to create only one controller that corresponds to all the models or should I create a controller for each model? And please I want reasons for your answers. Thank you.
Of course..
You could easily have something like:
#animals_controller.rb
def animals
#dogs = Dog.all
#cats = Cat.all
#cheetahs = Cheetah.all
end
BUT...
Imagine having an update, edit, new, show, create and destroy for each of these models in the same controller!!
That would be a mess - and you would end up with a complex file that would be (mildly said) annoying to maintain.
Therefore, for your own sake, and because it is good practice, you normally create a corresponding controller for each of your models(animals): model: dog.rb, controller: dogs_controller.rb, model: cat.rb controller: cats_controller.rb etc...
so you, after a few months abroad, can come back and continue your work without thinking What the ....
I would recommend looking into the MVC (for example on youtube) and really understanding how it connects the database with your view and so on.
Understand the data-flow and you will find yourself with a powerfull toll later on when you're debugging, construction forms, creating ajax-calls, custom attributes and so on.
ous,
You need to create a controller for each model in order to have CRUD options. It depends on what the models will do if you don't want a controller, for example if model just does some calculations and you don't really need to pass data to your view then you might not need it.

Is there anything bad about decorating objects (using Draper) at the start of a view?

Draper recommends decorating objects at the end of a controller method or alternatively using their decorates_associated method to automatically decorate them.
I like the idea of the view explicitly declaring what it expects to receive (granted the rest of Rails doesn't work like this but it still feels nice). I'd therefore prefer to decorate objects at the top of the view rather than at the end of the controller:
So in users#show I would like to do:
- #user = #user.decorate
%h1= #user.full_name
%p= #user.description
Instead of decorating in the users controller like this
class UsersController < ApplicationController
...
def show
#user = User.find(params[:id]
#user = #user.decorate
end
end
I suspect there's some caching issue with doing things this way round but it feels nice. What if anything am I missing? Is what I'm proposing bad practice?
I think this is a very good question, because draper confuses the traditional MVC logic a bit. As you can read in the draper README which you have linked, draper aims to replace cluttered helper definitions with a more structured approach.
However, to which part of M-V-C does this belong to? Draper claims to decorate objects, which can be associated to the representation part of the software architecture and therefore neither belongs to the Model nor does it really fit into the Controller.
Following #Damien Roche's comment, it also does not really fit into a View, because one usually does not expect a template file to perform any more actions on an object than displaying its properties.You also do not define your helpers in the template, right?
To my mind, draper can be seen more like an intermediary that extends the objects a controller has selected for displaying before they reach the view. Following that logic I prefer the usage of the decorate_assigned command, because that places the decorations somehow between Controller and View.
Additional remark: I know what you mean with 'the view declaring what it wants to receive' but this stands in contrast to the fact, that you usually define one decoration per model for the whole application. So there is not so much room for 'special requests'

What are best ways to consolidate instance variables (to pass data from controller to the view) in Rails?

I have a details page in Rails 3 app that has several instance variable. For example, my view has the following instances:
#product
#program
#action
...etc...
Some of them are single strings, some are arrays (result of requests from external APIs). Overall, the data comes from at least 3 models.
My question is: What is the best way to consolidate instance variables so my view and my controller look cleaner (without sacrificing readability)? I understand the question is open-ended and everyone has their own preferences, but what are some of the options? I am thinking along the lines of combining the instance variables into an object so the previous code becomes something like this:
Details.product
Details.program
Details.action
Thanks a lot!
UPDATE: Going the object route, is there any good examples (github, blog posts) of actually implementing it?
In my opinion the best way to clean up your controller actions is to assign as few instance variables as possible there. In practice, I try to only assign instance variables in the action/before_filters that are directly derived from the parameters passed to it. Also, in general I try to push as many instance variable assignments to before_filters as I can so as to keep the actions slim.
But the best way to cut down on instance variable assignment in the actions is to use view helpers. For instance if you are currently assigning an instance variable in an action which you use to output some div with, just use a view helper to do the output directly without any need to pass an object to the view.
Here's an example of a before_filter on a controller:
before_filter :assign_variables, :only => :show
def show; end
private
def assign_variables
#product = Product.find(params[:product_id])
#program = Program.find(params[:program_id])
#action = Action.find(params[:action_id])
end
I would suggest not using instance variables at all and go with the decent exposure gem instead.
https://github.com/voxdolo/decent_exposure
http://railscasts.com/episodes/259-decent-exposure
Whenever you find yourself with several instance variables in a controller action, it best to use the presenter pattern.
This video explains everything in details
"Ruby on Rails - Presenters & Decorators"

Using model directly in the view in Rails

I have a model named "Category". It is just a list of descriptions stored in the database. Now I want the category descriptions to appear in a drop down list.
Would the correct thing be to make an instance variable in the action where I say something like #categories = Category.all or do you use Category.all directly in the view?
What would be the shortcomings/advise against using the model directly in the view?
If Category.all is being called in the view only once, it's OK to write it directly. Else, it's better to write a helper rather than creating instance variables as per Rails convention. Something like
def all_categories
#all_categories ||= Category.all
end
It does a single query, if being used multiple times in the same view as well.
A common practice is to create the instance variable in the controller for the data which is specific to that request.
For you Category example, doing the query directly from the view is appropriate.
Could be in the view for that action, a partial, or even in the layout if it's used throughout the site.
A simple rule of thumb is to only save method results in instance variables if it saves you some code duplication because you want to use the result twice. Another rule of thumb is to never call methods that have side effects in your views. In this case it seems you are using a method without side effects once, so I would be okay with putting it in a view.
That said it is a lot easier to lose sight of your model method calls in your views than in your controller since they are mixed with the markup elements. That might cause you to overlook that you have called Category.all before when adding some new code in your view that also calls Category.all. Instead of easily noticing in your controller that you have an instance variable for Category.all, you are stuck with either going through your whole view or forgetting to do so.
Another case to make for using instance variables is that it can make your views easier to reuse. Instead of tightly coupling your view to the Category class, you might want to make the calls that are done on the instance variable generic enough to also be applicable to other models that can be viewed in a similar way. Using instance variables this way can help you take advantage of the duck typing that Ruby offers, but you have to weigh the ease of making things generic against the effort of remembering or looking up what kind of methods are actually available on the different kinds of objects that could be inside your instance variable.

Rails Model, View, Controller, and Helper: what goes where?

In Ruby on Rails Development (or MVC in general), what quick rule should I follow as to where to put logic.
Please answer in the affirmative - With Do put this here, rather than Don't put that there.
MVC
Controller: Put code here that has to do with working out what a user wants, and deciding what to give them, working out whether they are logged in, whether they should see certain data, etc. In the end, the controller looks at requests and works out what data (Models) to show and what Views to render. If you are in doubt about whether code should go in the controller, then it probably shouldn't. Keep your controllers skinny.
View: The view should only contain the minimum code to display your data (Model), it shouldn't do lots of processing or calculating, it should be displaying data calculated (or summarized) by the Model, or generated from the Controller. If your View really needs to do processing that can't be done by the Model or Controller, put the code in a Helper. Lots of Ruby code in a View makes the pages markup hard to read.
Model: Your model should be where all your code that relates to your data (the entities that make up your site e.g. Users, Post, Accounts, Friends etc.) lives. If code needs to save, update or summarise data related to your entities, put it here. It will be re-usable across your Views and Controllers.
To add to pauliephonic's answer:
Helper: functions to make creating the view easier. For example, if you're always iterating over a list of widgets to display their price, put it into a helper (along with a partial for the actual display). Or if you have a piece of RJS that you don't want cluttering up the view, put it into a helper.
The MVC pattern is really only concerned with UI and nothing else. You shouldn't put any complex business logic in the controller as it controls the view but not the logic. The Controller should concern itself with selecting the proper view and delegate more complex stuff to the domain model (Model) or the business layer.
Domain Driven Design has a concept of Services which is a place you stick logic which needs to orchestrate a number of various types of objects which generally means logic which doesn't naturally belong on a Model class.
I generally think of the Service layer as the API of my applications. My Services layers usually map pretty closely to the requirements of the application I'm creating thus the Service layer acts as a simplification of the more complex interactions found in the lower levels of my app, i.e. you could accomplish the same goal bypassing the Service layers but you'd have to pull a lot more levers to make it work.
Note that I'm not talking about Rails here I'm talking about a general architectural style which addresses your particular problem.
Perfect explanations here already, one very simple sentence as conclusion and easy to remember:
We need SMART Models, THIN Controllers, and DUMB Views.
http://c2.com/cgi/wiki?ModelViewController
The Rails way is to have skinny controllers and fat models.
Do put stuff related to authorization/access control in the controller.
Models are all about your data. Validation, Relationships, CRUD, Business Logic
Views are about showing your data. Display and getting input only.
Controllers are about controlling what data goes from your model to your view (and which view) and from your view to your model. Controllers can also exist without models.
I like to think of the controller as a security guard/receptionist who directs you the customer(request) to the appropriate counter where you ask a teller (view) a question. The teller (view) then goes and gets the answer from a manager (model), who you never see. You the request then go back to the security guard/receptionist (controller) and wait until you are directed to go another teller (view) who tells you the answer the manager (model) told them in response to the other teller's (view) question.
Likewise if you want to tell the teller (view) something then largely the same thing happens except the second teller will tell you whether the manager accepted your information. It is also possible that the security guard/receptionist (controller) may have told you to take a hike since you were not authorized to tell the manager that information.
So to extend the metaphor, in my stereotyped and unrealistic world, tellers (views) are pretty but empty-headed and often believe anything you tell them, security guard/receptionists are minimally polite but are not very knowledgeable but they know where people should and shouldn't go and managers are really ugly and mean but know everything and can tell what is true and what isn't.
One thing that helps separate properly is avoiding the "pass local variables from controller to view" anti-pattern. Instead of this:
# app/controllers/foos_controller.rb:
class FoosController < ApplicationController
def show
#foo = Foo.find(...)
end
end
#app/views/foos/show.html.erb:
...
<%= #foo.bar %>
...
Try moving it to a getter that is available as a helper method:
# app/controllers/foos_controller.rb:
class FoosController < ApplicationController
helper_method :foo
def show
end
protected
def foo
#foo ||= Foo.find(...)
end
end
#app/views/foos/show.html.erb:
...
<%= foo.bar %>
...
This makes it easier to modify what gets put in "#foo" and how it is used. It increases separation between controller and view without making them any more complicated.
Well, it sort of depends upon what the logic has to deal with...
Often, it makes sense to push more things into your models, leaving controllers small. This ensures that this logic can easily be used from anywhere you need to access the data that your model represents. Views should contain almost no logic. So really, in general, you should strive to make it so that you Don't Repeat Yourself.
Also, a quick bit of google reveals a few more concrete examples of what goes where.
Model: validation requirements, data relationships, create methods, update methods, destroy methods, find methods (note that you should have not only the generic versions of these methods, but if there is something you are doing a lot, like finding people with red hair by last name, then you should extract that logic so that all you have to do is call the find_redH_by_name("smith") or something like that)
View: This should be all about formatting of data, not the processing of data.
Controller: This is where data processing goes. From the internet: "The controller’s purpose is to respond to the action requested by the user, take any parameters the user has set, process the data, interact with the model, and then pass the requested data, in final form, off to the view."
Hope that helps.
In simple terms, generally,
Models will have all the codes related to table(s), their simple or complex relationships (think them as sql queries involving multiple tables), manipulation of the data/variables to arrive at a result using the business logic.
Controllers will have code/pointers towards the relevant models for the job requested.
Views will accept the user input/interaction and display the resultant response.
Any major deviation from these will put unwanted strain on that part and the overall application performance may get impacted.
Testing, Testing ...
Put as much logic as possible in the model and then you will be able to test it properly. Unit tests test the data and the way it is formed by testing the model, and functional tests test the way it is routed or controlled by testing the controllers, so it follows that you can't test the integrity of the data unless it is in the model.
j

Resources