<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-916695526519010627</id><updated>2011-08-06T06:55:14.407-07:00</updated><category term='svn shell one-liner'/><category term='xmpp perl crazy'/><title type='text'>Coding Cromulence</title><subtitle type='html'>Attempted coherence in programming, design, and project management</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://codingcromulence.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://codingcromulence.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Adam</name><uri>http://www.blogger.com/profile/02920251709791454221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://1.bp.blogspot.com/-iMNe8BV13uA/TVk6HkxflAI/AAAAAAAAAB0/Eag6bItFXIg/s220/adam.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>8</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-916695526519010627.post-4979320784313006292</id><published>2011-02-20T07:26:00.000-08:00</published><updated>2011-02-20T07:34:09.866-08:00</updated><title type='text'>DNA seen through the eyes of a coder</title><content type='html'>This has been around a while, but I just found it.  Explanation by a gentleman named Bert Hubert about the similarities between genetics and computer systems, with the analogy of DNA to source code as a focal point.  A very cool read: &lt;a href="http://ds9a.nl/amazing-dna/index.html"&gt;DNA seen through the eyes of a coder.&lt;/a&gt;  It seems to have been written 9 years ago (in 2002), and I'm no molecular biologist, but I think there have been some interesting advanced in &lt;a href="http://en.wikipedia.org/wiki/Epigenetics"&gt;epigenetics&lt;/a&gt; since then.  I wonder how that fits into the analogy?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/916695526519010627-4979320784313006292?l=codingcromulence.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingcromulence.blogspot.com/feeds/4979320784313006292/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=916695526519010627&amp;postID=4979320784313006292' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/4979320784313006292'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/4979320784313006292'/><link rel='alternate' type='text/html' href='http://codingcromulence.blogspot.com/2011/02/dna-seen-through-eyes-of-coder.html' title='DNA seen through the eyes of a coder'/><author><name>Adam</name><uri>http://www.blogger.com/profile/02920251709791454221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://1.bp.blogspot.com/-iMNe8BV13uA/TVk6HkxflAI/AAAAAAAAAB0/Eag6bItFXIg/s220/adam.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-916695526519010627.post-390731198631873963</id><published>2011-02-14T07:13:00.000-08:00</published><updated>2011-02-14T07:25:37.183-08:00</updated><title type='text'>ID numbers are not integers</title><content type='html'>Here at $work we use a numeric identifier called the &lt;strong&gt;UFID&lt;/strong&gt;.  It's an 8-digit string that uniquely identifies an individual related to the University.  It's protected information, according to $policy, so we have to be careful how we treat it.&lt;br /&gt;&lt;br /&gt;Note I called it an 8-digit string.  Unfortunately, I continue to see databases where this identifier is stored as an integer merely because it looks numeric.  That is to say, &lt;code&gt;int&lt;/code&gt; rather than &lt;code&gt;char&lt;/code&gt;.  This makes me sad.&lt;br /&gt;&lt;br /&gt;While it's possible to get into an involved academic discussion of why this is wrong, I'll just enumerate two simple rules for when to use a numeric type, such as integer:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;If the data is going to be used for arithmetic or statistical functions such as mean.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If the data serves as a counter, including auto-increment primary keys.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Note the second case is really an exception, and in the strictest of sense should not be allowed either.  But, in the spirit of pragmatism, it is easy enough to permit this very special, well-defined case without problems.  What does cause problems is using an integer type for a string field.  The most obvious problem is conversion from integer to string dropping the leading zeroes.&lt;br /&gt;&lt;br /&gt;Yes, it's possible to instruct most databases to return the data with leading zeroes prepended even though it's an integer.  That's an abomination.   Not only that, but if your ORM "knows" this is an integer, its internal representation will probably ditch that padding.  Now you have to make your code provide padding as well via &lt;code&gt;sprintf&lt;/code&gt; or similar.  Not very DRY.&lt;br /&gt;&lt;br /&gt;The data is not integer to begin with, you should not have to shoe-horn it into a type to which it does not belong.  What happens when one day they run out of IDs and start allowing letters in the ID?  ... &lt;br /&gt;&lt;br /&gt;Save yourself the worry.  Store identifiers as strings.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/916695526519010627-390731198631873963?l=codingcromulence.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingcromulence.blogspot.com/feeds/390731198631873963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=916695526519010627&amp;postID=390731198631873963' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/390731198631873963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/390731198631873963'/><link rel='alternate' type='text/html' href='http://codingcromulence.blogspot.com/2011/02/id-numbers-are-not-integers.html' title='ID numbers are not integers'/><author><name>Adam</name><uri>http://www.blogger.com/profile/02920251709791454221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://1.bp.blogspot.com/-iMNe8BV13uA/TVk6HkxflAI/AAAAAAAAAB0/Eag6bItFXIg/s220/adam.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-916695526519010627.post-373910489626417647</id><published>2010-01-27T18:08:00.000-08:00</published><updated>2011-02-14T07:12:28.733-08:00</updated><title type='text'>SVN LoC and churn metrics</title><content type='html'>Wrote a small Perl script to grab svn lines of code metrics (added, modified, deleted) and churn (added + modified) metrics, as well as number of files added, updated, or deletes by revision.  So you get a flat file output (fixed width) of timestamp, username, revision, lines added, modified, churned, deleted, files added, updated, deleted.&lt;br /&gt;&lt;br /&gt;Using that raw data it's a quick thing to parse it any way you like, such as applying math to predict your defect rate or simply graphing it over time.&lt;br /&gt;&lt;br /&gt;Here's the script, creatively named &lt;b&gt;svnloc&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;#!/usr/bin/perl&lt;br /&gt;use strict;&lt;br /&gt;use warnings;&lt;br /&gt;&lt;br /&gt;my $BARSIZE = 40; # Size of the progress bar&lt;br /&gt;my @statuses = qw(A U D);&lt;br /&gt;&lt;br /&gt;my $repo = shift();&lt;br /&gt;my $outfile = shift() || "./svnloc.txt";&lt;br /&gt;my $revision = shift();&lt;br /&gt;my $latest_rev;&lt;br /&gt;my %rev_users;&lt;br /&gt;my %rev_dates;&lt;br /&gt;my %rev_changes;&lt;br /&gt;my %rev_diff;&lt;br /&gt;&lt;br /&gt;if (not defined $repo or not -e $repo) {&lt;br /&gt;        print &amp;lt;&amp;lt;END_USAGE;&lt;br /&gt;Usage: svnloc repo [outfile [revision]]&lt;br /&gt;        repo      the path to the svn repository&lt;br /&gt;        outfile   the path for the output file, defaults to "./svnloc.txt"&lt;br /&gt;        revision  if specified, will append data for that revision to the output&lt;br /&gt;                  if not specified, all data for all revisions is obtained and the file&lt;br /&gt;                  is generated from scratch, overwriting the old file if it exists.&lt;br /&gt;END_USAGE&lt;br /&gt;        exit(1);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;my $bl_filename = "svnloc.blacklist";&lt;br /&gt;my @blacklist; # Don't count these revisions&lt;br /&gt;if (-e "svnloc.blacklist") {&lt;br /&gt;        open BL, $bl_filename;&lt;br /&gt;        do { chomp; push @blacklist, $_; } for (&amp;lt;BL&gt;);&lt;br /&gt;        close BL;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;if (defined $revision) {  #get info for our revision and append to output file&lt;br /&gt;        get_info($revision);&lt;br /&gt;        open OUTPUT, "&gt;&gt;$outfile";&lt;br /&gt;        output_line($revision);&lt;br /&gt;        close OUTPUT;&lt;br /&gt;} else { # generate output file from scratch&lt;br /&gt;        my $history = `svnlook history $repo`;&lt;br /&gt;        ($latest_rev) = $history =~ /(\d+)/s;&lt;br /&gt;        print "Latest revision: $latest_rev\n";&lt;br /&gt;&lt;br /&gt;        rev_loop("Obtaining revision information...",\&amp;get_info);&lt;br /&gt;&lt;br /&gt;        open OUTPUT, "&gt;$outfile";&lt;br /&gt;        printf OUTPUT ("%-20s%-18s%6s%7s%7s%5s%5s\n",&lt;br /&gt;            "Date","Username","Rev","Add","Mod","Chrn","Del",@statuses);&lt;br /&gt;&lt;br /&gt;        rev_loop("Generating outputfile ($outfile)...",\&amp;output_line);&lt;br /&gt;&lt;br /&gt;        close OUTPUT;&lt;br /&gt;}&lt;br /&gt;print "Finished.\n";&lt;br /&gt;&lt;br /&gt;sub get_info {&lt;br /&gt;        my $rev = shift;&lt;br /&gt;&lt;br /&gt;        my $info = `svnlook info -r $rev $repo`;&lt;br /&gt;        my ($user, $date) = split(/\n/,$info);&lt;br /&gt;        $rev_users{$rev} = $user;&lt;br /&gt;        $rev_dates{$rev} = $date;&lt;br /&gt;&lt;br /&gt;        my $changed = `svnlook changed -r $rev $repo`;&lt;br /&gt;        for my $s (split(/\n/,$changed)) {&lt;br /&gt;                my ($status) = substr($s,0,1);&lt;br /&gt;                $rev_changes{$rev}-&gt;{$status}++;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        my $diff = `svnlook diff -r $rev $repo`;&lt;br /&gt;&lt;br /&gt;        my ($added,$modified,$deleted,$temp_deleted) = (0)x4;&lt;br /&gt;        for my $line (split(/\n/,$diff)) {&lt;br /&gt;            my $c2 = substr($line,0,2);&lt;br /&gt;            my ($c) = substr($line,0,1);&lt;br /&gt;            next if ($c2 eq '--' || $c2 eq '++'); # ignore header lines&lt;br /&gt;            if ($c eq '-') {&lt;br /&gt;                $temp_deleted++;&lt;br /&gt;            } elsif ($c eq '+') {&lt;br /&gt;                if ($temp_deleted) {&lt;br /&gt;                    $temp_deleted--;&lt;br /&gt;                    $modified++;&lt;br /&gt;                } else {&lt;br /&gt;                    $added++;&lt;br /&gt;                }&lt;br /&gt;            } else {&lt;br /&gt;                $deleted += $temp_deleted;&lt;br /&gt;                $temp_deleted = 0;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        $rev_diff{$rev}-&gt;{added} = $added;&lt;br /&gt;        $rev_diff{$rev}-&gt;{modified} = $modified;&lt;br /&gt;        $rev_diff{$rev}-&gt;{churn} = $added + $modified;&lt;br /&gt;        $rev_diff{$rev}-&gt;{removed} = $deleted;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;sub output_line {&lt;br /&gt;        my $rev = shift;&lt;br /&gt;        no warnings 'uninitialized';&lt;br /&gt;        printf OUTPUT ("%20s%-18s%6d%7d%7d%7d%7d%5d%5d%5d\n",&lt;br /&gt;                substr($rev_dates{$rev},0,20),&lt;br /&gt;                $rev_users{$rev},&lt;br /&gt;                $rev,&lt;br /&gt;                $rev_diff{$rev}-&gt;{added},&lt;br /&gt;                $rev_diff{$rev}-&gt;{modified},&lt;br /&gt;                $rev_diff{$rev}-&gt;{churn},&lt;br /&gt;                $rev_diff{$rev}-&gt;{removed},&lt;br /&gt;                map { $rev_changes{$rev}-&gt;{$_} } @statuses);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;sub rev_loop {&lt;br /&gt;        my ($msg, $code) = @_;&lt;br /&gt;        my $progress;&lt;br /&gt;        print "$msg\n";&lt;br /&gt;        start_progress(\$progress);&lt;br /&gt;        for (1..$latest_rev) {&lt;br /&gt;                tick_progress(\$progress,$latest_rev);&lt;br /&gt;                next if (is_in($_,@blacklist));&lt;br /&gt;                $code-&gt;($_);&lt;br /&gt;        }&lt;br /&gt;        end_progress();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;sub start_progress {&lt;br /&gt;        my $progress = shift();&lt;br /&gt;        $$progress=0;&lt;br /&gt;        print "[" . (" " x $BARSIZE) . "]\r";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;sub tick_progress {&lt;br /&gt;        my $progress = shift();&lt;br /&gt;        my $max = shift();&lt;br /&gt;        my $ticks  = int(($$progress++/$max) * $BARSIZE);&lt;br /&gt;        my $spaces = $BARSIZE - $ticks;&lt;br /&gt;&lt;br /&gt;        printf "[" . ("=" x $ticks)&lt;br /&gt;                          . (" " x $spaces)&lt;br /&gt;                          . "] %-10s\r",$_;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;sub end_progress {&lt;br /&gt;        print "[" . ("=" x $BARSIZE) . "]\n\n";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;sub is_in {&lt;br /&gt;        my $item = shift;&lt;br /&gt;        my @list = @_;&lt;br /&gt;        my %seen;&lt;br /&gt;        @seen{@list} = (1) x scalar @list;&lt;br /&gt;        return $seen{$item};&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/916695526519010627-373910489626417647?l=codingcromulence.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingcromulence.blogspot.com/feeds/373910489626417647/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=916695526519010627&amp;postID=373910489626417647' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/373910489626417647'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/373910489626417647'/><link rel='alternate' type='text/html' href='http://codingcromulence.blogspot.com/2010/01/svn-loc-and-churn-metrics.html' title='SVN LoC and churn metrics'/><author><name>Adam</name><uri>http://www.blogger.com/profile/02920251709791454221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://1.bp.blogspot.com/-iMNe8BV13uA/TVk6HkxflAI/AAAAAAAAAB0/Eag6bItFXIg/s220/adam.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-916695526519010627.post-4101207929042508214</id><published>2009-02-11T05:10:00.000-08:00</published><updated>2009-02-11T16:58:19.198-08:00</updated><title type='text'>Vim Color Schemes</title><content type='html'>&lt;p&gt;Since deciding to get into &lt;a href="http://www.vim.org/"&gt;Vim&lt;/a&gt;, I seem to have wasted a considerable about of time on color schemes!  After discovering how to get 256 colors in my terminal window (&lt;em&gt;Hint&lt;/em&gt;: It works with &lt;a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/"&gt;PuTTY&lt;/a&gt; out of the box, just set your &lt;span style="font-family:courier new;"&gt;TERM&lt;/span&gt; environment variable to &lt;span style="font-family:courier new;"&gt;xterm-256color&lt;/span&gt;),  I decided to put together my own 256 color themes.  Well, sort of.  I actually just copied the code from the &lt;a href="http://dengmao.wordpress.com/2007/01/22/vim-color-scheme-wombat/"&gt;Wombat&lt;/a&gt; theme by Lars H. Nielsen and modified the colors.&lt;/p&gt;&lt;p&gt;This is the one I'm using by default, it's high-contrast and it seems to work very well for the Perl/Template/JS/HTML editing I am doing most of the time.  On account of the many bright colors (which I'm sure other people will think look ridiculous), I call this scheme &lt;b&gt;&lt;a href="http://sites.google.com/site/codingcromulence/Home/harlequin.vim"&gt;Harlequin&lt;/a&gt;&lt;/b&gt;.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;img src="http://sites.google.com/site/codingcromulence/Home/harlequin.png" /&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Inspired by the green/brown/white colors deployed by the marketing droids of the on-site coffee vendors where I work, I have created this scheme named &lt;b&gt;&lt;a href="http://sites.google.com/site/codingcromulence/Home/starbucks.vim"&gt;Starbucks&lt;/a&gt;&lt;/b&gt;.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;img src="http://sites.google.com/site/codingcromulence/Home/starbucks.png" /&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;And of course to round things out and bring some balance, here is this truly evil dark-side scheme which I call &lt;b&gt;&lt;a href="http://sites.google.com/site/codingcromulence/Home/magma.vim"&gt;Magma&lt;/a&gt;&lt;/b&gt;.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;img src="http://sites.google.com/site/codingcromulence/Home/magma.png" /&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;Of course, there are those who say that Starbucks&amp;#0174; is the true evil, but I digress...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/916695526519010627-4101207929042508214?l=codingcromulence.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingcromulence.blogspot.com/feeds/4101207929042508214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=916695526519010627&amp;postID=4101207929042508214' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/4101207929042508214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/4101207929042508214'/><link rel='alternate' type='text/html' href='http://codingcromulence.blogspot.com/2009/02/vim-color-schemes.html' title='Vim Color Schemes'/><author><name>Adam</name><uri>http://www.blogger.com/profile/02920251709791454221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://1.bp.blogspot.com/-iMNe8BV13uA/TVk6HkxflAI/AAAAAAAAAB0/Eag6bItFXIg/s220/adam.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-916695526519010627.post-2643970324744756344</id><published>2009-02-06T08:48:00.000-08:00</published><updated>2009-02-06T08:57:06.956-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='svn shell one-liner'/><title type='text'>SVN deletion goodness</title><content type='html'>&lt;p&gt;The process of getting a project which was not under version control into SVN can be a chore.  Usually the lack of source control has forced the creation of loads of temporary and backup files with silly names.  The easiest thing to do is to simply import the whole mess into the repository, and then go back and clean it up later.&lt;/p&gt;&lt;p&gt;That's what I was doing earlier this morning.  I checked out a copy and started trimming, and by the time I was ready to commit, I realized I had been accidentally deleting files directly in the shell instead of using &lt;span style="font-family:courier new;"&gt;svn delete&lt;/span&gt;.  Oops!  Now I have to go back re-delete them.  But they're gone, and there were probably a hundred files and directories removed.  Won't that be a huge pain?&lt;/p&gt;&lt;p&gt;Not really.  A little shell one-liner will take care of it for you:&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:courier new;"&gt;svn status | grep '^!' | awk '{print $2}' | xargs svn del&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Run that from the root of your working copy, and it will do the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Give you the status of all the files and directories in your working copy compared with the repository.&lt;/li&gt;&lt;li&gt;Extract only those lines which start with &lt;span style="font-family:courier new;"&gt;!&lt;/span&gt;, which is &lt;span style="font-family:courier new;"&gt;svn status&lt;/span&gt;'s way of saying &lt;em&gt;"Oh noes, I can't find that one!"&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Feed those lines into awk so that it can get the second item on the line, the path.&lt;/li&gt;&lt;li&gt;Use xargs to run &lt;span style="font-family:courier new;"&gt;svn del&lt;/span&gt; on each of those paths.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Now all of your deletes will be properly reflected in the repo at the next commit.  Phew!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/916695526519010627-2643970324744756344?l=codingcromulence.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingcromulence.blogspot.com/feeds/2643970324744756344/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=916695526519010627&amp;postID=2643970324744756344' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/2643970324744756344'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/2643970324744756344'/><link rel='alternate' type='text/html' href='http://codingcromulence.blogspot.com/2009/02/svn-deletion-goodness.html' title='SVN deletion goodness'/><author><name>Adam</name><uri>http://www.blogger.com/profile/02920251709791454221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://1.bp.blogspot.com/-iMNe8BV13uA/TVk6HkxflAI/AAAAAAAAAB0/Eag6bItFXIg/s220/adam.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-916695526519010627.post-7682386701833898811</id><published>2009-01-16T11:40:00.000-08:00</published><updated>2009-01-16T12:10:17.667-08:00</updated><title type='text'>Chat Hacking, Part II</title><content type='html'>&lt;p&gt;So, got it to work.  Turns out we were both right:  Danny was correct in that we weren't using the JSJaC library properly, and I was right in that the server detected our switcheroo and didn't want to talk to us.&lt;/p&gt;&lt;p&gt;Firstly, we discovered there's an internal (but public) method on JSJaC's connection object called&lt;span style="font-family:courier new;"&gt; inherit&lt;/span&gt;, which allows you to utilize an existing http-bind session when you fire up the chat engine.  That turns out to be the right way to do things.  It expects a number of arguments passed in an argument object, three of which are vital to convincing the server that you are who you say you are: &lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;sid&lt;/strong&gt;: (Session ID) This is generated by the server and sent in the connection phase, we already had this working fine in Perl, so no problems here.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;key: &lt;/strong&gt;The key is a hex-encoded sha1 hash sequence, and it's used to verify that each subsequent request comes from the same client.  How?  Well, each new key is the sha1 hash of the previous key.  If you transmit the wrong key, the server barfs on you.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;rid: &lt;/strong&gt;(Request ID) This is simply a sequential number, but it's important with respect to the key.  If your request ID is not in lock-step with your key sequence, the server again will barf on you.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Getting the key right was the trickiest part, but not too bad.  Essentially, JSJaC by default generates a list of 16 keys at a time to use.  Since we weren't initializing the session using JSJaC, we instead had Perl initialize those keys and use the first few to establish a connection.  The key list then gets injected in to the web page where JSJaC can pick it up.  As long as there are no fencepost errors, the whole sequence proceeds along without a hitch, and the server happily talks to the JavaScript.&lt;/p&gt;&lt;p&gt;It's actually not as fragile as we were afraid it might be, since the session/key/rid setup makes sure that each session is unique and can only be utilized in JS by the CGI that initiated it.  And it makes sure that all the credentials required for login are safely tucked away on the server side, where clients can't see them at all.  Now all we have to do is tweak (read: restrict or rewrite) the JWChat interface a little to give it fewer features, and add a bit of conversation logging, and we've basically accomplished what we set out to do.  &lt;/p&gt;&lt;p&gt;Which means we can have anonymous clients talking to our people on our internal chat server, in a controlled environment and without compromising any credentials.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/916695526519010627-7682386701833898811?l=codingcromulence.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingcromulence.blogspot.com/feeds/7682386701833898811/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=916695526519010627&amp;postID=7682386701833898811' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/7682386701833898811'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/7682386701833898811'/><link rel='alternate' type='text/html' href='http://codingcromulence.blogspot.com/2009/01/chat-hacking-part-ii.html' title='Chat Hacking, Part II'/><author><name>Adam</name><uri>http://www.blogger.com/profile/02920251709791454221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://1.bp.blogspot.com/-iMNe8BV13uA/TVk6HkxflAI/AAAAAAAAAB0/Eag6bItFXIg/s220/adam.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-916695526519010627.post-9173491754989552473</id><published>2009-01-15T18:03:00.000-08:00</published><updated>2009-01-16T04:39:59.909-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xmpp perl crazy'/><title type='text'>Chat Hacking</title><content type='html'>&lt;p&gt;In a blog post by the same name on our (internal) bug tracking system, my colleague Danny describes our (thus far futile) efforts to create an unholy union between JWChat and a cgi script.  That's right, we're trying to do XMPP Chat via the web, which means we're trying to utilize something which implements XEP-0124 (BOSH).&lt;/p&gt;&lt;p&gt;The point is that we want outsiders to be able to talk to our people who are using the internal XMPP server.  Trouble is, the XMPP server requires local credentials to log in (LDAP), so we need them to log in via dummy accounts.  But, we don't want to just hand out the user/pass to those dummy accounts by putting them into the JavaScript source of a web-based chat client.  Hence, the madness begins.&lt;/p&gt;&lt;p&gt;One of our system administrators had the lovely idea of using a CGI script to initiate the connection, keeping our credentials server-side, over an http-bind proxy.  Then we'd hand the session data to our JavaScript client-side, again through the proxy, hopefull unbeknownst to the chat server.  &lt;/p&gt;&lt;p&gt;Yeah, it's not going so well.  As in, not at all.  For one thing, there are no existing libraries to implement XEP-0124 in Perl.  We can do regular socket connections, sure, but not BOSH.  So, we were faced with either using a library from another language (like JavaScript or C++) inside a Perl wrapper, or just "faking" the BOSH process by sending some pre-formatted XML over an LWP connection to the http-bind port.  &lt;/p&gt;&lt;p&gt;Well, that part actually worked (suprisingly enough), we can connect server side and get session info.  But as of right now, we can't inject that info into the JavaScript client side and have it pick up the ball, so to speak.  I think maybe the server somehow can tell that we've pulled a fast one on it, and it's not willing to talk to the client masquerading as the server.   Danny thinks we probably just haven't covered all of our bases in initializing the JavaScript chat engine.  We're proceeding under the assumption that he's right, and it's still possible to get this to work.&lt;/p&gt;&lt;p&gt;Only time will tell if this crazy scheme of ours can work.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/916695526519010627-9173491754989552473?l=codingcromulence.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingcromulence.blogspot.com/feeds/9173491754989552473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=916695526519010627&amp;postID=9173491754989552473' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/9173491754989552473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/9173491754989552473'/><link rel='alternate' type='text/html' href='http://codingcromulence.blogspot.com/2009/01/chat-hacking.html' title='Chat Hacking'/><author><name>Adam</name><uri>http://www.blogger.com/profile/02920251709791454221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://1.bp.blogspot.com/-iMNe8BV13uA/TVk6HkxflAI/AAAAAAAAAB0/Eag6bItFXIg/s220/adam.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-916695526519010627.post-8534821030311924765</id><published>2008-12-06T17:37:00.001-08:00</published><updated>2008-12-06T18:25:12.451-08:00</updated><title type='text'>By Moradin's Hammer!</title><content type='html'>&lt;p&gt;We're just now getting around to formalizing our builds using a standard based around &lt;a href="http://search.cpan.org/~kwilliams/Module-Build/"&gt;Module::Build&lt;/a&gt;.  Our subclass is named &lt;a href="http://en.wikipedia.org/wiki/Moradin"&gt;Moradin&lt;/a&gt;, keeping with our "tradition" of using D&amp;amp;D deities.  Basically, we've implemented support for the following on top of the regular Module::Build functionality:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Downloading and installing other custom modules from their distribution source in our local SVN repository&lt;/li&gt;&lt;li&gt;Auto-installing other required modules from CPAN based on local CPAN config for the project&lt;/li&gt;&lt;li&gt;Building a directory tree structure which is different from that of a regular perl module, including adding empty folders necessary for app functionality, creating symlinks, and copying build files to arbitrary destinations.&lt;/li&gt;&lt;li&gt;Applying an appropriate (as determined at build time) 'use lib' line to installed executables (such as .cgi scripts)&lt;/li&gt;&lt;li&gt;Arbitrary string replacement inside source files&lt;/li&gt;&lt;li&gt;Applying permissions based on matching rules&lt;/li&gt;&lt;li&gt;Applying SELinux security context&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We also hope to implement additional features such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Database structure setup/update/rollback (in conjunction with appropriate database update files with our source revisions)&lt;/li&gt;&lt;li&gt;Generated source files for CSS management&lt;/li&gt;&lt;li&gt;Support for build automation (which we haven't really gotten to, yet)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Danny and I had a minor disagreement about implementation; we are supplying a minimal build script example with the module, and I was about to put code which prompted for required data in that script.  Danny objected strongly to this saying it was wrong, but we had a difficult back-and-forth as to why I shouldn't do that apart from propriety.  After some reflection, I realize there are a couple of very good reasons to do it "his" way (one of which he brought up at the time but I dismissed), by putting that logic in the module:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Makes it so the user can't inadvertently remove functionality that needs to be present.&lt;/li&gt;&lt;li&gt;The build script should only require the user to supply data.  If the user wishes to write some code to get that data, that's fine.  But the key is that we &lt;strong&gt;separate the m&lt;/strong&gt;&lt;strong&gt;odule's logic from the data on which it operates.&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Anyway, I just thought I'd put that out there.  Still have some fun to go.  In addition to implementing the above, I'm having trouble figuring out how to complete the unit tests in a comprehensive way.  In particular the automated module install bits.&lt;/p&gt;&lt;p&gt;After all, am I supposed to install a CPAN module to test that functionality?  And then remove it again?  Should I stat every file when testing permission setting to make sure those permissions are set properly?  Is there a tool that can do it for me?  And maybe most significantly, is there a way I can actually unit test that functionality without doing a complete test installation &lt;em&gt;for each test?&lt;/em&gt;&lt;/p&gt;&lt;p&gt;When we figure out how to proceed, I'll post the plan.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/916695526519010627-8534821030311924765?l=codingcromulence.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codingcromulence.blogspot.com/feeds/8534821030311924765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=916695526519010627&amp;postID=8534821030311924765' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/8534821030311924765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/916695526519010627/posts/default/8534821030311924765'/><link rel='alternate' type='text/html' href='http://codingcromulence.blogspot.com/2008/12/by-moradins-hammer.html' title='By Moradin&apos;s Hammer!'/><author><name>Adam</name><uri>http://www.blogger.com/profile/02920251709791454221</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://1.bp.blogspot.com/-iMNe8BV13uA/TVk6HkxflAI/AAAAAAAAAB0/Eag6bItFXIg/s220/adam.jpg'/></author><thr:total>0</thr:total></entry></feed>
