Saturday, December 6, 2008

Some months with RSpec


For several months, I have been writing test-case ahead of any implementation. But the joy multiplied when we started thinking BDD. To me using RSpec as BDD tool is not much different than Rails default test mechanism other than the representation, unless you try the Story.

Test first development has a big advantage that you consider all possible scenarios before writing code. Yet sometimes you may miss some lines covered. An wonderful gem you require to check test coverage in Ruby is rcov. And no wonder RSpec has built-in support for rcov :) To test you code coverage, run
rake spec:rcov

This will create a folder named "coverage" on your rails root. Open the index.html from that folder in your browser to see how much your code is covered by test case. The screenshot is a polling script I just started.

Monday, December 1, 2008

Show numeric digit in your language

My previous post was about a Ruby on Rails plugin I wrote to convert any numeric digit to other languages' representations. Here is the internals; how that works.

That plugin converts a whole string character by character. To add any language, just add that language's digits to "NUMERIC_LOCALIZE_MAP" hash.


NUMERIC_LOCALIZE_MAP = {
'bn' => ["","","","","","","","","",""],
'hi' => ["","","","","","","","","",""],
'ar' => ["٠", "١","٢","٣","٤","٥","٦","٧","٨","٩"]
}

def localize_numeric(source, locale_code = nil)
string_representation = source
result_string = nil

if locale_code && NUMERIC_LOCALIZE_MAP.include?(locale_code)
result_string = string_representation.gsub(/\d/){|digit|
NUMERIC_LOCALIZE_MAP[locale_code][digit.to_i]
}
else
result_string = string_representation
end
result_string
end

Tuesday, November 25, 2008

Ruby on Rails plugin to localize numeric digits (translate to any language)

Probably somewhere in... is the first company to build Bangla web2.0 solutions using rails. The first problem we faced is the lack of support for Unicode in Ruby. But fortunately we rarely needed to use string funtions and thus avoid the consequence. Secondly, we needed a stable and efficient il8n library(plugin) for Ruby on Rails to translate the interface (we had planned to roll out with support for two language initially). The rescue came from globalize plugin. Its pretty old and no update for quite a while, yet this robust plugin suits the best to our need. The translation list is backed by database. So, you can easily create admin panel to manage translation list.

Then we felt the need for another feature; representing numeric digits to local language. And its better if that can work seamlessly with our existing code written using globalize plugin. The plugin globalize_numeric, with globalize, can translate your digits to any language (currently have Banga, Hindi, and Arabic) with minimal change in code that uses globalize for localization.

To add other language, add the language digits to NUMERIC_LOCALIZE_MAP in 'lib/core_ext.rb' file.

Plugin's github repository

Monday, November 24, 2008

Converting Bangla (Bengali) digits to ascii ones

ruby 1.8 doesn't support unicode. This creates problem when I need to parse Bangla digits in a text where each Bangla digit is represented as three bytes in utf-8.

I used a simple approach to satisfiy my need - 1. take the the last byte of each bangla digit, and 2. subtract by last byte of bangla digit '0'. Converting the whole number is even simpler. Here is my code -

  def self.translate_number_from_bangla(p_bn_number)
en_number = 0
length = p_bn_number.length
for i in 1..length/3 do
cur_digit = p_bn_number[3*i-1].to_i - 166
if cur_digit >= 0 && cur_digit <= 9
en_number *= 10
en_number += cur_digit
end
end
return en_number
end

Tips: Update your rubygems (with special case)

The most suggested updating technique is
$ sudo gem update --system


But this technique may not work with rubygems version 1.1 and 1.2. run
$gem -v

to know your rubygems version.

For those having these two versions, install rubygems-update gem and then run update_rubygems command (as sudo if necessary)

$ sudo gem install rubygems-update
$ sudo update_rubygems

Tuesday, April 15, 2008

View offline documentation of installed ruby gems

From command line, just run this -
$ gem server

This will start ruby documentation server on port 8088

previously it was
$ gem_server

which is now deprecated and may not work in your system

Thursday, April 3, 2008

Installing Ruby On Rails on Ubuntu with rmagick

