Wednesday, September 24, 2008

Subversion pre-commit hook in Perl

Subversion has a beautiful concept called a "hook" to ensure/apply control mechanisms in your project.

A hook is a program triggered by some repository event like, creating a new revision or changing some property of an artifact. Each hook is given information as to what the event is, which target is it working on, username who triggered the event and depending on the hook’s output or return status, the hook program may continue the action, stop it or suspend it.

The "hooks" subdirectory of the repository path is filled with templates for various hooks like:

$ dir /hooks/
post-commit.tmpl pre-commit.tmpl
post-lock.tmpl pre-lock.tmpl
post-revprop-change.tmpl pre-revprop-change.tmpl

By examining the contents of these template scripts you can see which event triggers each such script to run and what data is passed to that script.

To actually have a working hook, you need to place some .exe or a script into the /hooks directory which can be executed and the name of the hook should be similar to the .tmpl name without its extension, like "pre-commit.exe" or "post-commit.bat" etc depending on the event it get triggered. As the name itself suggests, "pre-commit" hook gets triggered before a commit action is performed, a "post-commit" hook is triggered after a successful commit action is performed.

On unix these scripts can be written as a shell script, python program or a c program etc and the name should be exactly like the name of the hook. On windows the concept is the same but the extension of the script should be .bat or .exe accordingly.

Task to Perform: The pre-commit hook should checkout for:
  1. A “mandatory” message (comment) during svn commit operation. In case you haven’t given any message, it would throw an error and abort the commit.
  2. In case you have given a message but the message doesn’t meet the minimum character requirement, it would throw an error and abort the commit.
You will need these two files to implement it.
  1. pre-commit.bat:

    PATH=C:\perl\bin
    perl.exe c:\newrepo\hooks\pre-commit.pl %1 %2

    A windows batch file that sets the path of the perl executable and also specifies the path of the pre-commit.pl script that needs to be executed by the perl.exe.

  2. pre-commit.pl:

    #! c:\perl\bin;
    # creating scalar variables that holds some values
    $min = 8;
    $svnlook = '"C:\Program Files\CollabNet Subversion Server\\svnlook.exe"';

    #--------------------------------------------
    $repos = $ARGV[0];
    $txn = $ARGV[1];
    $msg = `$svnlook log -t "$txn" "$repos"`;
    chomp($msg);

    if ( length($msg) == 0 )
    {
    print STDERR "A Message is Mandatory";
    exit(1);
    }
    elsif ( length($msg) < $min ) { print STDERR "Message should be atleast $min characters in length"; exit(1); } exit(0);

    The above perl script that does the following:
    • Has the perl executable path set in the shebang line
    • Has a scalar variable which stores the minimum number of characters to expect in a message(comment)
    • Has another scalar variable which is set to the path of svnlook.exe.
      Note the entire path is enclosed within “” (double quotes) to negate the embedded spaces and then the whole entry is enclosed within a ‘’ (single quotes) and double back slashes before the exe name.
    • svnlook command runs to gather the message that was entered in the last transaction in the repository. If the length of the message is ‘0’, then it displays a message saying “A Message is Mandatory” , if the message is less than the minimum characters ie “8” in this example, it would throw an error message saying “ Message should be at least 8 characters in length”.

Set-up for Test: Go to the hooks directory of the repository path and create/copy both the above files there (i.e., pre-commit.bat and pre-commit.pl).

Now when you try to commit something into the repository giving an empty message or even a very short message, it should throw an error.

Now try out the following in your working copy and see the hook working:
  1. Try to open a file and change some contents of the file

    C:\wc1\trunk>notepad foo.txt

  2. Now try to commit the same with empty message (output will be an error)

    C:\wc1\trunk>svn commit -m ""

    Output:
    Sending trunk\foo.txt
    Transmitting file data .svn: Commit failed (details follow):
    svn: 'pre-commit' hook failed with error output:
    A Message is Mandatory

  3. Now try to commit the same with a message which is just 2 characters in length

    C:\wc1\trunk>svn commit -m "so"

    Output:
    Sending trunk\foo.txt
    Transmitting file data .svn: Commit failed (details follow):
    svn: 'pre-commit' hook failed with error output:
    Message should be atleast 8 in length

  4. Now try to commit the same with a message of appropriate length

    C:\wc1\trunk>svn commit -m "some meaningful msg"

    Output:
    Sending trunk\foo.txt
    Transmitting file data.
    Committed revision 10.

Wednesday, July 23, 2008

How to empty all tables in mysql database


