Who’s the boss

14/07/2009

Now that I am settled in India, I finally have time to update the blog. I was surprised by the penetration and popularity of broadband here in just a couple of years. Almost everyone who had dial-up has upgraded to broadband for the same price.  The speeds aren’t very fast but you can get upto 200KB/s consistently. 

The reason I came to India was to work in the family business and get some experience running a company before I go for my MBA. I was involved in the software field (undergrad & work) for 8 years and now I am working in the manufacturing sector. These fields technically are not even remotely related but from running a business view they aren’t very much different. A business is like a sailing ship for ex: the captain steers and the rest of the crew work as a team to get the sails in place. So you don’t need to be a technical wizard and know everything in that field to be able to successfully manage the business. However, you have to know enough so that people can’t fool you.

A person needs to act like a “sponge” when trying to understand the intricacies of business in a different field in a new workplace. I try to follow the “rules” below:
- self-learning
- talking to the employees about their work and learning from them
- listening to work related discussions, even though you can’t contribute much

Finally, do not feel self-entitled to be the “boss” right away. You need to show the employees that you are willing to work just as hard as they are and not just boss them around because your dad owns the business. I have been working for a week and I already feel excited about the work. Sure I don’t have the technical background but running your own business is very rewarding and gets me motivated.

We had to do a massive restructuring of one of our client’s applications and it required changes to the domain names and URL’s.

The command below will replace occurrences of www.infoexport.gc.ca/ie-en with www.tradecommissioner.gc.ca/eng in html files recursively. The \ is used to escape / in the URL’s.

If you are running this on Windows you HAVE to specify a backup file name using the -i parmeter. This is a limitation of the Windows file system, the side effect being its always good to have backups.

$ find . -type f | xargs perl -p -i’test.bak’ -e ’s/www.infoexport.gc.ca\/ie-en/www.tradecommissioner.gc.ca\/eng/g’ *.html

I came across a bug in our codebase, the end user didn’t see the error message but just a empty result so it wasn’t considered high priority .. But anyways, after looking at the logs the problem was with the NLSSORT and SQL ..

The sql was generated dynamically so it was a bit tricky to debug, lets take a look at the valid sql below :

SELECT a, b FROM x, y UNION SELECT c, d FROM m, n ORDER BY a ASC

This worked fine but the error came up when looking at french content which was sorted using NLSSORT.

SELECT a, b FROM x, y UNION SELECTc, d FROM m, n ORDER BY nlssort(a, ‘nls_sort=french’) ASC

The above SQL is invalid because of the way NLSSORT works .. We need to explicitly select the columns needed from the result of the SQL, like this :

SELECT * FROM (SELECT a, b FROM x, y UNION SELECT c, d FROM m, n) ORDER BY nlssort(a, ‘nls_sort=french’) ASC

I came across a small problem when trying to access a dblink in my SQL using java.sql.PreparedStatement .. I tried using java.sql.Statement instead to build my query and it worked fine.

I recently had a chance to work on a project which required massive amounts of file and folder restructring. I could’ve done this manually to minimize errors but I chose not to due to time constraints and the fact that most of the errors would be caught during UAT.

The script takes a parameter from the command line which sets the language. Depending on the language the script initializes the proper old -> new mappings for files and folders.  It then replaces any references to the old file with new file name.

require “ftools”

class FileRename

# Define old – > new mappings for english
@@hashEn = Hash.new
@@hashEn["old-name.jsp"] = “new-name.jsp”

# Define old – > new mappings for french
@@hashFr = Hash.new
@@hashFr["old-name.jsp"] = “new-name.jsp”

@@changes = 0

# Initialize hash of old – new file names for jsp’s
def initialize(lang)
if lang.eql? “en”
@oldPrefix = “ie-en”
@newPrefix = “eng”
@oldNewHash = @@hashEn
elsif lang.eql? “fr”
@oldPrefix = “ie-fr”
@newPrefix = “fra”
@oldNewHash = @@hashFr
end
end

# Copy contents of old files to new files
def renameFiles
@logFile = File.new(“file_renaming.log”, “w”)
@oldNewHash.each do |key, value|
oldFile = File.new(@oldPrefix + “/” + key, “r”)        # Open old file in READ-ONLY mode
newFile = File.new(@newPrefix + “/” + value, “w”)    # Open new file in WRITE mode
oldFile.each_line do |line|
newFile.puts line            # Copy each line in old file to new file
end
newFile.close                    # Close new file
oldFile.close                    # Close old file
@logFile.puts “Rename ” + key + ” to ” + value
File.delete(oldFile.path)        # Delete old file
end
@logFile.close
end

