Showing posts with label controller. Show all posts
Showing posts with label controller. Show all posts

Thursday, 23 August 2007

Bug #2: Problem with updating user object

The bug was found in the model side as I was trying to update the user object by doing:


@user = User.update(params[:id], {
:fname => params[:user][:fname],
:lname => params[:user][:lname],
:email => params[:user][:email] })

Whenever I tried to update, I'd get this error:

ArgumentError in UserController#update wrong number of arguments (2 for 4)

Could not figure out what was wrong with it from googling. So once again I decided to hop onto my trusty real-time help solution: irc. I got suggested to try this method of updating:

@user = User.find(params[:id])
@user.update_attributes({
:fname => params[:user][:fname],
:lname => params[:user][:lname],
:email => params[:user][:email]}) if @user != nil
This seemed to work perfectly. I still haven't figured out what was the problem with the other one. Perhaps its because I didn't provide the other two parameters that's in the table definition: password_hash and password_salt. I'm not sure.

Bug #1: AJAX checking in certain actions

So I came across a bug as I was testing my newly written code to check whether handles and usernames have already been registered in the database. This worked fine when I want to sign up. However, when I want to update a user's profile, it would not let me update my profile with my existing email because the email already exists in the database. Pretty obvious that there's a bug there.

So when I did my AJAX checking, I had to determine what type of action I was doing (i.e. signing up, updating...). I had a parameter called action which would be sent in the query string, telling the controller what kind of action I am doing. The problem is, whenever I did that, my code for some reason could not recognise what kind of action I'm performing. When I printed out params[:action], it gave me the controller's method name.

Out of frustration, I hopped onto irc to get some real-time help. Turns out that action was a reserved name used for routing purposes, which determines which method of the controller you're calling to. D'oh! Obviously I should've known this as my code is scattered with :action => "something".

So I changed the parameter name to actiontype and it worked perfectly!

Tuesday, 21 August 2007

Form validation with AJAX

Finally managed to utilise javascript and ajax to do client-side form checking. I am using javascript instead of rjs because I feel like I have more control over what I'm doing and also I'm more familiar with the javascript syntax. Since prototype framework is included as part of the javascript defaults in RoR, I will be using that framework too.

Its got some pretty handy things like accessing DOM elements using $('element id'), so you don't have to deal with document.getElementId etc. The framework also provides a cool Ajax object which handles all the nasty readystates that you will come across if you use the conventional way of doing Ajax.

Here is a snippet of the client-side code:

new Ajax.Request("/user/check_exists",
{
parameters: params + "=" + textField.value,
onLoading: showMsg(infoField, "<img src="../../images/ajax/ajax-loader.gif" border="0" /> Checking if available..."),
onSuccess: function(request) {
if (request.responseText)
{
showMsg(infoField, request.responseText);
textField.className = "fail";
textField.focus();
}
else
{
showMsg(infoField, "");
textField.className = "success";
}
}
});
Here is a snippet of the server-side code:

def check_exists
if params[:handle]
@handle_exists = User.find(:first, :conditions => [ "handle = ?", params[:handle] ])
if @handle_exists == nil
render :text => ""
else
render :text => "Username is taken. Please choose another."
end
elsif params[:email]
@email_exists = User.find(:first, :conditions => [ "email = ?", params[:email] ])
if @email_exists == nil
render :text => ""
else
render :text => "This email is registered. Please choose another."
end
end
end
Sorry about the indentations if they don't come out correctly. For some reason this current blogger layout strips any unnecessary whitespaces.

Here are some screen shots of what I've done today. You will notice I used a little CSS to make things look nicer.



[edit date="23-08-07"]

I decided to move the business logic into the model and leave the transaction logic in the controller. More elegant and scalable I reckon.

In the model:
def self.attribute_exists?(attribute, value)
@attribute_exists = User.find(:first, :conditions => [ attribute + " = ?", value ])
if @attribute_exists != nil
return @attribute_exists
else
return nil
end
In the controller:
def check_exists
if params[:actiontype] == "new"
if params[:handle]
@user = User.attribute_exists?("handle", params[:handle])
if @user == nil
render :text => ""
else
render :text => "Username is taken. Please choose another."
end
elsif params[:email]
@user = User.attribute_exists?("email", params[:email])
if @user == nil
render :text => ""
else
render :text => "This email is registered. Please choose another."
end
end
elsif params[:actiontype] = "edit"
@cur_user = User.find(params[:id])

if params[:handle]
@user = User.attribute_exists?("handle", params[:handle])
if @user == nil || @user == @cur_user
render :text => ""
else
render :text => "Username is taken. Please choose another."
end
elsif params[:email]
@user = User.attribute_exists?("email", params[:email])
if @user == nil || @user == @cur_user
render :text => ""
else
render :text => "This email is registered. Please choose another."
end
end
end
end
A general rule of thumb is that code that requires communication with the database should be placed in the model side instead of the controller. The controller's job is to handle the user's request, retrieve data from the model and provide a response if necessary. The view's job is to handle presentation layer (i.e. HTML & CSS) for viewing purposes (thus called the View).

[/edit]