Many a times, during web application development, particularly while testing, you require to empty all the tables in the database. MySql provides you with the handy 'truncate' function, that empties the table and resets the auto-increment value (if set). Or you can use phpMyAdmin - select table and click on 'empty'. If you have multiple tables, select each table and click on empty in phpMyAdmin or have a script for looping it in php to run the truncate query. It gets confusing, if few of the tables need not be reset all the time. Its furthermore cumbersome, if you have few tables where you have to insert some initial records. All along, I have used phpMyAdmin's feature. How many ever the number of tables, I have patiently and carefully, done select-and-empty. Then gone about manually inserting records.

During one of my php / MySql projects at Signature Solutions (a website design and development company in Bangalore, India), I was tackling more than 40 tables in a database. Few of the tables had data that did not need be emptied every time (particularly, ip2nation table had more than 29,000 records). Searching on the net only suggested - either I drop the entire database and recreate or select each of them individually, and empty using phpMyAdmin. Every time I needed to empty the entire database, I had to be very very careful. I finally, managed to put together a script that would do the job as well. So here it is:-

The approach:
  1. List all the tables in mysql database and store in an array
  2. Create an array with table names that need not be deleted
  3. Run the difference of arrays in a loop to truncate each
  4. Write custom insert initial values
The php code:

<?php

// CONNECT TO THE DATABASE
$dbhost = "localhost";
$dbuser = "username";
$dbpass = "password";
$dbname = "mydatabase";

$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die ('Error connecting to mysql: ' . mysql_error());
mysql_select_db($dbname);

// CREATE A NEW ARRAY TO STORE THE ALL TABLE NAMES
$all_tables = array();

// USE MYSQL'S SHOW TABLES TO GET ALL THE TABLE NAMES
$sql = mysql_query("SHOW TABLES") or die(mysql_error());

while($row = mysql_fetch_array($sql))
{
$all_tables[] = $row[0];
}

// CREATE A NEW ARRAY THAT CONTAINS NAMES OF TABLES THAT NEED NOT BE EMPTIED
$not_to_empty = array('admin', 'countrylist', 'currency', 'templates', 'ip2nation', 'ip2nationcountries', 'settings');

// FIND THE DIFFERENCE IN ARRAYS
$truncate_tables = array_diff($all_tables, $not_to_empty);
sort($truncate_tables);

// RUN A LOOP TO TRUNCATE THE TABLES
for($i=0; $i<count($truncate_tables); $i++)
{
$truncate = mysql_query("TRUNCATE TABLE $truncate_tables[$i]") or die(mysql_error());
if($truncate === true)
{
echo $truncate_tables[$i]." <span style=\"color:#060; font-weight:bold;\">truncated</span><br>";
}
else
{
echo $truncate_tables[$i]." <span style=\"color:#f00;\">could not be truncated</span><br>";
} // END OF IF
} // END OF FOR

// INSERT INITIAL VALUES
$insert_sql = mysql_query("INSERT INTO users (id, first_name, last_name, email, password, status) VALUES ('10000000', 'Signature', 'Solutions', 'sridhar@signature.co.in', '$rand_password', 'active')") or die(mysql_error());

echo ($insert_sql === true) ? "<br><br><strong>DONE.</strong>" : "<br><br>AN ERROR HAS OCCURED. PLEASE RESTORE THE DATABASE FROM THE BACKUP AND TRY AGAIN.";

?>


To dress it up nicely, add some JavaScript before running the php script, that confirms from the administrator if they want to empty of the database:

JavaScript Confirm code:

<script language="javascript" type="text/javascript">

function confirmreset()
{
alert("This option will reset all the data in the database. Before your proceed with the reset, it is strongly recommended that you take a backup of your database. To proceed with the reset, click on the OK button. Click on Cancel button to exit without reset.");

if(confirm("Do you really want to reset the database?"))
{
return true;
document.loginform.submit;
}
else
{
return false;
}
} // END OF FUNCTION

</script>


Sometimes, you are boggled, how simple things can be and how complex we make it.

Sunday, June 15, 2008

Photoshop Circular Patterns

Following the technique of this Photoshop Vector Sunburst tutorial, you can create circular patterns quickly and easily. It looks pleasant too, like the one I used on my web design site.