# Replace references to old file names with new file names in the newly created jsp’s
def replaceOldFileNames(line)
newLine = String.new
newLine.replace(line)            # create a new copy of the line passed
@oldNewHash.each do |key, value|
newLine.gsub!(@oldPrefix + “/” + key, @newPrefix + “/” + value)        # replace absolute references to old jsp names such as /ie-en/old.jsp with /eng/new.jsp
newLine.gsub!(“/” + key, “/” + value)    # replace <a href=”../old.jsp” with <a href=”../new.jsp”
newLine.gsub!(“\”" + key, “\”" + value)    #replace <a href=”old.jsp” with <a href=”new.jsp”
newLine.gsub!(key, value) # replace old.jsp with new.jsp, ONLY needed for .properties files, comment out otherwise
end
newLine            # return the new string
end

# Replace links to old jsp’s in files in current directory and its sub-folders
def replaceOldLinks(ext)
@logFile = File.new(“file_renaming.log”, “a”)
validFiles = File.join(“**”, ext)        # File filter to recursilve extract all files for the given extension
Dir.glob(validFiles) do |path|            # Scan every file
puts “Fixing paths in ” + path.to_s
@logFile.puts “Fixing paths in ” + path.to_s
aFile = File.new(path, “r”)        # open the file in read-only
lines = aFile.readlines            # Read all lines from file
aFile.close

aFile = File.new(path, “w”)        # open the file in write now and scan each line for old links and write it back
lineNumber = 1
lines.each do |line|
newLine = replaceOldFileNames(line)
unless newLine.eql? line
@logFile.puts “Changed line number ” + lineNumber.to_s + ” in ” + path.to_s
@@changes += 1
end
aFile.puts newLine
lineNumber += 1
end
aFile.close
puts “Done fixing paths in ” + path.to_s
@logFile.puts “Done fixing paths in ” + path.to_s
end
@logFile.puts “Total changes made : ” + @@changes.to_s
@logFile.close
end
end

langCode = ARGV[0] == nil ? “en” : ARGV[0]
fr = FileRename.new(langCode)
fr.renameFiles
fr.replaceOldLinks(“*.html”)
fr.replaceOldLinks(“*.htm”)
fr.replaceOldLinks(“*.jspf”)
fr.replaceOldLinks(“*.jsp”)
fr.replaceOldLinks(“*.properties”)

I tried to update to the latest Rails 2.1.0 version but got an error when trying to update the activesupport gem. So I decided to update each module manually and still got an error when updating activesupport

C:\InstantRails>gem update activesupport -y
Updating installed gems…
Bulk updating Gem source index for: http://gems.rubyforge.org
Updating metadata for 13 gems from http://gems.rubyonrails.org
………….
complete
Attempting remote update of activesupport
INFO:  `gem install -y` is now default and will be removed
INFO:  use –ignore-dependencies to install only the gems you list
ERROR:  Error installing activesupport:
        invalid gem format for C:/InstantRails/ruby/lib/ruby/gems/1.8/cache/activesupport-2.1.0.gem

The fix is to download the gem manually from here, save it and run gem install activesupport-2.1.0.gem.

Read this tutorial to get the scratch effect going. I have modified my version to draw a square instead of a circle and check when the user is “almost” done scratching the card.

To check to see if the user is almost done scratching the card I use a 2D array to represent a grid on the background image. The 2D array size or Grid size is the same as your image size, the first dimension being x or width and second dimension being y or height. By default all the values in the array are set to undefined in Actionscript. Therefore, everytime we draw a square we set the value at those indices in the array to be true. For example, a user clicks at (20,20) and we draw a square from (10,10) (10,30) (30,10) (30,30). We will set the value for all the indexes from x = 10 to 30 and y = 10 to 30 to true in our 2D array.

When a user lifts the mouse button we check to see his/her progress. Doing this is just iterating through the 2D array and checking to see which pixels have been set to true. You can check to see if all the pixels are cleared or just the ones around the middle or even have a percentage of the total pixels set to true.

Here’s the source code:

this.createEmptyMovieClip(‘mask_mc’,0);

bg_mc.setMask(mask_mc);

var contor:Number=0;

var imageHeight:Number = 300;    //Image Height

var imageWidth:Number = 400;    //Image Width

var totalPixels:Number = imageHeight * imageWidth;    //Total number of pixels in this image