My laptop hard disk drive (it was a fujitsu drive on an HP Pavilion nobebook pc) was gone withing just two months of purchase. Later I got the replacement but, I had to spent another to prepare rails stack again :(
Anyway, here is the recipe for Ubuntu 7.10
$ sudo apt-get install ruby irb ri rdoc ruby1.8-dev libopenssl-ruby1.8 build-essential
$ wget http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz
$ tar xzvf rubygems-1.0.1.tgz
$ cd rubygems-1.0.1
$ sudo ruby setup.rb
$ sudo ln -s /usr/bin/gem1.8 /usr/bin/gem
$ sudo gem update --system
$ sudo gem install rails
$ sudo gem install mongrel

I couldn't install rmagick on the first place. Because building rmagick (v2.3.0) requires ImageMagick version 3.0 or greater but current Ubuntu repository provides only v2.6 (by the time I wrote this). So, I had to install from the source. Thanks to Emmannual for this suggestion.
Remove ImageMagick, if you have already installed
sudo apt-get remove imagemagick

Now download, compile and install from the ImageMagick source
$ tar xvvzf ImageMagick.tar.gz
$ cd ImageMagick
$ ./configure –prefix=/usr
$ make
$ sudo make install
$ sudo gem install rmagick

Or alternatively, you can download binary of ImageMagick-dev from debian repo

$ sudo apt-get install libmagick9-dev
$ sudo gem install rmagick

One more step. Install all necessary gems that are required by the project you are working. Like, our current rails project uses the gem 'memcache-client'. So, I ran the following
$ sudo apt-get install memcached
$ sudo gem install memcache-client

Enable mod_rewrite in Apache under Ubuntu

'mod_rewrite' is disabled in default apache2 installation. To enable this, I followed following procedure. Thanks to Lavlu for showing me this.
At first, load mod_rewrite using Apache's mod enable command
# a2enmod rewrite


Then enable mod_rewrite from Apache2's configuration file. In default installation, there should be a 000-dafult file under /etc/apache2/sites-enabled directory. You should modify that. Just make the value 'All' (it should be 'None' by default) to AllowOverride key for '/' and '/var/www/' directory. So, the line will look like -
AllowOverride All



Now force reload Apache configuration
# sudo /etc/init.d/apache2 force-reload

Backing up mysql database and restore

This is also needed when you need to move your database elsewhere.
To backup your database -
mysqldump --user <mysql_username> --password=<mysql_password> <database> [<tablename>] > <output_filename>

To restore/initiate from the backup
mysql --user <db_username> --password=<db_password> <dbname> < <dump_file>

Thursday, March 20, 2008

Running a schedule task for a rails application

There are several ways described in ruby on rails wiki. But for our need, I took the simplest one - create a cron entry that executes RunnerScript.

At first create a static Model method. RunnerScript can only access model and run its class methods, it cannot access controllers. For example, we needed a script that periodically check for expired items and mark. So, I wrote a class method like follows -

def self.mark_expired_items()
update_query = "status = #{Constant::Item::STATUS_EXPIRED}"
# find expired items
update_conditions = "status = #{Constant::Item::STATUS_PUBLISHED}
AND created_at < '#{(-Constant::Item::EXPRITY_TIME).days.from_now.to_s(:db)}'
"
update_all(update_query, update_conditions)
end



Then create a cron entry that runs this periodically. For our purpose (we wanted to expire item 4 days after publishing)
0 * * * * <project_path>/script/runner Item.mark_expired_items

Thats it. This script checks every hour for expired items and mark all of those as expired

Tuesday, March 18, 2008

ssh login without re-entering password

At my work, i have to enter our hosting server. Sometimes, several times a day. And obviously I have to enter password to get through.

I knew the solution before. We used to do this in our distributed systems lab at college. Because, sometimes it becomes necessary to enter a remote machine without password by the automated scripts.

And today I had to rediscover this again ;)

Enter the remote machine and check there exists .ssh folder under `~` (home) folder. if it doesn't exist, create one.
mkdir ~/.ssh

then 'exit' to come back to your own console.
2. create your own key. For simplicity, just press enter at all prompts
ssh-keygen -t dsa

3. Copy your ssh key to remote host
cat ~/.ssh/id_dsa.pub | ssh <remote_host_address> "cat - >> ~/.ssh/authorized_keys"

Voilà! You're done. Now try to ssh again to the remote host. You'll get the remote shell without any password :)

Monday, March 17, 2008

Migrating subversion repository - forking new project

This far in our company we were using single repository project to maintain all our projects. That is, we create a new subdirectory in the root project directory when we need to start another project. It requires less server side maintenance at the cost of less security. It was just fine for a small developer team, until recently.

Now I need to create projects out from each directory under root (i.e. each project), with revision number starting from 1 for each project.

The steps I followed -
1. Created svk mirror of each projects root directory.
2. Dump svk's local mirror. This creates a plain svn dump maintaining projects revision number
3. Manually removing projects root directory
4. Load svn dump to a new svn repository

That's it :)

1. Create svk mirror for each project
a. initialize local mirror
svk mirror <svn url>/<project_directory> //<project_name>

b. sync, copy project directory to local svk mirror
svk sync //<project_name>
2. Dump local svk mirror
svnadmin dump ~/.svk/local > <dumpfile>
3. Manually removing project's root directory


4. Create a new project and load the dump

sudo svnadmin create <project_name>

sudo svnadmin load <project_name> < <dumpfile>

Monday, February 25, 2008

Rails Time and Database

Last few days I improved rails code related to time function in several places.

1.

Rails extended basic Time class to give output in several format. So that you can output in database datetime format by passing :db symbol as argument to Time's to_s() method. Previously I produced database format manually
Time.now.strftime("%Y-%m-%d %H:%M:%S").to_s
now, it simplifies to
Time.now.to_s(:db)
Detail documentation can be found here

2.
To parse database date to ruby Time, I used this.
Time.parse(token.created_at.to_s)
where created_at is DateTime type field in Token model

3.
To get back-and-forth between Unix Timestamp and ruby Time, you can simply use -
Time.now.to_i
and to read -
Time.at(unix_timestamp)