Take a canvas 450 x 450 pixel with white background. Change the foreground color (I took #800080). Choose a custom shape or any image cut out at the edges. I took a custom shape of a stem with leaves and flowers. Place the bottom-middle edge of the shape at the center of your canvas.


With the shape layer selected we will transform the layer (Edit > Free Transform or ctrl + T). Now you should see the bounding box with nine reference points. Drag the middle reference point to the middle of the bottom border. Changing the reference point will actually move the center of the object to the new position. In the set rotation text box, enter 15. Now your shape should have rotated 15 degrees clockwise with the center of the shape at bottom middle border. Hit enter twice to apply the transformation.

Now press ctrl + shift + alt + T. This should create a new shape layer with a further 15 degree rotation from the center of the canvas. Repeat ctrl + shift + alt + T, till you complete a circle and get your circular pattern.



Tuesday, May 20, 2008

Photoshop Vector Sunburst

I have been using sunburst in many of my works. But having to make them repeatedly, led me to see up a tutorial on vector sunburst. In most searches, I landed up here http://www.slicktutorials.com/vector_sunburst. Though it is a good way, but transforming each end of the flower shape is quite tiring. Suddenly, I came across a tip that helped me to create it much easily and faster. So here it is:
  1. Open Photoshop and a new document. I took 400 x 400 pixels.
  2. Set the foreground color to #ff9900 and background to white. Fill the background with white.
  3. Select the Polygon Tool and draw a triangle from top with one edge at the center of the document. Transform (Edit > Free Transform or ctrl + T) the triangle to look like a sunray. See my image below:
  4. Select the Path Selection Tool (A) and click on the vector layer. All the three edges should now have the anchor points.
  5. With the anchor points on, we will transform the layer (Edit > Free Transform or ctrl + T). You should see the bounding box with nine reference points and the middle one selected.
  6. Drag the middle reference point to the middle of the bottom border. Changing the reference point will actually move the center of the object to the new position.
  7. In the set rotation text box, enter 20. Now your shape should have rotated 20 degrees clockwise with the center of the shape at bottom middle border. Hit enter twice to apply the transformation.
  8. Keep the anchor points selected and press ctrl + shift + alt + T. This should create a new set of triangle with a further 20 degree rotation. Repeat ctrl + shift + alt + T, till you complete a circle and get your Sunburst.
  9. Now, you can unselect the vector shape and apply any blending options that you want to apply.
In this method, by keeping the anchor points selected and then doing a transform, you are adding to the vector shape within the main layer itself. This will help you in easily saving the shape as a custom shape.

Alternately, you can just transform & rotate without selecting with the Path Selection Tool, hitting ctrl + shift + alt + T to get individual duplicated layers.

Another beautiful realistic sunburst tutorial I found is here.

Monday, May 12, 2008

Using Perl CGI with MS-Access on IIS

Though I was working on Perl for the past 1.5 years I was unaware of a few simple permission settings that may be required on a Windows machine with IIS to ensure that a Perl CGI program interacting with MS-Access Database with the help of DBI package perfectly works.

Let us make a test case. We will require two files -
  1. main.html - a simple html form
  2. update.pl - Perl script that would be called once you submit the html form. This script would update the data into an MS-Access database - emp.mdb
main.html

<form id="form1" name="form1" method="post" action="update.pl">
<p>Employee Id:
<input name="emp_id" type="text" id="emp_id" />
</p>
<p>Employee Name:
<input name="emp_name" type="text" id="emp_name" />
</p>
<p>E-mail:
<input name="mail_id" type="text" id="mail_id" />
</p>
<p>
<input type="submit" name="Submit" value="Update" />
</p>
</form>

The html form as you can make out is a small data entry form with three text fields for filling up the details like employee id, name, email address and once you enter all these details, you click on the submit button.

update.pl

use CGI qw(:standard);
use DBI;

sub validEmpId
{
print "Validating Employee id";
if($emp_id !~ m/\d*/)
{
return false;
}
else{
return true;
}

}
sub validMailId
{
print "validating mail id";
if($mail_id =~ m/\w+\@\w+\.com/)
{
return true;;
}
else
{
return false;
}
}
$emp_id = param("emp_id");
$emp_name = param("emp_name");
$mail_id = param("mail_id");

if($emp_id =~ m/[a-z A-Z]/)
{
exit;
}
elsif($mail_id =~ m/\w+\@\w+\.com/){
##store it in the database
$dbh = DBI->connect("DBI:ODBC:emp");
$sth = $dbh->prepare("Insert into emp_data(Id,Name,Mail_ID) values($emp_id,'$emp_name','$mail_id')");
$sth->execute();
$sth->finish();
$dbh->disconnect();
}
else{
exit;
}

The data entered through the html form, is passed on to the update.pl Perl script which inserts the data into the database. You could also use the sub-routines of the program for some validations like ensuring the Employee ID is a number and the mail ID is in a correct format etc., and then insert the data into the MS-Access database.

Settings:
  1. The html file, the Perl script file and the emp.mdb (MS Access database) resides in a folder.
  2. Appropriate DBI/ODBC Perl packages are available.
  3. Appropriate ODBC DSN (Data Source Name) called “emp” is created , connecting to the MS-Access Database called “emp.mdb”, which has a table called emp_data which is supposed to store the entered employee details. (Done using the “Data Sources(ODBC)” option under the Control Panel -› Administrator Tools)
  4. Also created a virtual directory on the IIS and linked to the physical directory where the html and the Perl script resides (Eg.: A virtual directory called “ss” was created which was linking to the actual physical directory where the html, Perl and the mdb files were residing) (Done using the “Internet Information Services” option under the Control Panel -› Administrator Tools)
How the program is supposed to behave: Once the main.html file is accessed through the browser, the Data Entry Form is to be shown. Once all the data is entered into the text fields and form submitted, the data is supposed to be passed to the Perl script file (update.pl) which would in turn update the data into the corresponding database.

How was it actually behaving: The form shows up properly, but when the data was entered and submitted, it throws up an error, like:

CGI Error

The specified CGI application misbehaved by not returning a complete set of HTTP headers. The headers it did return are:
DBI connect('emp','',...) failed: [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified (SQL-IM002)(DBD: db_login/SQLConnect err=-1) at C:\mini-project\store.pl line 40
Can't call method "prepare" on an undefined value at C:\mini-project\store.pl line 41.

Solution:
  • The physical directory where you store all your files and then link it up with the virtual directory in IIS has to have some specific permissions settings. User called IUSR_, IWAM_ have to be given full permissions (at least read and write). (right click on the folder -> properties -> security tab -> add -> advanced -> find now -> choose the user earlier mentioned and give them full/appropriate read and write access)
  • Then in the data source name that we create in the ODBC, we need to create it as "system DSN" instead of "user DSN" and then give enough login and password details if required. For MS Access the default user is “admin” and password is NULL password.

Tuesday, April 15, 2008

Opportunities are more important than success

I can say that, having been able to build a web design company in Bangalore from ground up and keep it afloat for more than three years now. Like most of the startups I know of, we started off more out of compulsion than a pressing impulse to do so. Likewise, we didn’t have the wherewithal to invest or the wizardry to put up a gizmo on the market. All we had was a mouth that fed the stomach.

Put your mouth where the food is. When you have just started off, get into business areas that have very very short selling cycle, products / service that have low price band, and have sufficient demand to accommodate many vendors (at least you). This will help to earn the daily bread and keep the legs running. The low prices will keep the cash registers ringing and the customers wouldn’t bargain much allowing to keep the margins. Large demand base is important because, as a novice, nobody wants to be mauled in the competition and burn the profits. We got into domain registration and web hosting.

Sell what sells. Until unless you are the crafty type and can carve a niche for yourself with an ultra-modern innovative product / service, sell run-of-the-mill stuff. This will get the going quickly, without requiring to spend much of the time in developing out-of-the-box solutions at this juncture. The key is to keep the focus straight on generating revenues and avoid the gray areas. We got into static website design.

Build on your customers. Keep your customers in close quarters. Ensure that they know well about you and your business. Happy customers are more likely to entrust you with new business opportunities, although they know well that you have almost no experience in executing such work. They would factor reliability and confidence on you while avoiding having to search for an unknown, and may be, an expensive vendor. Today, we have customers who had started with basic website with us and we are managing their web applications.

I remember writing down the first two captions and sticking it up on my desk and repeating it over and over again to myself. It sure kept us in the loop all the time. We didn’t get much time to worry about the things that didn’t work. Volumes grew gradually. Along with it, the customer’s requirements grew too. They started demanding newer features. We kept toiling to execute each new requirement. Sometimes the requirements went overboard and we started getting all worked up, but stuck with it and achieved each one of them. In a while, we realized that this is the order of the day – newer requirements every moment, newer solutions all the time. The bar keeps raising all the time. Every time we overcame an impediment, it enthralled us, but would fade away the moment we had a new challenge. Success is always a moving target, opportunities are what make you stronger. Experience of handling varied challenges makes you knowledgeable. The know-how puts you in control of things and builds confidence. The confidence can open many more doors than anything else. In short, all the positive words of the dictionary start rolling into each other after this point. So Good Luck and open up your life to every opportunity.