is it changed? which attributes? what are the old values? in some cases you may be asking yourself these questions when dealing with some models
When should you care about these questions?
suppose you have a model doing an expensive operation on save. this operation is useful only if one specific attribute changed. so doing it on every save neglecting which attributes changed will degrades performance significantly
So what can you do?
you can load model old data from database and check if that attribute has changed or not and if so do your action
or
you can use acts_as_modified plugin, it helps keeping modified attributes with their old values within model in order to be capable of answering your questions without doing any extra database trips
I am exited, from where can i get it?
it depends on your rails version
1. ((rails 2.1)) do nothing, dirty objects already embedded in this version
2. ((rails r7315 and above)) install from http://svn.viney.net.nz/things/branches/acts_as_modified_edge
3. ((ELSE)) install from http://svn.viney.net.nz/things/rails/plugins/acts_as_modified
That's it, Enjoy the fun
Tuesday, July 22, 2008
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
Accessing the last return value
Rollback all database changes on exit
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
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
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
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
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
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
i got that error
i changed the controller to be
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
That's it, it worked and i didn't get the error once again
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
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
i was using the class and writing this snippet of code to get the count of feeds in the db
looking at the log, i found that the added query was
and not
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
or
but don't use 'length' method
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
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
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
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
Subscribe to:
Posts (Atom)