Tying Up the Loose Ends
When we developed the User management, the Tale management was affected as the
tales table has a many-to-one relationship with the users table. Now we can solve the
problem created by the foreign key reference. First, open the user.rb file and add
the following statement indicating that it is at the ‘one’ end of the relationship:
has_many :tale
After addition of the statement, the class will look like the following:
class User < ActiveRecord::Base
validates_presence_of :user_name, :password, :first_name,
:last_name, :age, :email, :country
validates_uniqueness_of :user_name
validates_numericality_of :age
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-
z0-9]+\.)+[a-z]{2,})\Z/i
belongs_to :role
has_many :tale
def check_login
User.login(self.name, self.password)
end
def self.login(name,password)
find(:first,:conditions => ["user_name = ? and password
=?",name, password])
end
end
Next, add the following statement to the tale.rb file:
belongs_to :user
The file will look like as follows:
class Tale < ActiveRecord::Base
validates_presence_of :title, :body_text, :source
belongs_to:genre
belongs_to :user
end
Next, open the tale_controller.rb file. In the create method, we need to add the
user’s id to the tale’s user id reference so that the referential integrity can be satisfied.
For that, we will get the current user’s id from the session and set it as the value of
the user_id attribute of the tale object. The create method will look like as follows,
after doing the changes:
def create
@tale = Tale.new(params[:tale])
@tale.genre_id=params[:genre]
@tale.user_id=session[:user_id]
@tale.status="new"
if @tale.save
flash[:notice] = 'Tale was successfully created.'
redirect_to :action => 'list'
else
render :action => 'new'
end
end
That’s it. The ‘loose ends’ related to the User management are tied up. Now let us
move onto the Comment Management module.
Developing the Comment Management Module
From the description of functionalities, we know that the module needs to support
only three operations—add, view, and delete. The steps for developing the module
are almost the same:
- Generating the Scaffold
- Modifying the Model
- Refining the View
- Customizing the Controller
We have changed the order of refining the view and customizing the Controller
steps. That’s what I meant by ‘almost the same’. Let’s get into the development.
Generating the Scaffold
Open the RoR prompt using use_ruby command, and enter the following command:
C:\InstantRails\rails_apps\talewiki>ruby script/generate scaffold
Comment comment list show new create destroy
You will get the following screen:
If the scaffold command is reused, then it will not rewrite the existing files
unless you specify the -force parameter. We need only new, list, and delete
functionalities. So, we have specified the actions that we need—list, show for listing
of comments, new and create for adding, and delete for deleting. However, it will
still create the stubs and links that need to be tackled at the View level. First, let us do
the required modifications at the Model level.
Modifying the Model
First, we have to tell RoR which fields should not be empty. For that, add the
validates_presence_of method with :comment_body as the argument in the
comment.rb file. After addition, the code shall be as follows:
class Comment < ActiveRecord::Base
validates_presence_of :comment_body
end
Next, we have to tell that the comments table is at the 'many' end of the relationship
with both tales and users table. For that, add a belongs_to declaration to the
comment.rb file.
class Comment < ActiveRecord::Base
validates_presence_of :comment_body
belongs_to :tale
belongs_to :user
end
The next step is to tell both the users and the tales table that they are at the 'one'
end of the relationship. For that, open the user.rb and tale.rb files, and add the
has_many declaration. After the additions, the code will be as follows for user.rb:
class User < ActiveRecord::Base
validates_presence_of :user_name, :password, :first_name,
:last_name, :age, :email, :country
validates_uniqueness_of :user_name
validates_numericality_of :age
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-az0-
9]+\.)+[a-z]{2,})\Z/i
belongs_to :role
has_many :tale
has_many :comment
def check_login
User.login(self.name, self.password)
end
def self.login(name,password)
find(:first,:conditions => ["user_name = ? and password
=?",name, password])
end
end
For tale.rb, here is the code:
class Tale < ActiveRecord::Base
validates_presence_of :title, :body_text, :source
belongs_to:genre
belongs_to :user
has_many :comment
end
That completes the changes to be done at the Model level. Next, let us refine
the View.
Refining the View
Comments will be given for a story. That means the page displaying a tale will have
a link to add comments. This also means that the Comment management module
is not a 'standalone' module like others, as it will not have its own menu when we
decide upon the template. Now coming back to links to the comments in the
tale display page, for what functionalities do we need the links? The answer is
two—adding a comment and listing the comment. The add comment link will lead
to the 'New Comment' page, and the view comments link will lead to the list view of
the comments. Now let us see what are the problems—each comment needs a user id
and the id of the tale for which the comment is being added. The listing of comments
needs only the id of the tale. As user id is available from the session, we have to add
only the tale id as a part of the link. That is what we are going to do.
Open the show.rhtml file from the app/views/tale directory. It contents
are as follows
<% for column in Tale.content_columns %>
<p>
<b><%= column.human_name %>:</b> <%=h @tale.send(column.name) %>
</p>
<% end %>
<%= link_to 'Edit', :action => 'edit', :id => @tale %>
<%= link_to 'Back', :action => 'list' %>
Now let us add two more links—one for adding a comment and another for listing
the comments:
<% for column in Tale.content_columns %>
<p>
<b><%= column.human_name %>:</b> <%=h @tale.send(column.name) %>
</p>
<% end %>
<%= link_to 'Edit', :action => 'edit', :id => @tale %>
<%= link_to 'Back', :action => 'list' %>
<%= link_to 'Add Comment',:controller=>'comment', :action => 'new', :
id => @tale.id %>
<%= link_to 'View Comments',:controller=>'comment', :action => 'list',
:id => @tale.id %>
The next change we have to do is remove the edit option from the viewing part
of the comments. So open the list.rhtml file from the app/views/comments. The
code will be as follows:
<h1>Listing comments</h1>
<table>
<tr>
<% for column in Comment.content_columns %>
<th><%= column.human_name %></th>
<% end %>
</tr>
<% for comment in @comments %>
<tr>
<% for column in Comment.content_columns %>
<td><%=h comment.send(column.name) %></td>
<% end %>
<td><%= link_to 'Show', :action => 'show', :id => comment %></td>
<td><%= link_to 'Edit', :action => 'edit', :id => comment %></td>
<td><%= link_to 'Destroy', { :action => 'destroy', :id => comment
}, :confirm => 'Are you sure?', :method => :post %></td>
</tr>
<% end %>
</table>
<%= link_to 'Previous page', { :page => @comment_pages.current.
previous } if @comment_pages.current.previous %>
<%= link_to 'Next page', { :page => @comment_pages.current.next } if @
comment_pages.current.next %>
<br />
<%= link_to 'New comment', :action => 'new' %>
Delete the tags that link to the Edit and New Comment functionalities. We do not need
anyone adding a comment without reading the story. After deletions, the code will
be as follows:
<h1>Listing comments</h1>
<table>
<tr>
<% for column in Comment.content_columns %>
<th><%= column.human_name %></th>
<% end %>
</tr>
<% for comment in @comments %>
<tr>
<% for column in Comment.content_columns %>
<td><%=h comment.send(column.name) %></td>
<% end %>
<td><%= link_to 'Show', :action => 'show', :id => comment %></td>
<td><%= link_to 'Destroy', { :action => 'destroy', :id => comment
}, :confirm => 'Are you sure?', :method => :post %></td>
</tr>
<% end %>
</table>
<%= link_to 'Previous page', { :page => @comment_pages.current.
previous } if @comment_pages.current.previous %>
<%= link_to 'Next page', { :page => @comment_pages.current.next } if @
comment_pages.current.next %>
<br />
That completes the refinement to be done to the VIEW. Now let's modify
the Controller.
Customizing the Controller
Open the comment_controller.rb file and in the new method add the tale_id to
the session object so that the method looks like the following:
def new
@comment = Comment.new
session[:tale_id]=params[:id]
end
Now in the create method, let us get the tale_id and the user_id from the
session, and pass it to the comment object. We have used the session object because
the tale_id is coming as a part of the get request, which will be available only to the
new method and not the create method. After the changes, the create method will
be as follows:
def create
@comment = Comment.new(params[:comment])
@comment.tale_id=session[:tale_id]
@comment.user_id=session[:user_id]
if @comment.save
flash[:notice] = 'Comment was successfully created.'
redirect_to :action => 'list'
else
render :action => 'new'
end
end
We do not want to show the list of comments, once a comment has been added.
Therefore, we will redirect the user to the tale's list once a comment has been added
successfully.
def create
@comment = Comment.new(params[:comment])
@comment.user_id=session[:user_id]
@comment.tale_id=session[:tale_id]
if @comment.save
flash[:notice] = 'Comment was successfully created.'
redirect_to :controller=>'tale', :action => 'list'
else
render :action => 'new'
end
end
Apart from this, we have to change the list method so that it finds that only those
comments are selected for which the tale_id has been passed through the link. So
let us modify the paginate method in the list method to add a condition. After
modification, the list method will be as follows:
def list
@comment_pages, @comments = paginate :comments, :
conditions=>['tale_id = ?',
params[:id]] :per_page => 10
end
As you can see, the paginate method takes the table to paginate, the condition
which is optional and the number of items to be shown per page as arguments.
And that completes our current work on the Comment management module. Now it
is testing time!
Testing the Module
Let us start with the authorization part. Give the following URL at the address bar:
http://localhost:3000/tale
If you get the following screen, it means authorization is working fine:
Next let us test the login functionality. Firstly, give the wrong User name and
Password (give anything). If you get the following screen, then the changes are
working fine:
Now, give the correct User name/Password combination. I am giving tester as User
name and testing as password. If you get the following screen, then authentication is
working fine, and also the redirection is doing what it is supposed to do.
Now click on the list link of the first tale and you will get the following screen:
On the detail page, click on the Add Comment link. The following screen
will be displayed:
Give the following inputs:
Comment Body—This is a test.
Submission Date—(leave the default date)
Now click on Create. Then, if you get the following screen you can rest assured that
everything is working as planned.
Now click on the Show link again and select the View Comments link. If you get the
following screen, then the functionality is working:
These tests tell us that the changes we did are working fine. And that completes our
'endeavour' on gathering the user comments.
Summary
We have completed Login management and Comment management. Login
management was one of the loose ends from the User management part. Now we
can concentrate on enhancing the developed modules. These enhancements include
custom template creation, the logout option, database-independent table creation,
and other features that need to be completed before moving on to developing the
new functionalities. These enhancements will implemented in the next chapter. So
keep reading!






September 29, 2009
Ruby