- What about methods on models that change themselves in some way?
<%= @post.last_viewed_at %>
Suppose the last_viewed_at method returned a previously stored time,
then updated the model to store a new current time. Maybe a bad example,
but I hope you get my meaning.
Don’t worry about it. What the method does
itself shouldn’t be your concern, in the view. You want your code to be
orthogonal, it shouldn’t matter how the variable returns returns the
date, or what it does when you request it, that is the model’s
prerogative. Trying to keep track all over your application of what
your model methods are doing requires you to know and consider their
internal plumbing, this couples your code that you write to the model’s
implementation. Something that won’t bite you on a small app, but will
likely turn into a nightmare on a large app.
If your view has
some object that the controller gave you, just consider it as an object
you can access in whatever way that is necessary to perform the
responsibilities of the view. Saving data is not a responsibility of
the view, so that should not happen there. Displaying the date it was
modified may possibly be a responsibility of the view, so you can
display that. If the model decides that it needs to do something every
time someone asks when it was saved, your view shouldn’t know or care
or change it’s behaviour accordingly.
Really, your view shouldn’t even know it is an ActiveRecord object, it
should just be some object that has the information necessary to get
things done. Then you can swap it out with other variables later, maybe
a struct or an object pulled from a yaml file, or whatever.
- What about aggregating class methods like count, sum or avg?
<%= Person.count %>
Obviously a class methods and does touch the database. I assume it would
be better to let the controller deal with stuff like this.
Controller
@person_count = Person.count
View
<%= @person_count %>
Thoughts anyone?
A variable is better here, because your view shouldn’t know how to
tabulate the size, that is business logic. What happens if you later
add another another type of user, and it should treat them as the same?
What happens if different controllers want to render that same view to
show their data? Your view knows too much about the data it is serving,
it works for Person.all, but what if you add another type of person
with different attributes, stored in the model OtherPerson ? Then you
cant use that view (or you will have to change it, and change the
controller for it).
To keep your code robust, keep your views stupid. To keep them stupid,
make sure they are agnostic towards the implementation of displaying
the data. Let the controller worry about that, that is why the
controller exists.
http://www.youtube.com/watch?v=ku3QkWcPSEw
It’s dirty, horrible, bad form, breaks the separation of layers…
I don’t know what you mean by dirty, it saves several lines of code
and when looking at the view code it is easier to see what is
happening than to see a variable that has to be hunted for in a filter
somewhere to find out what it is.
It saves 4 lines of code, but breaks one of the principles of MVC layered separation. I’d say the 4 lines is worth it for keeping the application clean.
But think how many lines of code you are going to have to go edit when you realize that you need to change it.
Also, I wouldn’t consider Person.all to be more clean than @people. What if you need to exclude some? Person.all :conditions => {whatever}, if you are just using a before filter, it is easy to override, you can override it for any given controller, and for any given controller method. If it’s hard coded into the view, then that view has to serve everybody’s wishes, it ends up having to know how it is to be used, and having lots of brittle conditional code for each of these situations.
This is why the controller must be responsible for supplying the appropriate data to the view, not the view being responsible for creating it’s own data.
It might start as innocently as Person.all, it can easily turn into
if this
Person.all
elsif that
OtherPerson.all
else
Person.all + OtherPerson.all
end