Sunday, June 22, 2008

Rails Validations & Unit Tests

I was doing a new feature in an existing rails project, and after finishing that feature, i ran all tests to check that everything is fine and no failures or errors occurred after my change

i ran the tests and found a failure in one of the unit tests, i opened it to see what is the problem. at this point i found in that file a function that gained my attention.

before stating what is inside this function, lets have a look at the part of the model that is being tested by this function

class Feed < ActiveRecord::Base
validates_presence_of :name

...
end

we can see in this part that name field should be present for the model to be valid, now lets look at the testing function

f = Feed.new
assert !f.valid?
assert f.errors.invalid?(:action)


So, what is the point in this code? the point is that at my first look at this function, i considered it a redundant test function because it checks an already tested rails function which is the validation function.

so i asked a friend of mine about his opinion, and he agreed with me that it is redundant and useless

later on, i asked someone with greater experience about his opinion also in this function as it represents a class of functions found in many models checking such validations.

But his opinion was different. he considered it useful and justified that by stating that this function tests the contract (which is that no feed is accepted if no name is stated) with the client, so that if someone later on changed this part in a wrong way, this test will alert us that the contract has changed. so that we reconsider our action to see if it has been really changed by the contract or by mistake

i found that answer reasonable and added to me a new way of thinking towards tests which is testing the agreements rather than checking whether our code is working or not

Rails: Modules & Routes

I used to make a module for every set of controllers which are doing certain task or feature because this way, things become organized in a better way

but something weird happened to me while trying to test a controller inside one of the modules, to see what happened i represent my case here by an example

i added in routes file this snippet of code

map.resources :items do |item|
item.resource :rate, :controller => 'ItemActions::Rate'
end

and in my controllers folder, made a folder called 'item_actions' and added inside it a file called 'rate_controller.rb', inside this file i wrote

module ItemActions
class RateController < ApplicationController
...
end
end

restarted the server and run my work, everything fine and i can call the controller actions as any other controller

then i opened the controller test file and inside one of its test functions, i added this code

post :create, {:item_id => 1, :rating => 50}


i got that error

No route to match {:controller => 'item_actions/rate', :action => 'create', :item_id => 1}


i changed the controller to be

class ItemActions::RateController < ApplicationController
...
end

but nothing changed, i am still getting that error once again, searching for a long time, i reached to a working solution which was to open routes file and change it to

map.resources :items do |item|
item.resource :rate, :controller => 'item_actions/rate'
end


That's it, it worked and i didn't get the error once again

Wednesday, June 18, 2008

Remember User Login Via Cookies

How to persist user login via cookies if session has expired?, this is a question almost answered in most of nowadays web applications, So how can this be done?

The idea is simple, and can be summarized in these simple steps

1. add a new column in your users table called login_key or remember_token or any name you prefer of type string

2. whenever user tries to login and his login is successful, add in his cookies a cookie called anything ex: remember_token and let its value be a generated random string of length around 40 characters for better security which is our token, and save this token in the db with that user

3. now add, in any action you need to check in it whether this user already in system or guest, a snippet of code that checks if the person isn't logged in whether the request has a cookie with the name choosed before and if so, check the db looking for a user having that token, if found then this user is logged in as if he did that in the normal ordinary way

That's it, now you can use cookie to log user in if his session has expired. There are also some points you may need to take care of when using this technique

1. Make cookie expires after some time so that it gets created once again with new token

2. In order to provide extra security, add with that token another info about the user

Monday, June 16, 2008

ActiveRecord Association Weird Length Method

The method 'length' defined for ActiveRecord associations works in a very weird manner.

consider this example


class User < ActiveRecord::Base
has_many :feeds
end


i was using the class and writing this snippet of code to get the count of feeds in the db


@user = User.find :first




puts @user.feeds.length


looking at the log, i found that the added query was


SELECT * FROM `feeds` WHERE (feeds.user_id = 1)


and not


SELECT count(*) AS count_all FROM `feeds` WHERE (feeds.user_id = 1)


so in order to get away from such weird sql statement which will exhaust a very large bandwidth getting all records to count them, use this instead


puts @user.feeds.count


or


puts @user.feeds.size


but don't use 'length' method