Using model directly in the view in Rails - ruby-on-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.

Related

With Rails 3 view inheritance, is it possible to call "super" within a child view, but send the parent variables?

Every show action in my app has the same template for every model (it is an Admin-portal type interface). I have a view show.html.erb that all of these models inherit.
The controllers all use InheritedResources, so I have access to some helper variables such as resource, which I use to display the attributes for the model being shown through resource.attributes in my parent view.
What I would like to be able to do is whitelist which attributes are being shown in each model. I have thought of two ways to do this... the first is to define some method in a class my model's inherit from, say it is called attr_visible and define each attribute in the model. The reason I don't like this approach is that it puts too much of the view logic in the models. I would rather provide my views with the model, and let the view determine what is displayed.
The second approach is what this question is about. I want to have a file /app/views/users/show.html.erb, and set which attributes for a User I would like to be shown, say something like:
#attributes = [:name, :email, :etc]
And then pass them to the parent view which I described in the beginning of this question. That view would then be able to loop through #attributes and display the attributes necessary.
Is it possible to use a "super"-type method from views? Everything I have read about view inheritance suggests the view is loaded based on where it is located in the directory structure, and then after that you're out of luck.
edit: I have sort of done what I want. I defined a partial _show.html.erb in the /app/views/users directory that looks like:
<% #attributes = ['name', ...] %>
And then in the parent view I call <%= render "show" %> before looping through #attributes. This works, but feels clunky since I am not actually rendering anything when I call render. Is there a better way?
You can use a presenter for this. Adding "view" logic to a model is better done through this type of object. Take a look at draper.
Once the resource is decorated, you can just call visible_attributes or whatever. Of course, you would need to implement that method for each resource.
Views inheritance works when looking for a view, not when rendering it. Take a look at the original plugin implementation.
Other way would be to make the show parent view to call render on resource and create several partials for the classes.
For a better understanding on Rails Views, I recommend The Rails View: Create a Beautiful and Maintainable User Experience

Ruby: Variables scope questions

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

How do I create the Controller for a Single Table Inheritance in Rails?

I am setting up the Single Table Inheritance, using ContactEvent as the Model that ContactEmail, ContactLetter, and ContactCall will all inherit.
But I'm stumped on how to create the routing and the controller.
For example, let's say I want to create a new ContactEvent with type Email.
I would like a way to do the following:
new_contact_event_path(contact, email)
This would take the instance from Contact model and from Email model.
Inside, I would imagine the contact_event_controller would need to know...
#contact_event.type = (params[:email]) # get the type based on what was passed in?
#contact_event.event_id = (params[:email]) #get the id for the correct class, in this case Email.id
Just not sure how this works....
I had similar problem.
See here how I solved it.
I would have a controller (and maybe views) for each of your resource types. So add a controller for ContactEmail one for ContactLetter etc. Don't bother with one for the base class ContactEvent. Then your paths would read something like:
new_contact_email_path(#contact) or new_contact_letter_path(#contact)
The controller actions would then use the right model for that they represent, ie:
#contact_email = ContactEmail.new(params[...])
If you can keep your three types of resources separate rather than trying to pass the type in and building the right object in one controller you should find life much easier. The downside is you may need more links/forms/views in the front end, however depending on your application that may not be a bad thing from the user's perspective.

Multiple dynamic method calls or declaring a variable in Grails

In my view, if I have a situation where I need to use a dynamic method (such as Domain.findByName("name")) in multiple places, would it be better to define a variable with and refer to that, rather than have the dynamic method in multiple places? I know this seems like an obvious answer, but I just wanted to make sure Grails doesn't cache it or something, and indeed two DB calls are being made.
By default, grails will only cache "get" requests (i.e. Book.get(4)), if you don't set up any additional caching, you'll hit the database for each request (as you're seeing).
See the 'caching queries' section of the grails documentation for more detail.
If you only want the call to be made once (which makes sense in a view since you'd want it to be consistent), I'd either do the query in the action and pass it in the model, or else you could also use the g:set in your view to set it (though this sounds like it's more appropriate for a controller or service).
It would be better to send the domain object to the view as part of the model, rather than calling Domain.findByName("name") from your view.
So in your controller method you'd want
def myAction = {
def myObject = Domain.findByName("name")
// do other stuff
[myObject: myObject]
}
then in your view you can access it by
${myObject.property}

Rails STI Controllers

I have a Rails site using STI with the following classes:
Pages
Homepage < Pages
LandingPage < Pages
On the front-end all requests get handled by the Pages controller. However, if the object detected is actually an instance of LandingPage, i'd like to have the action on a LandingPages controller get called. (for example, the show method in the child controller classes will pull in some specific lookups that aren't always relevant).
Any suggestions on how to best accomplish this?
Thanks
This sounds a bit like you are clouding the MVC distinction, but it should be doable.
I'd add a series of tests on the Pages model (e.g. supports_buzzbar_foo? or wiggums_itemization_controller, then override them as appropriate in the subclasses) and use these in the view to conditionally generate the appropriate links to the controller methods you want.
That way you're keeping each part (roughly) doing it's job.
Markus' solution should work. You could also keep your links in the views pointed to Pages, evaluate the incoming object and then redirect_to the appropriate controller based on the object class.
However, unless you're performing completely different actions with each type of object, then you'll wind up with duplicate code in your controllers. So you might be better off sticking with the Pages controller and just adding some methods that handle the extra lookups that are needed for that object.

Resources