Sunday, July 20, 2008

Rails Console Tips & Tricks

Rails console have many useful tips that you may exploit as

Getting out of a block of code


If you're in an indented block of code (e.g., something that should be terminated with end) and want to escape without finishing, you can use ctrl + c to get back to the top level. The current expression will be ignored and not run.



Accessing the last return value


you can use the magic variable (_) for accessing last value
and you can then assign this value to a variable as x = _



Rollback all database changes on exit


script/console --sandbox

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

Thursday, May 15, 2008

SVN Branches

Imagine working on a deployed project, and you want to add some new features and changes on the running copy.

Since making new features may cause problems in the deployed project, you need to have another copy from the application and work on it freely then merge it with the running copy to apply your changes.

Subversion makes this very easy, all you need to do is to create a new branch.

So, what is a branch?

in order to understand that, lets look at the repository location hierarichy, you will find that it is as follows
--
-- trunk
-- branches
  • trunk is the folder containing the version of the website currently deployed

  • branches is another folder containing some other versions, as a new edition of the application
Now, in order to do that, just run this command

svn copy ((repository-path))/((project-name))/trunk ((repository-path))/((project-name))/branches/((branch-name)) -m "Creating a private branch in order to ... bla bla bla"


now you are done with your branch, checkout the project from that new path, and do any changes you want freely then at the end merge with your trunk to apply your changes

Monday, May 5, 2008

acts_as_changed plugin farewell

i used acts_as_changed plugin since a while and i have been enjoying the idea of being capable to know which attributes has been changed in my model and to take action upon certain attributes changes

and today i only tried to look at it again to know if there are any changes made in it or not but, i was shocked when i read a comment from the owner stating that it isn't plugin anymore

it is now an alternative base.rb file that you can update your activerecord with if you want to enjoy the plugin functionality once again

and he stated that he did so because he was facing problem in callbacks that he couldn't solve till now except through this way

anyway, i hope he finds a solution for it so that we all benefit from this plugin functionality