Here's the third quiz for people who are just getting started learning web-application development. This revisits the basics of MVC, implementing basic user-authentication and polymorphic associations.
What's the difference between rendering and redirecting? What's the impact with regards to instance variables, view templates?
If I need to display a message on the view template, and I'm redirecting, what's the easiest way to accomplish this?
If I need to display a message on the view template, and I'm rendering, what's the easiest way to accomplish this?
What should we do if we have a method that is used in both controllers and views?
If we want to prevent unauthenticated users from creating a new comment on a post, what should we do?
Suppose we have the following table for tracking "likes" in our application. How can we make this table polymorphic? Note that the "user_id" foreign key is tracking who created the like.
id user_id photo_id video_id post_id 1 4 12 2 7 3 3 2 6 How do we set up polymorphic associations at the model layer? Give example for the polymorphic model (eg, Vote) as well as an example parent model (the model on the 1 side, eg, Post).
MY ANSWERS:
A1: Rendering a view template happens as (the final) part of a single HTTP request, whereas redirecting means end of a request and initiation a whole new request. And because of that, redirecting would not enable use of instance variables in the view templates unlike direct rendering.
Back
A2: Using a hash referenced in the layout would be the easiest way to display messages if redirecting. For example:
flash[:notice] = 'You have successfully updated the profile'
flash[:error] = 'The login credentials you entered are incorrect'
Back
A3: For generic messages, using the same hash as described in A2 above would work. If error messages on a model-object stored in an instance variable need to be displayed, that instance variable can be used directly in the view template using something like
1
2
3
4
5
6
7
8
9
10
11
12
<% if model_obj.errors.any? %>
<div class='row'>
<div class='alert alert-error span8'>
<h5>Please fix the errors below to submit successfully:</h5>
<ol>
<% model_obj.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ol>
</div>
</div>
<% end %>
A4: Passwords should definitely never be stored directly as strings in the database. They should be auto converted to complex strings (obfuscated) using a dedicated gem (like bcrypt-ruby). Such a gem can help obfuscate the user-entered password using the same mechanism to obfuscate the originally set password. Thus the comparison between the user entered and originally set password can happen indirectly through the obfuscated strings. This obfuscation is one-way, that means the stored string cannot be converted back to the actual password string.
In case of the bcrypt-ruby gem, the database table for the model needs to have a column named 'password_digest' that can hold a string. The model itself needs to have the has_secure_password
validation declared in its class-definition. bcrypt-ruby gives a method named authenticate
for the model-object which can be used to match the user-entered password to the stored one.
Back
A5: If a helper method defined in the ApplicationController class (which each of the controllers inherit from) needs to be used by both the views, and the controllers, the helper_method
needs to be called in the ApplicationController class definition with the helper-method names passed to it as arguments (as :symbol
s). So, a line like the one below in the ApplicationController class definition
helper_method :current_user, :logged_in?
will allow the methods current_user
and logged_in?
to be used in both views and controllers. Note that a call to helper_method
is not necessary if these only need to be used in the controllers.
Back
A6: 'Memoization' (a play on the word 'memoir') is an optimization technique used to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same operation is requested repeatedly.
It can be implemented in Ruby using the operator ||=
to assign a value to an instance variable. This can be used effectively to store results from a repeated database query (which can be quite performance intensive). The statement below is 'memoization' in action:
@current_user ||= User.find(session[:user_id]) if session[:user_id]
If the current_user
instance variable is already populated (say, from a previous call to this statement), the cached result will be returned and evaluation of the right side of the above expression will be avoided.
Back
A7: The rendering and submission of new comment form (Comments#new and Comments#create actions) should be allowed only if a user is logged in. This can be achieved by a common helper method that checks whether a valid user is logged in. This method will be defined in the parent controller class ApplicationController. This method can be called in the Comments controller class using the before_action
keyword which will trigger checking of a valid user before executing certain actions.
before_action :logged_in?, only: [:new, :create]
Back
id | user_id | likeable_type | likeable_id |
---|---|---|---|
1 | 4 | Video | 12 |
2 | 7 | Post | 3 |
3 | 2 | Photo | 6 |
Back
1 2 3 4 |
|
1 2 3 4 5 6 |
|
1 2 3 4 |
|
1 2 3 4 |
|
1 2 3 4 |
|
A10: Entity Relationship Diagrams show individual ActiveRecord models and their association with each other. Individual models are represented as blocks and also show the columns that model's database table needs to have. These are really handy when we are setting up the database models and associations between them.
See the ERD below for the PostIt application (Courtesy - Tealeaf Academy):
Back