Tuesday, December 31, 2013

Hard Link Directories in OSX

Ever had one of those days when things that worked fine just don't anymore?

Today, for some reason I have yet to discover, my soft linked directories (created with ln -s <SOURCE> <TARGET>) became unsearchable by the bash find command.

I had to resort to creating a C program, in order to use it's link function.

SOURCE


#include <unistd.h>
#include <stdio.h>
#include <string.h>
/*
Purpose:  To create a hard link to a directory
Install:  gcc hln.c -o hln
*/
int main(int argc, char* argv[]) {
    //Make sure we have the right arguments
    if (argc != 3) {
        fprintf(stderr,"Usage:   hln <SOURCE_DIRECTORY> <TARGET_DIRECTORY>\n\n");
        fprintf(stderr,"To later unlink target directory:   hln -u <TARGET_DIRECTORY>\n");
        return 1;
    }
    int ret = 0;
    if(strcmp(argv[1], "-u") == 0)
        ret = unlink(argv[2]);
    else
        ret = link(argv[1],argv[2]);
         
    if (ret != 0)
        perror("hardlink");
    return ret;
}

Compile it

After compiling, you'll want to copy the hln executable to a suitable directory, like /usr/bin.

$ gcc hln.c -o hln

All good

Now, I can create (hard) linked directories and unlink them (with the -u option).

Thursday, December 12, 2013

Displaying Escaped Characters Better with Back References

Have you ever received strings with literal characters, like the one below, that just look like a lot of noise?

Start with this


"body"=>"\"{\\\"class\\\":\\\"MigrationMgr\\\",\\\"args\\\":[null,\\\"migrate_product\\\",1,3]}\""

A little better

Open up an IRB or Ruby command console and when you use puts, it looks a little better...

