The final lesson (Lesson 4) of Course 2 involved adding some more functionality to the PostIt app (the Reddit clone).
1. 'AJAX'ifying the voting
1 2 3
remote: true switch dictates AJAX implementation of that particular link.
Another thing to note is that the
respond_to do |format|
flash[:notice] = 'Your vote was cast.'
flash[:error] = 'You can vote only once on this post.'
format.js # by default renders the [action_name].js.erb template in the views/[controller_name] folder
The code in the [action_name].js.erb could be something as simple as:
$('#post_<%= @post.id %>_votebox').html("<%= @post.total_votes %>");
or a bit more complicated as in this file
2. 'Slug'ifying the routes
There is a security concern with URLs that look like 'ppj-postit.heroku.com/users/3' with the '3' representing the ID of the User object. Same is the case with Posts and Categories. A more appropriate URL for the above would be 'ppj-postit.heroku.com/users/testman' where 'testman' is automatically generated from the
username attribute of a user. This is what is called a 'slug' in web development jargon. To achieve this, the
to_param() method of the model object has to be over-ridden, because this is the method used by the named-route methods like
user_path, et al.
First a 'slug' column needs to be added to the models (tables) to store the respective slugs. The models also need to have a way to automatically generate and save the slug. The former can be done by simple column creating migration. The generation of the slug can also be done in the same migration (for existing records). The slug-generator will take the appropriate attribute of the model, the
:username attribute in case of the Users table.
generate_slug method for the model will be useful for auto-generation of slugs. This method can then be called to create a slug every time a new User object is created using the
before_save ActiveRecord callback.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
1 2 3 4
Note: The implementation of
generate_slug shown above is very simplistic. A robust implementation should ensure that the slugs are never repeated for the same model. This is especially applicable for the models where slugs will be generated from a non-unique attribute (e.g. Posts.title). The code below shows exactly that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
3. Simple Role Implementation
- add a 'role' columns to the Users table using a simple migration
- add methods to the model to check whether a particular user has a specific role (indicated by a string in the role column)
1 2 3
def admin? self.role == 'admin' end
- enable/disable features in the controllers…or in the view templates…
1 2 3
def require_admin access_denied "You need admin access to do that!" unless logged_in? and current_user.admin? endbased on the role checks
1 2 3
<% if logged_in? and current_user.admin? %> <li><%= link_to "Create New Category", new_category_path %></li> <% end %>
A more serious implementation would call for defining a Role model and a Permission model. A has-many through associations will define Users can have many Roles
through: :permissions. The role checks can now be done through this association. Chris Lee, the instructor of the 2nd course suggests that this should be avoided unless absolutely necessary, because this rolls into a massive net of checks and nested models. The simple implementation shown above suffices our need.
4. Time-zone Setting
The app has a default time-zone (UTC) which can be overridden by specifying a different one in the './config/application.rb' file.
config.time_zone = 'Melbourne' to this file will set the default time-zone of the application to Melbourne time overriding the default (UTC)
Note: All available time-zones can be listed by running the rake task
Now that the application has a default time-zone, each user should be able to set his/her own. That would need a dedicated column to save that user's 'timezone' in. This can be added by a simple migration:
rails generate migration add_timezone_to_users
- Add time-zone column to the User table:
1 2 3 4 5
class AddTimezoneToUsers < ActiveRecord::Migration def change add_column :users, :timezone, :string end end
Time to modify the view template to allow setting of the time-zone: the common form partial used for User registration (User#new) and profile-update (User#edit). The following addition to the existing form partial will address this need:
<%= f.label :timezone %>
<%= f.time_zone_select :timezone, nil, default: Time.zone.name %>
The strong parameters need to include the new attribute
:timezone for the above to work through the web interface.
params.require(:user).permit(:username, :password, :timezone)
For time to be displayed in the logged in user's time-zone, the time-display helper now needs to be modified:
1 2 3 4 5 6 7
Refer to this commit to the PostIt app for details of code changes.