var almostDonePixelCount:Number = 0.8 * totalPixels;    //Number of pixels that have to be scratched to consider entire picture to be scratched. 80% of all pixels have to be scratched now.

var pixelArray:Array = create2DArray(imageWidth, imageHeight);        // 2D array to represent the image grid. For my image x is 400 and y is 300

// function create2DArray creates a 2D array with x = width and y = height

function create2DArray(width:Number, height:Number):Array

{

var array:Array = Array();

for(var index:Number = 0; index < width; ++index)

array.push(Array(height));

return array;

}

// function drawSquare draws a square on mask_mc MovieClip of sides length 2*r and having center to mouse coordinates.

function drawSquare(mask_mc:MovieClip):Void

{

var r:Number = 20;    // this controls how far off the clicked point we start drawing a square

var xcenter:Number = _xmouse;    // x co-ordinates of user click

var ycenter:Number = _ymouse;    // y co-ordinates of user click

//Draw the square

mask_mc.moveTo(xcenter-r, ycenter-r);

mask_mc.beginFill(0×000000, 100);

mask_mc.lineTo(xcenter-r, ycenter-r);

mask_mc.lineTo(xcenter+r, ycenter-r);

mask_mc.lineTo(xcenter+r, ycenter+r);

mask_mc.lineTo(xcenter-r, ycenter+r);

mask_mc.endFill();

//Loop to set pixels within the square to true

for(var xIndex:Number = xcenter-r; xIndex <= xcenter + r; xIndex++)

{

for(var yIndex:Number = ycenter-r; yIndex <= ycenter + r; yIndex++)

{

pixelArray[xIndex][yIndex] = true;

}

}

}

// function allScratched checks if the user has scratched off most of the upper layer which is set in almostDonePixelCount

function scratchDone():Boolean

{

var pixelsScratched:Number = 0;

for(var xIndex:Number = 0; xIndex < imageWidth; xIndex++)

{

for(var yIndex:Number = 0; yIndex < imageHeight; yIndex++)

{

if(pixelArray[xIndex][yIndex] == true)

{

pixelsScratched++;

}

}

}

return pixelsScratched > almostDonePixelCount;

}

// contor variable is used to hold if the mouse is pressed (contor is 1) or not (contor is 0)

this.onMouseDown=function()

{

contor=1;

}

// if the mouse is hold and moved then we draw a square on the mask_mc

this.onMouseMove=this.onEnterFrame=function()

{

if (contor==1)

{

drawSquare(mask_mc);

}

}

// check to see if the upper layer has been scratched

this.onMouseUp=function()

{

contor=0;

if(scratchDone())

{

trace(“Scratch Done”);

}

}

So the website goes down on my very first day of providing support for it. But whats even more interesting is the fact that it went down on the first day of vacation of the senior guy who sat next to me, trained me and used to support it.

I spent last week working on some JSP’s. This was my first time using them so I spent sometime doing trial and error. I also used some built-in ATG libraries for formatting content. Overall I found them to be cumbersome compared to View files (.rhtml) in Rails. Maybe this was because the system is old (still on Java 1.3) and I had to use old libraries. I am sure newer Java enterprise web frameworks like Spring make it much easier.

Next I used javascript for some select all and reset selection features. Although a powerful scripting language it can be tricky to debug.

While working with dates I came across the Calendar class in java and I absolutely love it ! You can do all kind of date manipulations, like adding a day or month to a date, comparing two dates etc etc.

I find it ironic how most programmers hate adding documentation, but then complaint about other software not having enough documentation. While working on some bugs I would thank whoever added comments and curse who didn’t. Therefore its only fair that I add comments to my code even though it takes more time. Its time well spent.

While working on some new features I was surprised on how much time I spent adding inline comments to the code. I had created 6-7 classes and was working on them in an ad-hoc manner, going back and forth adding methods, logic etc. I witnessed the following benifits:

  • No need to decode variable names, you know exactly what they do.
  • Javadoc for methods, saves you time from trying to figure out what it really does.
  • If you came up with a nifty little trick to solve a problem, it is essential you add comments or else it’ll take you longer to try and understand it later.

These benefits are multiplied when you work in teams. Code reviews take less time and there’s less chance of misinterpretation. The reviewer in frustration would just reject your changes or even worse approve them, only to find a bug later.

I believe implementation isnt fully complete unless you have finished documenting it. Yes Yes I know you have deadlines, but if you were to just add comments while writing code it wont take you any longer.

So in summary even though you might hate adding documentation, DO IT ! Someone will thank you later when they see your code.