[1] pry(main)> s=< "\"\"{\\\"class\\\":\\\"MigrationMgr\\\",\\\"args\\\":[null,\\\"migrate_product\\\",1,3]}\"\"\n"
[2] pry(main)> puts s
""{\"class\":\"MigrationMgr\",\"args\":[null,\"migrate_product\",1,3]}""
But it's still not as clean as you'd like.

Back References to the Rescue

First, create the back references

back_refs = {
   '\"' => '',
   "\\" => ''''
}


Next, use gsub and regex groupings to replace the back references with the desired, simpler characters.

hash_var['body'].gsub(/([\"\\])/) { back_refs[$1] }.gsub(',', ', ')

Result

Now, we have nicely formatted data that we can use for display purposes.

{class:MigrationMgr, args:[null, migrate_product, 1, 3]}

Wednesday, December 4, 2013

Upgrade from PostgreSQL 9.2.4 to 9.3.1 on OSX 10.9 and Centos 6.5

While testing a new async infrastructure, which requires more than 20 database connections, I ran out of connections and looked into upgrading PostgreSQL. The default setting for max_connections (found in /usr/local/var/postgres/postgresql.conf) is 100.

Ends up the upgrade from 9.2.4 to 9.3.1 is worthwhile for me since it greatly reduces System V shared memory requirements.

The upgrade was not time consuming, but required attention to details.

Here's what worked for me on OSX 10.9


$ brew upgrade postgresql

Keep Install Notes


==> Upgrading 1 outdated package, with result:
postgresql 9.3.1
==> Upgrading postgresql
==> Downloading http://ftp.postgresql.org/pub/source/v9.3.1/postgresql-9.3.1.tar.bz2
######################################################################## 100.0%
==> Patching
patching file contrib/uuid-ossp/uuid-ossp.c
==> ./configure --prefix=/usr/local/Cellar/postgresql/9.3.1 --datadir=/usr/local/Cellar/postgresql/9.3.1/share/postgresql --docdir=/usr/local/Cellar/postgresql/9.3.1/share/doc/postgresql --enable-thread-safety --with-bonjour --with-gssapi --with-krb5 --with-ldap --wi
==> make install-world
==> Caveats
initdb /usr/local/var/postgres -E utf8    # create a database cluster
postgres -D /usr/local/var/postgres       # serve that database
PGDATA=/usr/local/var/postgres postgres   # …alternatively

If builds of PostgreSQL 9 are failing and you have version 8.x installed,
you may need to remove the previous version first. See:
  https://github.com/mxcl/homebrew/issues/issue/2510

To migrate existing data from a previous major version (pre-9.3) of PostgreSQL, see:
  http://www.postgresql.org/docs/9.3/static/upgrading.html

When installing the postgres gem, including ARCHFLAGS is recommended:
  ARCHFLAGS="-arch x86_64" gem install pg

To install gems without sudo, see the Homebrew wiki.

To have launchd start postgresql at login:
    ln -sfv /usr/local/opt/postgresql/*.plist ~/Library/LaunchAgents
Then to load postgresql now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
Or, if you don't want/need launchctl, you can just run:
    pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start
==> Summary
🍺  /usr/local/Cellar/postgresql/9.3.1: 2919 files, 39M, built in 78 seconds


Initialize new 9.3 database


$ cd /usr/local/var
$ mkdir postgres9.3
$ initdb /usr/local/var/postgres9.3 -E utf8

Migrate Data to New 9.3 Format


$ pg_upgrade \
-d /usr/local/var/postgres \
-D /usr/local/var/postgres9.3 \
-b /usr/local/Cellar/postgresql/9.2.4/bin/ \
-B /usr/local/Cellar/postgresql/9.3.1/bin/ \
-v

Create Link to Generic PostgreSQL directory


$ mv postgres postgres9.2.4
$ ln -s /usr/local/var/postgres9.3 /usr/local/var/postgres
$ psql postgres -c "select version()"

Increase System Memory for PostgreSQL


$ sudo sysctl -w kern.sysv.shmall=65536
$ sudo sysctl -w kern.sysv.shmmax=16777216

Create /etc/sysctl.conf file with following values


kern.sysv.shmall=65536
kern.sysv.shmmax=16777216

Reboot computer

Create postgres user


createuser -s -r postgres

Start postgreSQL

Note: I don't use launchd

$ pg_ctl -D /usr/local/var/postgres -l $PGDATA/server.log start

Verify PostgreSQL 9.3 Works


$ psql postgres -c "select version()"

Vacuum db


$ vacuumdb --all

Reinstall PG gem

Note: Uninstall the gem on the system command line and let Bundle Install reinstall your project's required version, which for me was version 0.17.

$ gem uninstall pg



$ cd <RAILS_PROJECT_DIR>

Possible Error

If you get the following...


Building native extensions. This could take a while... ERROR: Error installing pg:

ERROR: Failed to build gem native extension.


...then run the following after uninstalling the pg gem:


$ brew install apple-gcc42
$ gcc --version


A typical OSX DevTools install won't be enough, but you can run it anyway:

xcode-select --install

Install Command Line Tools (OS X Mavericks)

If you use PostgreSQL.App, you'll need to install Command Line Tools (OS X Mavericks) for Xcode - Late October 2013

Do the following:
  • $ gem uninstall pg
  • $ xcode
  • From Xcode menu: Xcode > Open Developer Tool > More Developer Tools... > Choose "Command Line Tools (OS X Mavericks) for Xcode - Late October 2013"
  • Run the installation program
  • $ gem install pg

Update following in Gemfile


gem 'pg', '~> 0.17'

Bundle Install


$ bundle package --all


Verify PostgreSQL 9.3 works with Rails

Change directory to your favorite Rails project open the Rails Console and do some ActiveRecord queries.

Cleanup


$ brew cleanup postgresql
$ rm -rf /usr/local/var/postgres9.2.4

Notes

  • Since directories have been moved and linked, do not run delete_old_cluster.sh
  • When tweaking system memory setting, keep in mind that SHMMAX must be an exact multiple of 4096
  • Special thanks to Matt Brictson, referenced below
  • This update will likely break your PostgreSQL admin tool like pgadmin
  • The pg_upgrade application only migrates the public schema.
  • Upgrade to lastest (v1.18.1) pgAdmin app, but backup your data first.
  • Do not remove the old version (9.2.4) until you are certain that all data has been migrated successfully.

Centos Install

This is still a work in progress...

$ su -
# cd softwares
# curl -O http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-centos93-9.3-1.noarch.rpm
# rpm -ivh pgdg-centos93-9.3-1.noarch.rpm
# yum install postgresql93 postgresql93-server postgresql93-contrib
# service postgresql-9.3 initdb
# chkconfig postgresql-9.3 on
# yum erase postgresql92*
# reboot

References

http://blog.55minutes.com/2013/09/postgresql-93-brew-upgrade http://www.postgresql.org/docs/current/static/release-9-3.html http://www.postgresql.org/docs/8.4/static/kernel-resources.html http://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server http://postgresapp.com http://www.pgadmin.org/download/macosx.php

Tuesday, November 12, 2013

Create Graphs with Ruby, CSS and HTML

My high schooler had a homework lab assignment that required creating graphs.

She asked if I could help her by creating a nice looking graph to put in her composition lab book.

I thought, why not? ...and why not use some Ruby, CSS and HTML to make it happen?

Here's one of the graphs

CM
RAIN
TEMP
°C
36 36
34 32
32 28
30 24
28 20
26 16
24 12
22 8
20 4
18 0
16 -4
14 -8
12 -12
10 -16
8 -20
6 -24
4 -28
2 -32
0 -36


























Since I prefer to save trees I decided to fit 4 per page (see Final Results below).

Next year, when my soon-to-be high schooler asks me for help with the same lab, I'll refer her to this post :-)

Perhaps, it will help somebody else... The source can easily be modified to create a lot similar kinds of graphs.

Enjoy!

Ruby code for calculating labels

 ary2 = 36.downto(0).select{|n| 0 == n % 2 }  
 ary4 = 36.downto(-36).select{|n| 0 == n % 4 }  
 (0..ary2.size - 1).each {|i| puts "<tr> <td>#{ary2[i]}</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>#{ary4[i]}</td> </tr>"}  

HTML Source

This is one time that it was just easier to copy and paste...
 <html>  
 <head>  
   <style type="text/css">  
    table,th, td  
    {  
      border: 1px solid black;  
      margin: 0;  
      padding: 0;  
    }  
    table {  
      border-spacing: 0;  
      border-collapse: collapse;  
      border: 0px;  
    }  
    tr {  
      height: 20px;  
    }  
    td {  
      width: 10px;  
      text-align: right;  
      padding-right: 10px;  
    }  
    .first-col {  
      width: 70px;  
      text-align: right;  
      border: 0px;  
      padding-right: 20px;  
    }  
    .last-col {  
      border: 0px;  
    }  
    #header-row td {  
      border: 0px;  
    }  
    #header-row .label {  
      position: relative;  
      left: 4px;  
    }  
    #row1-col1 {  
      float: left;  
      margin-bottom: 40px;  
    }  
    #row1-col2 {  
      float: left;  
      margin-bottom: 40px;  
    }  
    #row2-col1 {  
      float: left  
    }  
    #row2-col2 {  
      float: left  
    }  
   </style>  
 </head>  
 <body>  
   
   
 <div id="row1-col1">  
   <table>  
    <tr id="header-row">  
      <td>CM<br>RAIN</td> <td colspan="12"></td> <td class="label">TEMP<br>&deg;C</td>  
    </tr>  
    <tr> <td class="first-col">36</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">36</td> </tr>  
    <tr> <td class="first-col">34</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">32</td> </tr>  
    <tr> <td class="first-col">32</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">28</td> </tr>  
    <tr> <td class="first-col">30</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">24</td> </tr>  
    <tr> <td class="first-col">28</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">20</td> </tr>  
    <tr> <td class="first-col">26</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">16</td> </tr>  
    <tr> <td class="first-col">24</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">12</td> </tr>  
    <tr> <td class="first-col">22</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">8</td> </tr>  
    <tr> <td class="first-col">20</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">4</td> </tr>  
    <tr> <td class="first-col">18</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">0</td> </tr>  
    <tr> <td class="first-col">16</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-4</td> </tr>  
    <tr> <td class="first-col">14</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-8</td> </tr>  
    <tr> <td class="first-col">12</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-12</td> </tr>  
    <tr> <td class="first-col">10</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-16</td> </tr>  
    <tr> <td class="first-col">8</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-20</td> </tr>  
    <tr> <td class="first-col">6</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-24</td> </tr>  
    <tr> <td class="first-col">4</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-28</td> </tr>  
    <tr> <td class="first-col">2</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-32</td> </tr>  
    <tr> <td class="first-col">0</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-36</td> </tr>  
   </table>  
 </div>  
   
   
 <div id="row1-col2">  
   <table>  
    <tr id="header-row">  
      <td>CM<br>RAIN</td> <td colspan="12"></td> <td class="label">TEMP<br>&deg;C</td>  
    </tr>  
    <tr> <td class="first-col">36</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">36</td> </tr>  
    <tr> <td class="first-col">34</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">32</td> </tr>  
    <tr> <td class="first-col">32</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">28</td> </tr>  
    <tr> <td class="first-col">30</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">24</td> </tr>  
    <tr> <td class="first-col">28</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">20</td> </tr>  
    <tr> <td class="first-col">26</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">16</td> </tr>  
    <tr> <td class="first-col">24</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">12</td> </tr>  
    <tr> <td class="first-col">22</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">8</td> </tr>  
    <tr> <td class="first-col">20</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">4</td> </tr>  
    <tr> <td class="first-col">18</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">0</td> </tr>  
    <tr> <td class="first-col">16</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-4</td> </tr>  
    <tr> <td class="first-col">14</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-8</td> </tr>  
    <tr> <td class="first-col">12</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-12</td> </tr>  
    <tr> <td class="first-col">10</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-16</td> </tr>  
    <tr> <td class="first-col">8</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-20</td> </tr>  
    <tr> <td class="first-col">6</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-24</td> </tr>  
    <tr> <td class="first-col">4</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-28</td> </tr>  
    <tr> <td class="first-col">2</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-32</td> </tr>  
    <tr> <td class="first-col">0</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-36</td> </tr>  
   </table>  
 </div>  
   
 <br>  
 <div id="row2-col1">  
   <table>  
    <tr id="header-row">  
      <td>CM<br>RAIN</td> <td colspan="12"></td> <td class="label">TEMP<br>&deg;C</td>  
    </tr>  
    <tr> <td class="first-col">36</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">36</td> </tr>  
    <tr> <td class="first-col">34</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">32</td> </tr>  
    <tr> <td class="first-col">32</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">28</td> </tr>  
    <tr> <td class="first-col">30</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">24</td> </tr>  
    <tr> <td class="first-col">28</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">20</td> </tr>  
    <tr> <td class="first-col">26</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">16</td> </tr>  
    <tr> <td class="first-col">24</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">12</td> </tr>  
    <tr> <td class="first-col">22</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">8</td> </tr>  
    <tr> <td class="first-col">20</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">4</td> </tr>  
    <tr> <td class="first-col">18</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">0</td> </tr>  
    <tr> <td class="first-col">16</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-4</td> </tr>  
    <tr> <td class="first-col">14</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-8</td> </tr>  
    <tr> <td class="first-col">12</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-12</td> </tr>  
    <tr> <td class="first-col">10</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-16</td> </tr>  
    <tr> <td class="first-col">8</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-20</td> </tr>  
    <tr> <td class="first-col">6</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-24</td> </tr>  
    <tr> <td class="first-col">4</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-28</td> </tr>  
    <tr> <td class="first-col">2</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-32</td> </tr>  
    <tr> <td class="first-col">0</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-36</td> </tr>  
   </table>  
 </div>  
   
   
 <div id="row2-col2">  
   <table>  
    <tr id="header-row">  
      <td>CM<br>RAIN</td> <td colspan="12"></td> <td class="label">TEMP<br>&deg;C</td>  
    </tr>  
    <tr> <td class="first-col">36</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">36</td> </tr>  
    <tr> <td class="first-col">34</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">32</td> </tr>  
    <tr> <td class="first-col">32</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">28</td> </tr>  
    <tr> <td class="first-col">30</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">24</td> </tr>  
    <tr> <td class="first-col">28</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">20</td> </tr>  
    <tr> <td class="first-col">26</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">16</td> </tr>  
    <tr> <td class="first-col">24</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">12</td> </tr>  
    <tr> <td class="first-col">22</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">8</td> </tr>  
    <tr> <td class="first-col">20</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">4</td> </tr>  
    <tr> <td class="first-col">18</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">0</td> </tr>  
    <tr> <td class="first-col">16</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-4</td> </tr>  
    <tr> <td class="first-col">14</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-8</td> </tr>  
    <tr> <td class="first-col">12</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-12</td> </tr>  
    <tr> <td class="first-col">10</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-16</td> </tr>  
    <tr> <td class="first-col">8</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-20</td> </tr>  
    <tr> <td class="first-col">6</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-24</td> </tr>  
    <tr> <td class="first-col">4</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-28</td> </tr>  
    <tr> <td class="first-col">2</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-32</td> </tr>  
    <tr> <td class="first-col">0</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class="last-col">-36</td> </tr>  
   </table>  
 </div>  
   
   
 </body>  
 </html>  

Final Result

Wednesday, October 23, 2013

IBM Demos Some of My Work on Youtube.com

I just noticed that IBM published a demo of some of the work I did on the IBM Network Protection NextGen IPS - Management Dashboard.

Screen shot of Dashboard



What is this thing?

This is the management dashboard for a Next Generation Intrusion Protection Device.

The NextGen IPS device like a fancy firewall that understands the content in corporate IP traffic which allows corporate management to monitor and control traffic by user (or contrived) groups, application type and more.

Here are some examples of what this device can do (and what you can manage via this dashboard):
  • A manager could allow all employees to view Facebook, but not allow the Finance group to use Facebook Chat application.
  • A manager could view which applications are hogging the corporate bandwidth
  • A manager could see who's trying to send credit card or SSN information across the network

References

http://www.youtube.com/watch?v=NB6Wea3xMGg
http://www-03.ibm.com/software/products/us/en/network-ips/
http://hackmageddon.com/tag/web-application-firewall/
http://lexsheehan.blogspot.com/2013/10/mavericks-osx-109-upgrade.html

Mavericks (OSX 10.9) Upgrade

I just upgraded one of my computers to OSX 10.9.

I'll post issues related to this upgrade that pop up as time permits...

Caution

I'd recommend your making a full backup before upgrading.

Expectations

Expect the installation to
  • take longer than advertised
  • require reinstall of your Java runtime
  • replace SecondBar functionality (if you have multiple monitors)

Install Java Runtime

If you get an error message like, "you need a java se 6 runtime" when opening your mac apps, just install a Java Runtime from: http://support.apple.com/kb/DL1572?viewlocale=en_US


Install Nokogiri

Error when installing nokogiri gem


Installing nokogiri (1.5.4)
Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

        /Users/lex/.rbenv/versions/1.9.3-p448/bin/ruby extconf.rb
checking for libxml/parser.h... no
-----
libxml2 is missing.  please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.
-----
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
	--with-opt-dir
	--without-opt-dir
	--with-opt-include
	--without-opt-include=${opt-dir}/include
	--with-opt-lib
	--without-opt-lib=${opt-dir}/lib
	--with-make-prog
	--without-make-prog
	--srcdir=.
	--curdir
	--ruby=/Users/lex/.rbenv/versions/1.9.3-p448/bin/ruby
	--with-zlib-dir
	--without-zlib-dir
	--with-zlib-include
	--without-zlib-include=${zlib-dir}/include
	--with-zlib-lib
	--without-zlib-lib=${zlib-dir}/lib
	--with-iconv-dir
	--without-iconv-dir
	--with-iconv-include
	--without-iconv-include=${iconv-dir}/include
	--with-iconv-lib
	--without-iconv-lib=${iconv-dir}/lib
	--with-xml2-dir
	--without-xml2-dir
	--with-xml2-include
	--without-xml2-include=${xml2-dir}/include
	--with-xml2-lib
	--without-xml2-lib=${xml2-dir}/lib
	--with-xslt-dir
	--without-xslt-dir
	--with-xslt-include
	--without-xslt-include=${xslt-dir}/include
	--with-xslt-lib
	--without-xslt-lib=${xslt-dir}/lib
	--with-libxslt-config
	--without-libxslt-config
	--with-pkg-config
	--without-pkg-config
	--with-libxml-2.0-config
	--without-libxml-2.0-config
	--with-libiconv-config
	--without-libiconv-config


Gem files will remain installed in /Users/lex/.rbenv/versions/1.9.3-p448/lib/ruby/gems/1.9.1/gems/nokogiri-1.5.4 for inspection.
Results logged to /Users/lex/.rbenv/versions/1.9.3-p448/lib/ruby/gems/1.9.1/gems/nokogiri-1.5.4/ext/nokogiri/gem_make.out

An error occurred while installing nokogiri (1.5.4), and Bundler cannot continue.
Make sure that `gem install nokogiri -v '1.5.4'` succeeds before bundling.
You can try upgrading homebrew ...

$ brew update
$ brew upgrade
...and you may feel good your progress, and try to reinstall nokogiri and it's dependencies...

brew install libxml2 libxslt
brew link libxml2 libxslt
gem install nokogiri
But you'll probably get the following message: Warning: No developer tools installed. You should install the Command Line Tools. Run `xcode-select --install` to install them. But you'll probably still have probably still get the following error:

clang: error: invalid arch name '-arch -D_REENTRANT=1'
error: command 'clang' failed with exit status 1
Finally, when you install nokogiri with xcode dependencies using the following you may profit:

$ gem install nokogiri -- --with-xml2-include=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/libxml2

Update Gemfile

Be sure to reference the proper version of nokogiri in your Gemfile.

gem 'nokogiri', '~> 1.6'


New Features

Here are a few new features to checkout
  • Upgrade existing apps (Calendar, Finder, Mail/gmail archiving, Notifications)
  • New apps (iBooks, Maps, iCloud Keychain)
  • Power Saving Functionality (Timer Coalescing, Compressed Memory, App Nap)
See the full list here: https://developer.apple.com/osx/whats-new/

Activity Monitor

I'm a fan of the new graphics in the new Activity Monitor's Network graphic...


...largely, because Apple uses the same design that I originally implemented in the IBM Network Protection NextGen IPS (NextGen) - Management Dashboard.

It's easy to see spikes in traffic and the load coming across the wire.

IBM NextGen dashboard traffic widget

The current IBM NextGen dashboard traffic widget implementation is more like what Apple used in pre-10.9 Activity Montior Network graphs:


For a short demo of the BM Network Protection NextGen IPS (NextGen) - Management Dashboard see http://www.youtube.com/watch?v=NB6Wea3xMGg

Fine Grained Control of Which Apps Get Accessibility Features

Unlike previous versions of OS X, which used a universal checkbox in an “all or nothing” approach, the new functionality in Mavericks allows users to individually choose which apps can gain control of the system.

If you use an application like CheatSheets or SizeUp, just add the app in the OSX System Preferences > Security & Privacy > Privacy > Accessibility dialog.

Thoughts

I ran into the nokogiri problem mentioned above, which is not bad and was easy to fix.

Given that this is a FREE upgrade and the tendency of Apple to not support legacy systems for long, upgrading is a no brainer.

References

http://lexsheehan.blogspot.com/2013/10/ibm-demos-some-of-my-work-on-youtubecom.html
http://www.tekrevue.com/2013/06/25/how-to-enable-access-for-assistive-devices-in-os-x-mavericks/
http://www.macrumors.com/roundup/os-x/

Thursday, October 17, 2013

Convert Spaces to Tabs in one whack

Working with CoffeeScript is a pleasure.

It is easy to create beautiful CoffeeScript code since blocks are determined by indentation.

However, that can be a two edged sword.

If you mix your SPACES with TABs, which can be easy to do in a team development environment, you are in for a world of hurt.

Fortunately, there are techniques for dealing with this TAB/SPACE issue.

Here are a few things I configure in my IDE for working with CoffeeScript files:
  • Use TAB characters instead of SPACES
  • 3 characters per TAB
  • Show Whitespaces

For non CoffeeScript files

For all other file types (not CoffeeScript) I use SPACES
  • Use SPACES for TABs
  • 3 characters (spaces) per TAB
  • Do not show Whitespaces

The reason for using SPACEs, e.g., for Ruby files, is because when copy/pasting from IDE to console you'll often get errors when using TABs.

Bash script used by find command

When I have a number of files that have spaces in them, here's how a convert the lot of them from SPACES to TABs:

unexpand_cmd


FILENAME=$1
CURRENT_PROCESS_NAME=$$
TMP_FILENAME="/tmp/$(basename $0).$CURRENT_PROCESS_NAME.tmp"
if [ -z $FILENAME ]; then
 echo "Usage:  unexpand_cmd "
 exit 1
fi
unexpand -t 3 $FILENAME > $TMP_FILENAME && cat $TMP_FILENAME > $FILENAME && rm $TMP_FILENAME

Word of caution

This assumes that the source files use units of 3 spaces for indentation.

If your source files use other units, you should replace the "3" with what ever unit your files use.

Set the execute bit


$ chmod +x ./unexpand_cmd

p.s. You'll want to save this file in a directory that's on your path.
p.s.s. Since this modifies your source files, please make a full backup before running this.

Run find command and execute unexpand_cmd on each


$ find app/assets/javascripts -name "*js.coffee" -type f -exec unexpand_cmd {} \;

Notes

When you see "Soft Tabs" in your IDE preferences, that means "Use 3 spaces instead of 1 tab character", assuming you have set your tab to 3 spaces.

Tuesday, October 15, 2013

Install Textmate Bundle for CoffeeScript

Here's all you need to do to get the CoffeeScript bundle installed for TextMate (version 1.5.11)

Example


$ cd /Applications/TextMate.app/Contents/SharedSupport/Bundle
$ git clone git://github.com/jashkenas/coffee-script-tmbundle CoffeeScriptBundle.tmbundle

Tuesday, September 17, 2013

Highlighting Lines of Code in Github

Ever needed to share some code with somebody and point them to the lines of interest?

Here's how to do that if the code is in github:

Summary

Just add
#L[STARTING_LINE_NO]-[ENDING_LINE_NO]
to the end of the original url.

Example


https://github.com/angular/angular.js/blob/master/test/ApiSpecs.js#L6-16

Monday, September 2, 2013

21 CFR Part 11 and the SDLC

In this posting, I will attempt to address the challenges posed by the FDA 21 CFR Part 11, which is a law aimed at ensuring that companies implement good business practices, in their developing software efforts to meet said requirements.

Background Definitions

21 CFR Part 11

Title 21 is the portion of the Code of Federal Regulations (CFR) that governs food and drugs within the United States for the Food and Drug Administration (FDA), the Drug Enforcement Administration (DEA), and the Office of National Drug Control Policy (ONDCP).

21 CFR Part 11 addresses Standard Operating Procedures (SOPs), Medical Device Quality Standards Regulation (QSR), Good Clinical Practices and Good Manufacturing Practices.

SDLC

The systems life cycle (SLC) is a methodology used to describe the process for building information systems.

The Systems development life cycle (SDLC) is a process used by a systems analyst to develop an information system, training, and user (stakeholder) ownership. The SDLC aims to produce a high quality system that meets or exceeds customer expectations, reaches completion within times and cost estimates, works effectively and efficiently in the current and planned Information Technology infrastructure, and is inexpensive to maintain and cost-effective to enhance.

Following a SLDC in order to develop a 21 CFR Part 11 regulated software application is essential because its very intent is to provide a very deliberate, structured and methodical way, reiterating each stage of the life cycle. The SDLC describes the different stages involved in the software development project from the drawing board/planning, implementation, testing, deployment, through the completion of the project.

Project managers benefit from the process, tools and project artifacts generated as a result of following an SDLC. The project life cycle integrates the project management and SDLC cycles with the activities directly associated with system deployment and operation.

The Hurtle

The FDA recommends that application development teams take an integrated approach to their software development lifecycle (SDLC), which combines risk management strategies with principles for software validation.

But developing software for medical devices that complies with the FDA's Quality System regulation is necessary hurtle for companies that want to compete in the medical/FDA-regulated industry.

This hurtle is not near as "complicated" as some make it seem.

It basically means that your software needs to be engineered properly with complete access control and audit capabilities.

Experience Matters

Since I have well over 10 years of enterprise application security software development experience and have worked with some of the most talented software engineers in the world, these requirements seem easy/natural to me.

I enjoyed over 10 years of hard core software application development prior to joining IBM.

In my first project at IBM, I researched and developed security components for SmartCard LifeCycle Management System using IBM's flagship e-business security software, Tivoli Policy Director (TPD), which allows organizations to create secure relationships among customers, partners, suppliers and employees.

I focused on enterprise-security-related software application development projects, such as:
  • Software Engineer I architected and implemented various design patterns for Web Identity (formerly IBM Registration) is the standard service for IBM external applications that register, authenticate, or profile external web-based customers, business partners, suppliers, and other users of ibm.com. WI performs Single Sign On, Global Session Persistence, and User Identity and Profiling and Access Control functions.
  • Security Consultant Implemented IBM.COM Single Sign On (SSO) prototype for IBM’s BT/CIO office and the Tivoli PDT.
  • Security Architect & Lead Developer Designed and implemented Voice over IP (VoIP) security architecture for a fortune 5 telecommunications company. System components (order entry, billing and account management ) were presented to users (customer facing and a customer service rep (CSR)) via web based portal application. Designed corporate messaging infrastructure to manage application entitlements (user creation and role management).
  • Lead Security Application Developer Performed analysis, design and implementation phase of enterprise Authentication and Authorization infrastructure replacement project for fortune 5 financial corporation.
  • SOA Security Architect Produced detailed SOA Security Architecture deliverable for Service Oriented Architecture Methodology (SOMA) engagement focused on contract, pricing and rebate functionality for a distributor of medical products, equipment, billing services and pharmaceuticals.

I later transferred from IBM Global Business Services to the IBM Software group to help develop the administrative UI for the IBM Next Generation Intrusion Protection Appliance.

Software Development Methodologies


Waterfall Methodology

Early in my programming career, I worked for several fortune 500 companies that followed the Waterfall software development methodology:

What always happened on those projects was the business analysts would take in inordinate amount of time to fully define everything about the system, even though none of them had any relevant software development experience, and produced a tome of a design document, which was supposed to serve as a reference for developers' requirements.

At that point, the project was always behind schedule and my team took over.

I would spend a day or two reading through that wordy tome and make my own (useful) design documents.

Next, I would find the subject matter experts (SMEs) to validate my designs and begin a series of design/implement/evaluate sessions, very much like the Agile process, but light on testing. (The need for testing, especially in a team environment that uses a constant integration (CI) server is a topic for another day :-)

The fact was that the project management office would be freaking out as the customer began to tighten the rope of leniency as they began to get concerned that the project was falling behind since they had not "seen" anything.

Process and methodology at that moment was always thrown out the door as the project manager (PM) would scream, "Just get 'er done."



Rational Unified Process

Prior to joining IBM I worked at an internet consulting company iXL where we followed the Rational Unified Process (RUP) of software development.

The purpose of following RUP software engineering methodology is to provide a disciplined approach to assigning tasks and responsibilities within a development organization. Its goal is to ensure the production of high-quality software that meets the needs of its end-users, within a predictable schedule and budget. RUP is “use-case driven, architecture-centric, and incremental and iterative”.

RUP Phases

Here's a high level view of what the RUP phases:

Here's what a typical RUP plan would look like:

Objectives

  • Improve the integrity of the commitments made by SDLC by defining the business process by which decisions are made.
  • Minimize product changes (churn) once the decision is made to initiate a project.
  • Define a common framework for communicating project status in terms of business commitments
  • Enforce appropriate minimum standards on entry and exit criteria for each applicable project lifecycle phase.
  • Define milestones for project process metrics: Cycle Time & Quality


RUP was a big improvement over the Waterfall methodology, but many software development projects would still fall behind schedule, leaving PM's standing in the lurch. Then, along came Agile...

Agile software development

In 2001, the Agile software development methodology became popular, which is based on the Agile Manifesto:
We are uncovering better ways of developing software by doing it and 
helping others do it. Through this work we have come to value:

Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan

That is, while there is value in the items on the right, 
we value the items on the left more.
Agile software development is a group of software development methods based on iterative and incremental development, where requirements and solutions evolve through collaboration between self-organizing, cross-functional teams.

Agile provides well-defined planning events and a "time-boxed", iterative approach to development that project managers like.

The trouble with many Agile projects is that the focus is on cranking out stories (aka Use Cases in RUP parlance) and their associated features with less emphasis on code quality.

However, the 21 CFR Part 11 and the FDA, in general, is all about quality...

... and that's a problem.

Some Software requirements for 21 CFR Part 11

  • System validation to make sure performance of the system is accurate and reliable.
  • System should be able to produce accurate/complete record copies that are readable both in human and electronic forms.
  • Record protection to ensure they are accurate and able to be retrieved easily.
  • System access should have strong controls to prevent unauthorized access.
  • Must have time stamped audit trails.
  • Operational system checks must be in place to ensure proper sequencing of steps and events.
  • Authority checks must be in place to make sure that only the authorized personnel can get into the system.
  • Proper controls on system documentation
  • Complete and accurate record retention practices
  • Written policies must be produced that hold everyone accountable for any actions via their electronic signature.

Other Systems Impacted By 21 CFR Part 11 Requirements

  • Employee Training
  • Document Management
  • Change Control

Out of Compliance

If you get audited and found wanting, you will get a 483 (FDA violation) citation.

A 483 is issued when investigators observe any significant objectionable conditions. FDA investigators are trained to ensure that each observation noted on the 483 is clear, specific and significant. The observations are cited when in an investigator’s judgment these conditions or practices observed indicate that an FDA-regulated target is in violation of FDA’s requirements.

Example 483 violations

http://www.fda.gov/Food/RecallsOutbreaksEmergencies/Outbreaks/ucm224855.htm
http://www.fda.gov/AboutFDA/CentersOffices/OfficeofGlobalRegulatoryOperationsandPolicy/ORA/ORAElectronicReadingRoom/ucm061813.htm
http://www.fda.gov/downloads/ICECI/EnforcementActions/WarningLetters/1999/UCM067262.pdf
http://www.fda.gov/ICECI/EnforcementActions/WarningLetters/2005/ucm075385.htm

I think it's safe to say that no business owner would ever want to receive one of those.

Summary

All projects need to be performed and delivered under certain constraints. Traditionally, these constraints have been listed as "scope," "time," and "cost". These are also referred to as the "Project Management Triangle". You cannot change any one constraint without affecting the other two.

These three constraints are often competing constraints: increased scope typically means increased time and increased cost, a tight time constraint could mean increased costs and reduced scope, and a tight budget could mean increased time and reduced scope.


Quality is of utmost importance in projects that intend to meet 21 CFR Part 11 requirements.

Therefore, unless the technical resources that implement the application care a lot about the quality of their code, are seasoned enough to understand what good code means, and are willing to enforce coding standards and stand up to PM's that are always pushing to "get 'er done", the risk of delivering software that complies with FDA regulations will always exceed an acceptable level, regardless of the chosen software development methodology (and some form of agile is the way to go).

Sunday, September 1, 2013

lexsheehan.blogspot.com has over 25,000 page views!

Started blogging less than a year ago and now have over 25,000 page views ...


Thank you for reading my stuff!

Lex

A Predictable Reaction to a Software Development Estimation with Full Disclosure


I was recently asked, by a close friend, to look at a simple web application that starts with a large page of nicely styled questions, followed by another page of questions and a results page, full of analysis and a well designed summary pie chart graphic.

Here was my response:

If your question is how "hard", I'd say "not hard".
If your question is how much would it cost and how long would it take? ... then how "hard" it is depends.
As you know, delivering software involves the interaction between business policy/process, data, technology and people.
Consider:
1. Is the algorithm for determining the scores well defined?
(Would the question/answer > bucket be provided in the form of a spreadsheet file (or would the programmer be given written descriptions and have to type everything in from scratch?)
2. How much flexibility is there on the final graph image?
(Looks like they have created a whole lot of static images, one for each possible answer. Another solution would be to have a javascript driven/dynamic graph, which would likely be the way I'd lean.)
3. Are you storing the results or simply generating the resulting score for display on the web site at the end of the survey?
(This is a big one. If the answer is just generate the answer and not record anything, other than possibly an email then this could all be done in javascript, i.e., in the web browser...no backend database required.)
4. Is deployment infrastructure in place?
(Do you have a server for this application to run on already?)
5. Is this part of a bigger solution?
(Need to integrate this with other systems? Do you need to share the responses or "customers" with other systems? Do you need to automate emails to these customers? Etc.)
6. How engineered does the solution need to be?
(Most apps are developed using and agile methodology, which requires a continuous integration server that monitors for code updates, validates that new coded does not break the build, which implies all work has test code (unit, functional, integration)... I would not think this would not require multiple developers, which greatly simplifies things.
7. How many change requests will be involved?
(Will the user acceptance testing be reasonable or will there with a lot of "I know that's what you thought I said, but I was thinking of something more like this here...So, do it over, again."
8. Has a technology and/or development toolset been pre chosen?
(Does it matter what language is written in or what tools are used to develop the web app?)

[SUMMARY]

The result you get when developing software is largely dependent on the team doing the work.
I would say you're looking at any where from 4 hours to 4 weeks [or more], depending on your answers above, whether requirements will change and the experience of who's doing the work.
... [ some discussion of cost ] ...
I also included one of my favorite comic strips:

Response to full-disclosure-estimation

I think I have figured out how to do this through excel.


Wednesday, August 14, 2013

Strategy Design Pattern Using Ruby

Back when I worked in Global Business Services at IBM, I participated in a Design Patterns user group.

It was moderated by a super smart dude, "Coop".

We studied the patterns found in the Gang of Four book and worked through coding examples in Java each time we met.

Ends up most of us had been using proven patterns in our code, but didn't realize they had names.

Object oriented programming (OOP) is great when used properly and that's where design patterns come in...

A day ago during an interview, I was asked to implement a Ten Pin Bowling Game Scoring Algorithm using Ruby. So, I put the Strategy Pattern to work to solve it and landed an awesome job.

I was given the following rules...

1. 10 Frames
2. Each frame throw the ball 2 times *
3. 10 pins
4. Each pin is worth 1 point


# Spare

7 3 -> Frame 1: 15
5 0 -> Frame 2: 15 + 5 -> 20


# Strike

10 0 -> Frame 1: 18
5 3 -> Frame 2: 18 + 8 -> 26


... and asked to code that along with Rspec test cases, which I will cover in another blog posting.

After a short pair programming session I was able to take some time to look at the problem and saw that Objects involved:
  • BowlingGame (Client)
  • Frame (StrategyBase)
  • Spare (ConcreteStrategy)
  • Strike (ConcreteStrategy)
A Spare Is A Frame, ditto for a Strike.

How to Play

After the BowlingGame is initialized with the Frame, Spare and Strike classes you call the roll method from 12 to 22 times, depending on what was rolled and then finally call the score method to find out how well you did.

Sample Code


class BowlingGame
    NUMBER_OF_FRAMES = 10

    # BowlingGame.new(Strike, Spare, Frame)
    def initialize(*policies)
        @policies = policies  # Each Frame type has different policies used for scoring
        @rolls = []           # Array of rolls (number of pins knocked down)
    end

    # Add number of pins knocked down to @rolls array
    def roll(pins_knocked_down)
        @rolls << pins_knocked_down
    end

    # Sum each frame's score for total
    # See: http://lexsheehan.blogspot.com/search?q=functional+programming
    def score
        frames.reduce(0) { |sum, frame| sum + frame.score }
    end

    # A frame consists of two rolls, unless first roll is a Strike (special rules for last frame)
    def frames
        roll_index = 0
        frames = []
        NUMBER_OF_FRAMES.times do |i|
            frames << frame(roll_index)
            # Polymorphism: Frames could be a Strike or a Spare, both of which have a number_of_rolls method
            # Spare has 2 rolls and Strike has 1 roll
            is_last_frame = i.eql?(NUMBER_OF_FRAMES - 1)  # Last frame gets bonus roll(s)
            roll_index += frames.last.number_of_rolls(is_last_frame)
        end
        frames
    end

    # Use strategy pattern to select appropriate Frame type (regular Frame, Spare or Strike)
    # See:  http://en.wikipedia.org/wiki/Strategy_pattern
    def frame(roll_index)
        @policies.each do |policy|
            # Select frame based on policy in found? methods
            return policy.new(@rolls, roll_index) if policy.found?(@rolls, roll_index)
        end
    end

end

class Frame
    def initialize(rolls, roll_index)
        @rolls = rolls            # Array of rolls, where each equates to number of pins knocked down in that roll
        @roll_index = roll_index  # The first roll index in this frame
    end

    def score
        @rolls[@roll_index] + @rolls[@roll_index + 1]  # Sum of both rolls in frame
    end

    def self.found?(rolls, roll_index)
        true  # If neither Spare or Strike then this is a regular Frame
    end

    def number_of_rolls(is_last_frame)
        2  # Default to 2 rolls (regardless of whether it's last frame)
    end
end

class Strike < Frame
    # In the frame where strike is rolled, add 10 to number of pins knocked down in next two rolls
    def score
        10 + @rolls[@roll_index + 1] + @rolls[@roll_index + 2]
    end

    # Strike strategy found when number of pins knocked down in first rolls of frame equals 10
    def self.found?(rolls, roll_index)
        rolls[roll_index].eql?(10)
    end

    # Override Frame type's number_of_rolls method
    def number_of_rolls(is_last_frame)
        # If the last frame starts with a strike, we get 2 bonus rolls
        is_last_frame ? 3 : 1
    end
end

A Few Test Cases


    it "scores 0 with a gutter ball game" do
        20.times { game.roll 0 }
        expect(game.score).to eq(0)
    end

    it "scores 300 with all strikes" do
        12.times { game.roll 10 }
        expect(game.score).to eq(300)
    end

    it "scores 150 with all 5's" do
        21.times { game.roll 5 }
        expect(game.score).to eq(150)
    end

References

Get the code and a nice README with more references here: https://github.com/l3x/lex-dp-strategy-ruby