<?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-1158653486009791669</id><updated>2012-02-16T01:08:35.297-08:00</updated><category term='lightsabers'/><category term='pipelines'/><category term='numpty'/><category term='DBIx::Class'/><category term='badminton'/><category term='bug'/><category term='development'/><category term='bingo'/><category term='software east'/><category term='haggis'/><category term='mental health'/><category term='Roles'/><category term='array'/><category term='test'/><category term='inheritance'/><category term='year review'/><category term='L-mode'/><category term='git'/><category term='AI'/><category term='teacher'/><category term='shift'/><category term='spam'/><category term='Net::Stomp'/><category term='attributes'/><category term='List-MoreUtils'/><category term='crontab'/><category term='stuck jobs'/><category term='xp'/><category term='Duran Duran'/><category term='sort'/><category term='barcamb'/><category term='Reaction'/><category term='system'/><category term='burns'/><category term='mysql'/><category term='refactoring'/><category term='breakfast'/><category term='WoW'/><category term='perlcritic'/><category term='readdir'/><category term='ActiveMQ'/><category term='gaming'/><category term='banana'/><category term='PAUSE'/><category term='Weetabix'/><category term='perl pomodoro_technique development agile'/><category term='changing'/><category term='software'/><category term='panic'/><category term='coding'/><category term='TeX'/><category term='dev'/><category term='perl Moose MooseX JSON'/><category term='List-AllUtils'/><category term='testing'/><category term='pomodoro technique'/><category term='gd'/><category term='blogging'/><category term='Moose::Role'/><category term='webapps'/><category term='svn'/><category term='rw/ro'/><category term='MooseX'/><category term='agile cambridge'/><category term='LSF'/><category term='MVC'/><category term='Readonly'/><category term='Perl6::Slurp'/><category term='World of Warcraft'/><category term='concat'/><category term='perl'/><category term='SQLLite'/><category term='Selenium'/><category term='qx//'/><category term='eval'/><category term='Class::Std'/><category term='github'/><category term='benchmark'/><category term='Carp'/><category term='cornflakes'/><category term='human genome project'/><category term='Test::MockObject'/><category term='daemon'/><category term='Blizzard'/><category term='unconference'/><category term='agile'/><category term='whisky'/><category term='ironman'/><category term='MooseX::Storage'/><category term='course'/><category term='Food'/><category term='agile development'/><category term='PBP'/><category term='clearpress'/><category term='RabbitMQ'/><category term='error messages'/><category term='pragprog'/><category term='catalyst'/><category term='perl catalyst Moose MooseX'/><category term='code'/><category term='markup'/><category term='teaching'/><category term='MooseX::AttributeCloner'/><category term='database'/><category term='Pragmatic Thinking and Learning'/><category term='apache'/><category term='debug'/><category term='recovery'/><category term='MooseX::Getopt'/><category term='R-mode'/><category term='write to file'/><category term='cpan'/><category term='test::builder'/><category term='programming'/><category term='objects'/><category term='wii'/><category term='agilecam'/><category term='OO'/><category term='API'/><category term='STOMP'/><category term='life'/><category term='Module::Install'/><category term='regex'/><category term='phishing'/><category term='0 but true'/><category term='pragmatic'/><category term='scrum'/><category term='bio'/><category term='pluggable'/><category term='index'/><category term='career'/><category term='LaTeX'/><category term='Moose'/><category term='Module::Build'/><title type='text'>Vampire Software Development</title><subtitle type='html'>Thoughts and releases on my experiences and abilities as a software developer</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default?start-index=101&amp;max-results=100'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>108</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1499867109737755871</id><published>2012-01-13T02:19:00.000-08:00</published><updated>2012-01-13T02:19:47.441-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='markup'/><category scheme='http://www.blogger.com/atom/ns#' term='TeX'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='clearpress'/><title type='text'>TeX - an odd markup language</title><content type='html'>&lt;p&gt;Why do I say odd?&lt;/p&gt;&lt;p&gt;Over the last couple of iterations at $work, I have wanted to produce some automated PDF reports from out of our database, with the option to pull in some additional images should they be available.&lt;/p&gt;&lt;p&gt;Now, I'm using the perl ClearPress MVC framework for the application, and it is nice and friendly in that I can Template my TeX and push that through pdflatex to get the PDF.&lt;/p&gt;&lt;p&gt;No problems so far. The data is therefore obtained and pushed into the template in exactly the same way as with HTML.&lt;/p&gt;&lt;p&gt;A nice simple bit of TeX markup and I get a nicely formatted PDF report yes?&lt;/p&gt;&lt;p&gt;No.&lt;/p&gt;&lt;p&gt;Unlike scripting languages and HTML, it doesn't seem to be doing everything top to bottom. Some of my tables end up above Section headings, images too.&lt;/p&gt;&lt;p&gt;What?&lt;/p&gt;&lt;p&gt;Now, prior to this I have had no experience with TeX in any flavour before.&lt;br /&gt;I start by looking into main uses of TeX. For those not in the know, it is a markup designed for Mathematical equation rendering, and is popular with Journal entries as with simple markup given with the paper, they can change the formatting to suit the Journal.&lt;/p&gt;&lt;p&gt;So I start looking around the Google querying how to do formatting. The answers to queries I have seem to always take me to forums, as opposed to any sort of manual. The first answer took me to the info in the paragraph above, basically saying that we want to do your formatting, don't do it yourself. And there was an interesting discussion in a forum with one person giving a way of telling your table/image 'Put yourself there!' and another asking 'Why on Earth would you want to limit your table/image to the one position'.&lt;/p&gt;&lt;p&gt;Also, another one was escaping special characters. Eventually, I came to the following link &lt;a href="http://www.google.co.uk/url?sa=t&amp;rct=j&amp;q=latex%20text%20symbols&amp;source=web&amp;cd=2&amp;sqi=2&amp;ved=0CCkQFjAB&amp;url=http%3A%2F%2Fwww.tex.ac.uk%2Ftex-archive%2Finfo%2Fsymbols%2Fcomprehensive%2Fsymbols-a4.pdf&amp;ei=Ne4PT_62F4SFhQeXja23Ag&amp;usg=AFQjCNEFsjyle6iPx01QsUa60KrbeGBhYw&amp;cad=rja"&gt;The Comprehensive LaTeX Symbol List - The UK TeX Archive&lt;/a&gt; (pdf download) but not after having been pushed around forum answers again.&lt;/p&gt;&lt;p&gt;It goes to show that forums are great places for information (although, sometimes the question to Google often brings up that in favour of the manual :) ).&lt;/p&gt;&lt;p&gt;But again, why do I say odd?&lt;/p&gt;&lt;p&gt;Well it seems that TeX doesn't do Markup in quite the logical way I would do Markup (which is the way HTML does it). HTML says start at the top, display the marked up code from top to bottom, unless the markup (combined with css/javascript) tells me to do it otherwise. TeX seems to say, Start at the top, but float everything to where I think it is best, unless the markup specifies something different. This seems to keep the order of the same types of items, but doesn't necessarily keep the items where written.&lt;/p&gt;&lt;p&gt;Also, special characters are not always represented with a textual markup version, or an actual escaped version. i.e.&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;  \ needs to be \textbackslash, not \\ (as that actually means newline)&lt;br /&gt;  # needs to be \#, there isn't a \texthash, and \hash doesn't always work&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;I think that, had I known this at the start, I'd have then had a better idea of how to query Google for info. TeX is clearly a useful Markup, and by the looks of it, very extensible (I pulled in 'grffile' in order for it to deal with spaces in filenames and paths), but it isn't as quick to pick up as html.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1499867109737755871?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1499867109737755871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1499867109737755871' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1499867109737755871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1499867109737755871'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2012/01/tex-odd-markup-language.html' title='TeX - an odd markup language'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-2085700343319327858</id><published>2011-12-03T00:48:00.000-08:00</published><updated>2011-12-03T00:51:55.709-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webapps'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='clearpress'/><title type='text'>Test::WWW::Selenium::Conversion::IDE</title><content type='html'>&lt;pre&gt;At $work we have a LIMS system running as a web app. Nothing unusual there. However, we had a user experience a bug which had only be seen once before, and seemed impossible to recreate.&lt;br /&gt;&lt;br /&gt;It seemed to come from asynchronous Ajax updates to one field on the user session.&lt;br /&gt;&lt;br /&gt;My first thought, was how to recreate this bug which only comes up rarely. On my development version, I couldn't get it to happen. So, I decided to have a play with Selenium. After downloading the Firefox plugin, I recorded the actions that our user did, and ran through as fast as it would replay.&lt;br /&gt;&lt;br /&gt;Success, the bug reappeared. If run at slow speed, the test ran through happily, but fast caused the issue with the Ajax.&lt;br /&gt;&lt;br /&gt;Working on a solution then gave me a test case to work against, and I can happily report that the bug has gone away.&lt;br /&gt;&lt;br /&gt;Now, as a reader of this blog, you know I am a Perl programmer, and, yes, this app is just that. A Clearpress app.&lt;br /&gt;&lt;br /&gt;We have a good test suite exercising the model, view and api, but we don't have anything UX tests. So, can I run the Selenium IDE file (Selenese HTML) in the Perl test suite. CPAN has two modules providing drivers for the Selenium Server, but no converter from Selenese to Perl Test. That seemed to go some time ago.&lt;br /&gt;&lt;br /&gt;So, time to actually do something about it. I chose to use the Test:: WWW::Selenium module over Selenium::Remote::Driver, as I preferred the API.&lt;br /&gt;&lt;br /&gt;Now, I had two strategies here.&lt;br /&gt;&lt;br /&gt;1) Fully convert the Selenese HTML file to a Perl Test file,&lt;br /&gt;2) On the fly, convert and run the Selenese HTML file&lt;br /&gt;&lt;br /&gt;I opted for 2, as I coul just hard code in a wrapper test a Selenese suite file, and then every time we record another user story, it will just get added to the suite, and run. Also, keeping the code DRY, as it will b handy to keep the Selenese HTML around to run individually in the IDE.&lt;br /&gt;&lt;br /&gt;So, I started developing Test::WWW::Selenium::Conversion::IDE, to do just that. I released it yesterday to CPAN.&lt;br /&gt;&lt;br /&gt;It should most definitely be considered ALPHA code. I am unlikely to change the function names, as $work will rely on it, but it is by no means feature complete. I welcome comments and patches if you need more features.&lt;br /&gt;&lt;br /&gt;The way it works:&lt;br /&gt;&lt;br /&gt;1) In a wrapper test file, create a Test::WWW::Selenium object with creds for your Selenium Server&lt;br /&gt;2) Pass to the suite runner function this object and the filename of your suite file. You can also pass the location of the file, but it defaults to t/selenium_tests.&lt;br /&gt;3) Rather than give a plan, use done_testing() at the end of your file, so the TAP plan can be accounted.&lt;br /&gt;&lt;br /&gt;See POD for more options.&lt;br /&gt;&lt;br /&gt;So, I push it out for the communities use.&lt;br /&gt;&lt;br /&gt;Bye for now.&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-2085700343319327858?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/2085700343319327858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=2085700343319327858' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2085700343319327858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2085700343319327858'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2011/12/testwwwseleniumconversionide.html' title='Test::WWW::Selenium::Conversion::IDE'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total><georss:featurename>Haverhill, Suffolk, UK</georss:featurename><georss:point>52.0847688 0.4367951</georss:point><georss:box>52.0652543 0.3973131 52.1042833 0.4762771</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4479620344265127669</id><published>2011-09-02T01:49:00.000-07:00</published><updated>2011-09-02T01:49:11.322-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recovery'/><category scheme='http://www.blogger.com/atom/ns#' term='LSF'/><category scheme='http://www.blogger.com/atom/ns#' term='stuck jobs'/><category scheme='http://www.blogger.com/atom/ns#' term='pipelines'/><title type='text'>Deep Magic - recover a file</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;We have hopefully managed to recover a file (or two) today from an existing process in which the files live in /tmp/&lt;rnd_dir_name&gt;, but if the process goes on too long, get deleted by the system&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;Find relevant Process ID&lt;br /&gt;  &gt;ps aux or top&lt;br /&gt;  &gt;pstree -p &lt;PID&gt;&lt;br /&gt;&lt;br /&gt;Work out files which have handles but are deleted&lt;br /&gt;  &gt;lsof -p &lt;PID&gt;&lt;br /&gt;  bwa     22039 srpipe    4r   REG      104,7  424718660  67171651 /tmp/iFCyWKyDjR/1.sai (deleted)&lt;br /&gt;  bwa     22039 srpipe    5r   REG      104,7  401682372  67171652 /tmp/iFCyWKyDjR/2.sai (deleted)&lt;br /&gt;&lt;br /&gt;Rebuild files&lt;br /&gt;  &gt;mkdir /tmp/iFCyWKyDjR&lt;br /&gt;  &gt;cat /proc/22039/fd/4 &gt; /tmp/iFCyWKyDjR/1.sai&lt;br /&gt;  &gt;cat /proc/22039/fd/5 &gt; /tmp/iFCyWKyDjR/2.sai&lt;br /&gt;&lt;br /&gt;Check&lt;br /&gt;  &gt;ls -lh /tmp/iFCyWKyDjR&lt;br /&gt;  total 789M&lt;br /&gt;  -rw-rw-r-- 1 srpipe solexa 406M 2011-09-02 09:30 1.sai&lt;br /&gt;  -rw-rw-r-- 1 srpipe solexa 384M 2011-09-02 09:30 2.sai&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/1158653486009791669-4479620344265127669?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4479620344265127669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4479620344265127669' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4479620344265127669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4479620344265127669'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2011/09/deep-magic-recover-file.html' title='Deep Magic - recover a file'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3221123802004552869</id><published>2011-06-14T07:13:00.000-07:00</published><updated>2011-06-14T07:14:01.563-07:00</updated><title type='text'>To remember</title><content type='html'>my $last_base_index = ( length $fields[$SEQUENCE_INDEX] ) - 1;&lt;br /&gt;&lt;br /&gt;The parenthesis are needed!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3221123802004552869?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3221123802004552869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3221123802004552869' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3221123802004552869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3221123802004552869'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2011/06/to-remember.html' title='To remember'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8114646754863348780</id><published>2011-06-03T03:50:00.000-07:00</published><updated>2011-06-03T04:33:48.668-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='catalyst'/><category scheme='http://www.blogger.com/atom/ns#' term='bingo'/><category scheme='http://www.blogger.com/atom/ns#' term='github'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Bingo!</title><content type='html'>Last year, my dad, as the Chairman of the local community association says that what might be a good fund raiser is a bingo evening.&lt;br /&gt;&lt;br /&gt;Initially, my thought was, who are they going to get to run that then?&lt;br /&gt;&lt;br /&gt;But he continued, and came up to the question of sourcing a bingo machine. This is were I came in and said,&lt;br /&gt;&lt;br /&gt;  'Oh, that's easy, I could write one in a couple of weekends on the computer, we jsut hook my laptop up to a big screen and that's that.'&lt;br /&gt;&lt;br /&gt;Can you see where I'm going with this?&lt;br /&gt;&lt;br /&gt;So, time passes, I had a quick stab last November, but stopped as Christmas was coming up, and I was writing a perl course for work...&lt;br /&gt;&lt;br /&gt;Until March, when Dad turns round and says&lt;br /&gt;&lt;br /&gt;  'Are you going to be able to write the bingo machine in time for Annual fundraising week? What day of the week is best for you to run it?'&lt;br /&gt;&lt;br /&gt;Eeep.&lt;br /&gt;&lt;br /&gt;Still, never one to back down from a request to do something, I set to work. Went back to github, and decided to scrap most of what was there, start a new Catalyst project, and a couple of weekends later, I have a bingo caller, with big ball images, and a pretty simple interface.&lt;br /&gt;&lt;br /&gt;It could still do with a pretty looking interface, but it does the job, and should run nicely next week - assuming I can sort out Moose&lt;br /&gt;&lt;br /&gt;https://github.com/setitesuk/Bingo-Caller&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8114646754863348780?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8114646754863348780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8114646754863348780' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8114646754863348780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8114646754863348780'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2011/06/bingo.html' title='Bingo!'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4187344872055909122</id><published>2011-03-16T03:17:00.000-07:00</published><updated>2011-03-16T03:26:46.188-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perlcritic'/><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Interesting bug</title><content type='html'>I have just discovered an interesting bug, which took a lot of staring at to fathom out exactly what is wrong here:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  if ( $self-&gt;is_paired_read() ) {&lt;br /&gt;    my $args = {&lt;br /&gt;      dir =&gt; $arg_refs-&gt;{dir},&lt;br /&gt;      position =&gt; $position,&lt;br /&gt;      read =&gt; $self-&gt;general_values_conf()-&gt;{sanger_read_two},&lt;br /&gt;      job_dependencies =&gt; $arg_refs-&gt;{job_dependencies},&lt;br /&gt;      control =&gt; $is_control,&lt;br /&gt;      is_spiked_phix =&gt; $is_spiked_phix,&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    if ( $self-&gt;is_indexed() ) {&lt;br /&gt;      $args-&gt;{read} = $self-&gt;general_values_conf()-&gt;{illumina_read_two};&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    $bsub_command = $self-&gt;_calibration_table_bsub_command( {&lt;br /&gt;      dir =&gt; $arg_refs-&gt;{dir},&lt;br /&gt;      position =&gt; $position,&lt;br /&gt;      read =&gt; $self-&gt;general_values_conf()-&gt;{sanger_read_two},&lt;br /&gt;      job_dependencies =&gt; $arg_refs-&gt;{job_dependencies},&lt;br /&gt;      control =&gt; $is_control,&lt;br /&gt;      is_spiked_phix =&gt; $is_spiked_phix,&lt;br /&gt;    } );&lt;br /&gt;&lt;br /&gt;    if ( $self-&gt;verbose() ) {&lt;br /&gt;      $self-&gt;log( $bsub_command );&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    push @{ $job_ids }, $self-&gt;submit_bsub_command($bsub_command);&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Some of you may have spotted it straight away, but others may not.&lt;br /&gt;&lt;br /&gt;The problem comes from a bit of&lt;br /&gt;&lt;br /&gt;1) copy and paste programming (there is a read 1 as well)&lt;br /&gt;2) not paying attention&lt;br /&gt;&lt;br /&gt;Had I not had the if ( $self-&gt;is_indexed ) block, then Perl::Critic would actually have saved me here, with a unused variable error, but because $args (lexically scoped) , is used in the block, we skip that.&lt;br /&gt;&lt;br /&gt;Simply, when you go to the trouble of defining your args outside of the call to the method, then use the args, don't rewrite. (Bangs head against wall to try to remember the fact).&lt;br /&gt;&lt;br /&gt;The bug in production is that we always get a calibration table name with read 'sanger_read_two', when it should be read 'illumina_read_two' on an indexed run.&lt;br /&gt;(These are config variables, which actually equate to 2 and 3 respectively)&lt;br /&gt;&lt;br /&gt;So a change to:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    $bsub_command = $self-&gt;_calibration_table_bsub_command( { $args } );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and all is fixed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-4187344872055909122?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4187344872055909122/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4187344872055909122' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4187344872055909122'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4187344872055909122'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2011/03/interesting-bug.html' title='Interesting bug'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-51380888801244202</id><published>2011-02-07T08:21:00.000-08:00</published><updated>2011-02-07T08:31:55.871-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='course'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='0 but true'/><title type='text'>Perl Course help!</title><content type='html'>I need some help as to whether or not to do include something in a beginners perl course I am writing to run at work.&lt;br /&gt;&lt;br /&gt;Courses I went on and textbooks I have read (including the excellent Modern Perl) mention the idiom '0 but true', to have a true 0.&lt;br /&gt;&lt;br /&gt;Everyone in my office when I asked either went 'what?', 'why?' or 'just something not needed and is confusing'.&lt;br /&gt;&lt;br /&gt;Personally, I agree with the 'why?' and 'confusing', so I'm inclined to leave it out. However, what do the community at large think? One of my colleagues has been programming Perl for 15+years, and has never seen it. Is this generally the case?&lt;br /&gt;&lt;br /&gt;Thanks to any responses in advance that support either side of the argument, particularly with good reasoning.&lt;br /&gt;&lt;br /&gt;Just to confirm my 4 reasons for leaving it out:&lt;br /&gt;&lt;br /&gt;1) Never seen it used&lt;br /&gt;2) Too confusing to explain to people experienced in Perl, let alone Newbies&lt;br /&gt;3) Too confusing to read in code&lt;br /&gt;4) Won't be automatically assigned to a variable when answer is 0 (4-4), so better to use defined&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-51380888801244202?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/51380888801244202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=51380888801244202' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/51380888801244202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/51380888801244202'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2011/02/perl-course-help.html' title='Perl Course help!'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-214880818643474128</id><published>2010-11-19T00:17:00.000-08:00</published><updated>2010-11-19T00:28:51.568-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile cambridge'/><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='software east'/><category scheme='http://www.blogger.com/atom/ns#' term='agile development'/><title type='text'>Talk to Software East - Aglie Analysis Pipeline</title><content type='html'>Last night I gave a talk to the monthly &lt;a href="http://softwareast.ning.com/"&gt;Software East&lt;/a&gt; meeting, held by Mark Dalgarno at &lt;a href="http://www.red-gate.com/"&gt;RedGate Software&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Big Thanks to Mark for inviting me, and everyone who showed up for an interesting discussion on Agile techniques. Also, thanks to RedGate for hosting.&lt;br /&gt;&lt;br /&gt;The slides have been uploaded to Slideshare:&lt;br /&gt;&lt;br /&gt;&lt;div style="width:425px" id="__ss_5831566"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/setitesuk/agile-analysis-development" title="Agile analysis development"&gt;Agile analysis development&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5831566" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=agileanalysisdevelopment-101119021249-phpapp02&amp;stripped_title=agile-analysis-development&amp;userName=setitesuk" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5831566" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=agileanalysisdevelopment-101119021249-phpapp02&amp;stripped_title=agile-analysis-development&amp;userName=setitesuk" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/setitesuk"&gt;setitesuk&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;It was a great talk, and the first time I didn't get groans outside of work about having used Perl :)&lt;br /&gt;&lt;br /&gt;Looking forward to the next &lt;a href="http://softwareast.ning.com/xn/detail/4432089:Event:1207?xg_source=activity"&gt;Software East meeting&lt;/a&gt; in January, which will be Rachel Davies talking about Agile Retrospectives.&lt;br /&gt;I saw Rachel give the second day's keynote at Agile Cambridge, and she was brilliant. She was also very nice to chat to at lunch that day. She has written the book &lt;a href="http://pragprog.com/titles/sdcoach/agile-coaching"&gt;Agile Coaching (Pragmatic Programmers)&lt;/a&gt; so for fear of overloading the session with people, I can thoroughly recommend going to this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-214880818643474128?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/214880818643474128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=214880818643474128' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/214880818643474128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/214880818643474128'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/11/talk-to-software-east-aglie-analysis.html' title='Talk to Software East - Aglie Analysis Pipeline'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7774742033630549588</id><published>2010-11-02T01:54:00.000-07:00</published><updated>2010-11-02T01:55:48.135-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='webapps'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Web design Survey</title><content type='html'>&lt;a href="http://alistapart.com/articles/survey2010"&gt;&lt;img src="http://aneventapart.com/webdesignsurvey/templates/ala/images/i-took-the-2010-survey.gif" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7774742033630549588?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7774742033630549588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7774742033630549588' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7774742033630549588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7774742033630549588'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/11/web-design-survey.html' title='Web design Survey'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4992736150518099913</id><published>2010-10-18T04:04:00.000-07:00</published><updated>2010-10-18T04:05:58.308-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile cambridge'/><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='xp'/><category scheme='http://www.blogger.com/atom/ns#' term='agilecam'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='pomodoro technique'/><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><category scheme='http://www.blogger.com/atom/ns#' term='agile development'/><title type='text'>Day 2 of Agile Cambridge</title><content type='html'>After far too little sleep, again I got the first bus out of Haverhill off to Cambridge, luckily not feeling bad after too much beer.&lt;br /&gt;&lt;br /&gt;Arriving about 8.30ish, I chatted with a couple of people from the previous day, and we went through for the second Keynote, Building Trust in Agile Teams - Rachel Davies. Rachel is the co-author of Agile Coaching, and talked a lot about trust. What is trust, how do we trust people (both socially and in the work environment), and how do we build trust. Examples included borrowing £20 of a member of the audience, and doing the falling backwards into a group of peoples arms (which worked, and Rachel actually seemed a bit scared of the volunteers going through with). This was an excellent keynote. Rachel is a great speaker, and presented the material in a very easy to understand way.&lt;br /&gt;&lt;br /&gt;After a coffee break, for me it was back into the Gilb Theatre for Scrum/XP Add-ons workshop, presented by Jon Mullen and Paul Fairless from BSkyB. The organisation of this was different, in that, with a bit of PowerPoint trickery, they were able to allow us to choose the order of the presentation topics. We did some voting (What we preferred, what our teams did agile), we choose the order we would put 'stories' based on complexity (I clearly thought that baking and decorating a cake for 20 was more complex than washing and waxing a car). The method of this though was (I thought) better than story poker). A story card is put on the table, then each person in turn either puts a new card either side (or between), or moves a card. This continues until an order is established of complexity. After this, the cards are then graded 1-&gt;8 for measure of complexity. They gave an excellent description of the way that they work (1 week sprints, pair programming, office layout), and a great little video (with sweets!) of a scrum (that had a number of things wrong with it). It all finished with an attempt to win a bottle of bubbly, in a game like they play on a Friday afternoon.&lt;br /&gt;&lt;br /&gt;Next was lunch, and possibly the biggest thing ever to render me speechless. Rachel caught me and complimented my talk on the Pomodoro Technique. I was quite shocked, and as such completely failed to mention how much I had enjoyed her talk (Sorry Rachel). Here was someone who is experienced in presenting, complementing me on mine. After a little discussion about how I might start bringing Agile into my team here (including the idea of a taskboard, that needn't be on full display the whole time), she moved off and I had another Chat with Martin from &lt;a href="http://www.aptivate.org"&gt;Aptivate&lt;/a&gt; who was telling me about the work they had gone out to do in Zambia bringing women out there up to speed with IT, including the setting up of an Internet Cafe and training staff. It's great to hear about people doing this sort of work, and a shame that they possibly aren't getting as much funding this year to do it as fully as they have done in previous years.&lt;br /&gt;&lt;br /&gt;After lunch, the final workshops session. I opted for Visual Management for Agile Teams - Xavier Quesada Allue. In this, we were tasked with creating a Taskboard which would be the focus of the scrum, and would keep track of stories, tasks, if we were on track, who was doing what and backlog.&lt;br /&gt;&lt;br /&gt;Here is the one my team came up with&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/setitesuk/5093118628/" title="101015_142251 by setitesuk, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4129/5093118628_2121d3bede.jpg" width="500" height="375" alt="101015_142251" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Although, as we went around the room, no two boards were the same. One team (featuring Conny) opted for a Kanban approach&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/setitesuk/5092519995/" title="101015_143508 by setitesuk, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4129/5092519995_769d3c6f00.jpg" width="500" height="375" alt="101015_143508" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;But the coolest had their stories on hearts, which moved along a track. If they stopped, they picked up hairs (a rolling stone gathers no moss) or broke if they were blocked in any way.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/setitesuk/5092519899/" title="101015_145136 by setitesuk, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4090/5092519899_173fed837a.jpg" width="500" height="375" alt="101015_145136" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This was a great way of getting on task with using a board, and showed that I think if the team is involved in the design, it probably adds more ownership and encourages it's use more.&lt;br /&gt;&lt;br /&gt;Xavier also showed us some pictures of taskboards he had helped develop for teams. This was an excellent workshop, and well worth attending.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;After the final coffee break of the conference (a chance to make sure people had my twitter alias - setitesuk), we headed back into the Gilb theatre for a panel Q&amp;A session 'Creating a Development Process for your Team: What, How and Why'. The panel was led by Giovanni Asproni, who I met the previous day in Bob Marshalls workshop, and who previously worked at the EBI (so right next door to me!). Beforehand he admitted that he was happy to do lead the session, since he didn't need to do much talking (I must remember that trick next time).&lt;br /&gt;&lt;br /&gt;The panel consisted of a number of the speakers from the 2 days (although I can only remember the names of Rachel, Allan Kelly and Willem). The main topic eventually was a debate around pair programming, although other aspects of Agile got mentioned (and even the Pomodoro Technique got dropped in as noting it had become a discussion point - yay me :)  ).&lt;br /&gt;&lt;br /&gt;I must say, that after two days I was starting to flag a little, so picked up the least amount from this session, but it was interesting nonetheless, and rounded off the two days in a great way.&lt;br /&gt;&lt;br /&gt;Mark closed off the conference, and we all left (after grabbing more choccies from the RedGate guys!)&lt;br /&gt;&lt;br /&gt;Overall, the conference for me was a great success. An opportunity to find out lots about Agile process. A steep learning curve in places, and just a great opportunity to meet others who are producing great software. My favourite workshop/talk has got to go to Gojko and David for 'The Specification Game', for the great way of teaching us all what we do wrong (it is certainly true that you learn more getting it wrong than right), and favourite snippet goes to James A. Whittaker for being so surprised that so many of us Brits still use a phone book.&lt;br /&gt;&lt;br /&gt;Of all my sessions that I had my time again, I would have chosen not to go to, would be Code Debt. As I mentioned in my previous blog, not because it was lacking, but that it was the least relevant to Agile.&lt;br /&gt;&lt;br /&gt;Thanks to Mark Dalgarno and his team for organising a great Conference, at a fantastic venue. Thanks to all the speakers that I saw, if I could present even half as well as you all, I'd be happy, and your material was in general excellent. Thanks to everyone I met for engaging discussions, and thanks to RedGate Software for the beers!&lt;br /&gt;&lt;br /&gt;I look forward to next year.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-4992736150518099913?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4992736150518099913/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4992736150518099913' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4992736150518099913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4992736150518099913'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/10/day-2-of-agile-cambridge.html' title='Day 2 of Agile Cambridge'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm5.static.flickr.com/4129/5093118628_2121d3bede_t.jpg' height='72' width='72'/><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-2484056100610439470</id><published>2010-10-16T09:08:00.000-07:00</published><updated>2010-10-17T00:24:04.084-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile cambridge'/><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='xp'/><category scheme='http://www.blogger.com/atom/ns#' term='agilecam'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='pomodoro technique'/><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><category scheme='http://www.blogger.com/atom/ns#' term='agile development'/><title type='text'>Day 1 of Agile Cambridge 2010</title><content type='html'>Agile Cambridge 2010&lt;br /&gt;&lt;br /&gt;Day 1&lt;br /&gt;&lt;br /&gt;With much excitement I left Haverhill on the first bus of the day in order to go to the conference. There was method to this madness, since the plan was drinks in the evening, and I wasn't going to pass that opportunity up.&lt;br /&gt;&lt;br /&gt;I left with laptop, plan of the sessions I ideally wanted to achieve, and the bits for my lightning talk.&lt;br /&gt;&lt;br /&gt;I arrived at Murray Edwards College around 8.30, and found those of my colleagues (Beth Jones and Conny Brunkvist) who were also attending. Also picking up a pack and some freebies from RedGate Software (choccies!)&lt;br /&gt;&lt;br /&gt;At 9 we went through for the welcome talk (Mark Dalgarno - Software Acumen) and the first Keynote.&lt;br /&gt;&lt;br /&gt;The Keynote was James A. Whittaker (Google) who delighted us all with his running of test at Google.&lt;br /&gt;As well as unit tests and functional tests, they also have a suite of different Tours, which are designed to go off and fully test the whole applications under different situations, but which are named rather 'cleverly' (I would love to know what 'The Couch Potato Tour' is). He also showed us problems that had been sent in with Google Maps, including the best route to walk from Cambridge to Hull is taking the ferry via Holland.&lt;br /&gt;&lt;br /&gt;He also seemed rather surprised that over half of the audience had actually used a phone book in the last 6 months. Clearly a difference between Americans and Brits.&lt;br /&gt;&lt;br /&gt;After the first coffee break (good letting them be 30 mins, allows plenty of time to chat!), it was the first sessions with choices. I opted for the Workshop 'The Specification Game', since one thing I admit I am bad on is getting specifics up front, and in the end, this only proved it.&lt;br /&gt;&lt;br /&gt;Gojko Adzic and David de Florinier introduced us to a 'simple task'. They were hiring (i.e. they were the customer - note that, I'll come back to it) us to do them a blackjack application. We were to go through the first iteration, and should produce them something which was playable. We needed a business/project owner (that was anyone who knew blackjack - me!) and at least one dev and 1 tester.&lt;br /&gt;&lt;br /&gt;This was were we made our second mistake, (the first being that we completely forgot they were the customer). We went for having the dev and tester as separate people.&lt;br /&gt;&lt;br /&gt;I spec'd out what I thought we could achieve in the first iteration, (not negotiating at all with the customer), and we went for it. I went for a playable version which wouldn't bother with betting, but would check blackjack, that the dealer would deal correctly, and would say if the player or the dealer won.&lt;br /&gt;&lt;br /&gt;At the end of the time allowed, it is safe to say that we failed. Reasons:&lt;br /&gt;&lt;br /&gt;1) Some of my specs weren't good enough&lt;br /&gt;2) One of my specs wasn't dealt with&lt;br /&gt;3) I spent too much time helping the tester, than guiding my two devs and the tester&lt;br /&gt;4) I never consulted with the customer for any user acceptance tests&lt;br /&gt;5) I never consulted with the user for exactly what they wanted in the first iteration, and negotiated that it was too much for my team&lt;br /&gt;&lt;br /&gt;So, I accept the failure.&lt;br /&gt;&lt;br /&gt;Initially afterwards we felt a bit aggrieved since the one thing that everyone in the room had failed to realise was that Gojko and David were our customers, and so we never felt we could consult with them, you notice that I mentioned that they did tell us this up front, so we all failed because we never got them to discuss with us the specifications. We chatted with Gojko nd David, and found that this was what they were trying to get across to us, and that this is the biggest problem with the specifications is that we forget to/or just don't get the customer involved in what to do that iteration. Also the other was that we never asked the customer for UATs, so when we finally got some, they inevitably failed. Also, no-one did the tests before the development. Whilst they said there must be at least one Dev and one Tester, they never said that they couldn't be the same person.&lt;br /&gt;&lt;br /&gt;I think that this was an excellent session. One that I have certainly taken a lot away from.&lt;br /&gt;&lt;br /&gt;After lunch, I went for the Hands-On session Code Debt (David Harvey and Peter Marks). This was a break away from Agile, but I was interested in seeing exactly what was meant by it (the term is bandied around a fair bit, but not with any real explanation). The best definition of Code Debt that was mentioned is 'It is code that you owe time to'.&lt;br /&gt;&lt;br /&gt;We all have written terrible code (I am sure I still do). The purpose of the first exercise was to change/add to some javascript code to make a new set of tests of that code pass. We were given 10 minutes. Some in the group managed it, some didn't. What we were unaware of at the time was that one side of the group were given nice concise well written javascript, and the other just javascript that worked (it was all done via TDD), but had no best practice about it. As such it was essentially unmaintainable code, and us such, they weren't expected to be able to do the task.&lt;br /&gt;&lt;br /&gt;And this seemed to be pretty much the point of the session. If the code just does the job, but hasn't been thought about (idiosyncrasies of the language, meaningful function/variable names, refactored) then there is some level of debt owed to the code to get it to that point.&lt;br /&gt;&lt;br /&gt;It was a good workshop to have attended, but in this case, I think a little out of place at a conference about Agile, and possibly a bit too much time spent getting over the one point. Refactor and make sure that your naming means something. Still, well presented. (And I also discovered I was probably the only Perl programmer there - or at least willing to admit it).&lt;br /&gt;&lt;br /&gt;Another coffee break, and the onto 'Understanding the Agile Mind: How Mindsets Transform as Organisations Rightshift Effectiveness' (Bob Marshall - Falling Blossoms twitter @flowchainsensei)&lt;br /&gt;&lt;br /&gt;I had shared a few words with Bob at lunch (we seemed to have managed to be at all the same sessions so far), but hadn't actually twigged he was running this session.&lt;br /&gt;&lt;br /&gt;The focus of this workshop was looking at how organisations worked at the effectiveness multipliers that give serious increases in production with lot less waste, but are so far above the norm, that the mindset seems 'Alien' to most. Working in small groups, we tried to come up with things that we thought companies that were at 0/1/2/3/4/5x effectiveness (1x being the norm) would be doing. Bob then mentioned&lt;br /&gt;the 4 different types of organistional styles (Ad-Hoc, Analytic (sometimes called Mechanistic), Holistic/Synergistic and Chaordic) (I've just found a &lt;a href="http://www.smsexemplar.com/wp-content/uploads/How-to-Win-Hearts-And-Minds-For-Your-Next-Change-Programme-v0d.pdf"&gt;pdf paper by Bob&lt;/a&gt; about this)&lt;br /&gt;&lt;br /&gt;I think that clearly this is a huge area to look into (I admit by this time of the day I was more thinking about the pub - sorry Bob), but looks incredibly interesting, and I shall be taking the time to read the above paper soon.&lt;br /&gt;&lt;br /&gt;After another coffee break, we had the final session of the day, and a chance for me to Do My Stuff. So, obviously, I attended the Lightning talks (since I was presenting one). I should have gone first, but as with all things, the projection system decided to take that opportunity to have a nap, so I got a bit delayed.&lt;br /&gt;&lt;br /&gt;The other talks were really interesting though. Nick ably stepped up to now go first, with some interesting insights into how agile and scrum very much mimics how humans have always done things, including that daily scrums are like sitting around the tribal campfire.&lt;br /&gt;&lt;br /&gt;Bob then came up, with an explanation of how he came up with his twitter moniker. Tiss from RedGate (I think rather badgered into it by Helen) did some Improv very Whose Line is it Anyway. I then gave my talk on The Pomodoro Technique (&lt;a href="http://tinyurl.com/39o6dtl"&gt;see previous blog post&lt;/a&gt;), and then Alan Kelly came on to show how 'Doing it Right' against 'The Right (Business Aligned) Way to do it' affects the productivity, cost and sales. Took me right back to A-Level Economics, but a reminder of how things need levels of thought and balance.&lt;br /&gt;&lt;br /&gt;After this, a move to the Castle Inn, for a well earned pint (or four) and food, all courtesy of those fine fellows at RedGate, who I spent most of the night chatting to, along with others who were there. (Sorry guys, I can't remember most of your names and it seems that there was no delegate list in the pack).&lt;br /&gt;&lt;br /&gt;I left the pub (unfortunately rather abruptly - big apologies to those I was talking to at the time) in order to catch the bus back to Haverhill. I though some sleep might be useful before the following day.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-2484056100610439470?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/2484056100610439470/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=2484056100610439470' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2484056100610439470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2484056100610439470'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/10/day-1-of-agile-cambridge-2010.html' title='Day 1 of Agile Cambridge 2010'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7066802752233250107</id><published>2010-10-15T02:34:00.000-07:00</published><updated>2010-10-15T02:39:16.006-07:00</updated><title type='text'>Lightning Tomatoes</title><content type='html'>I'm currently at the agile cambridge conference, having a great time finding more out about agile practices (what I'm doing wrong...and right).&lt;br /&gt;&lt;br /&gt;In the lightning talk session I gave a quick 10 minute presentation of &lt;a href="http://www.pomodorotechnique.com"&gt;The Pomodoro Technique&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here are the slides:&lt;br /&gt;&lt;br /&gt;&lt;div style="width:425px" id="__ss_5449401"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/setitesuk/pomodoro-lightning-talk" title="Pomodoro lightning talk"&gt;Pomodoro lightning talk&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5449401" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=pomodorolightningtalk-101015023224-phpapp01&amp;stripped_title=pomodoro-lightning-talk&amp;userName=setitesuk" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5449401" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=pomodorolightningtalk-101015023224-phpapp01&amp;stripped_title=pomodoro-lightning-talk&amp;userName=setitesuk" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/setitesuk"&gt;setitesuk&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The talk generated a fair amount of discussion (and some people saying that they will give it a go). I just hope with most it wasn't just the beer talking (Thanks to &lt;a href="http://www.red-gate.com"&gt;Redgate Software&lt;/a&gt; for that).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7066802752233250107?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7066802752233250107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7066802752233250107' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7066802752233250107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7066802752233250107'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/10/lightning-tomatoes.html' title='Lightning Tomatoes'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-816304718367780217</id><published>2010-09-03T06:35:00.001-07:00</published><updated>2010-09-03T07:35:43.619-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl pomodoro_technique development agile'/><title type='text'>The Tomato Cometh</title><content type='html'>So I have now been doing the Pomodoro Technique for 3 weeks, and I have to say, that it is working very well.&lt;br /&gt;&lt;br /&gt;I am even writing this blog within a Pomodoro - watch for live end/starts and interrupts/failures&lt;br /&gt;&lt;br /&gt;I am averaging 8 completed Pomodoro a day. You can see my end of day report sheet &lt;a href="http://www.flickr.com/photos/setitesuk/4953669319/"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/setitesuk/4953669319/" title="report_table_20100903 by setitesuk, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4110/4953669319_af96a954af.jpg" width="500" height="375" alt="report_table_20100903" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you took a look, you'll see that the most I get is 11, and the least is 5. There is no direct correllation between failures and successes, but some things to note:&lt;br /&gt;&lt;br /&gt;The Boss is away: Less completed and more interruptions/failures as I am being (Pomodoro ends) requested in his place. (finished sentence and save)&lt;br /&gt;&lt;br /&gt;Quick 5 minute break and I'm back (start)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/setitesuk/4953669137"&gt;Here is a one day todo when my boss was off&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/setitesuk/4953669137/" title="pomodoro_0820 by setitesuk, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4105/4953669137_f9450cc251.jpg" width="500" height="375" alt="pomodoro_0820" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Discussion with others: This is difficult, especially when I'm the one being asked, to keep to within 25 minute slots, so an hour can get lost outside of my daily tasks.&lt;br /&gt;&lt;br /&gt;So, how is my day worked.&lt;br /&gt;&lt;br /&gt;First hour - no Pomodori&lt;br /&gt;&lt;br /&gt;8 -&gt; 9am&lt;br /&gt;get in, check emails and relevant blogs/websites.&lt;br /&gt;write out plan for today. If there is nothing already on my radar, then set aside a Pomodoro to go through emails/RT to get new things onto my radar, and replan the day&lt;br /&gt;&lt;br /&gt;9-&gt;9.05 - 5 minute break, ready for first Pomodoro to start.&lt;br /&gt;From now on, I work in Pomodori, 25 minutes and then a 5 minute break.&lt;br /&gt;(Just had a failure - interruption where I needed to discuss something with someone for &gt; 30s - start new Pomodoro)&lt;br /&gt;&lt;br /&gt;If I get an interruption of approx 30s (very quick question from my team members) then this only counts (to me) as a brief interruption, and I don't fail the Pomodoro. If it is longer than this, then the Pomodoro fails.&lt;br /&gt;&lt;br /&gt;Now, I am expected to act on emails if they are urgent, so at the end of any Pomodoro or interruption, I check my inbox, and if necessary, replan my day for the next Pomodoro (or x) to act on an urgent request, else they get deleted or put to act later.&lt;br /&gt;&lt;br /&gt;In my break, I typically get a drink, look out of the window, go for a quick stretch of my legs.&lt;br /&gt;&lt;br /&gt;This tends to go until the end of the Pomodoro that occurs after 4pm. At this point, I don't start another one, but do the daily tidy up (although not of my desk :) ) and fill in my report chart, final check of emails, and twitter out my successful Pomodoro for the day, plus the last song I was listening to.&lt;br /&gt;&lt;br /&gt;Interesting things I have noted over the last 3 weeks:&lt;br /&gt;&lt;br /&gt;1) Time goes quickly&lt;br /&gt;&lt;br /&gt;2) Breaking jobs up into smaller tasks really helps&lt;br /&gt;&lt;br /&gt;3) I can't always predict the number of Pomodori a task will take, but I'm getting slightly better&lt;br /&gt;&lt;br /&gt;4) Failing at 20 minutes can make it seem like a task has taken less Pomodori than it actually did&lt;br /&gt;&lt;br /&gt;5) I can get more done this way (one set of tasks took about 1/2 day less)&lt;br /&gt;&lt;br /&gt;6) A five minute break away increases the likelihood of the Eureka solution to the problem you spent the last 15 minutes looking at&lt;br /&gt;&lt;br /&gt;7) Time really does go very fast&lt;br /&gt;&lt;br /&gt;So now what?&lt;br /&gt;&lt;br /&gt;I am going to continue with this, and I am planning a talk on it now. I think the technique really works for me, and I can track what I have done so much easier than just closing RT tickets/completing features in Pivotal Tracker.&lt;br /&gt;&lt;br /&gt;I strongly recommend this to anyone, and if you need any more suggestion to give it a go, here is a paraphrased Tweet I read a couple of weeks ago:&lt;br /&gt;&lt;br /&gt;The PomodoroTechnique quite literally saved my arse - Software developers at increased risk of Hemorrhoids.&lt;br /&gt;&lt;br /&gt;So get up and move every 25 minutes - you'll be better for it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;(9 minutes left of Pomodoro, time for review of the post)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The two Pomodori this was written in&lt;br /&gt;&lt;br /&gt;  - first 15 minutes of P1 - taking pictures of my sheets and storing on Flickr&lt;br /&gt;  - review - fix all cases of lower case pomodoro(i) to Pomodoro(i)&lt;br /&gt;  - review - deciding that I would add this &lt;a href="http://www.flickr.com/photos/setitesuk/4954260256"&gt;link to yesterdays To Do Today sheet&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/setitesuk/4954260256/" title="pomodoro_0902 by setitesuk, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4081/4954260256_cbfa0a0224.jpg" width="500" height="375" alt="pomodoro_0902" /&gt;&lt;/a&gt;&lt;br /&gt;  - review - find out how to embed the photos instead of links&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-816304718367780217?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/816304718367780217/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=816304718367780217' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/816304718367780217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/816304718367780217'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/09/tomato-cometh.html' title='The Tomato Cometh'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm5.static.flickr.com/4110/4953669319_af96a954af_t.jpg' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1442432076027847044</id><published>2010-08-25T06:36:00.000-07:00</published><updated>2010-08-25T06:41:15.861-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PAUSE'/><category scheme='http://www.blogger.com/atom/ns#' term='MooseX::AttributeCloner'/><category scheme='http://www.blogger.com/atom/ns#' term='github'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>MooseX::AttributeCloner v0.21</title><content type='html'>I have just completed version 0.21 of MooseX::AttributeCloner.&lt;br /&gt;&lt;br /&gt;The only change here is that I have forced the command options to come back in a sorted order. This was discovered when we found that in some tests, on my developers MacBook, we get a different order of production commands than on the Linux boxes. As such, this is causing tests to fail. A fix of this to return in a fixed order should solve this problem.&lt;br /&gt;&lt;br /&gt;It has been submitted to PAUSE (I hope, first time I have tried uploading from a tag in github) but you can also find it on my github account&lt;br /&gt;&lt;br /&gt;github.com/setitesuk/MooseX--AttributeCloner&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1442432076027847044?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1442432076027847044/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1442432076027847044' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1442432076027847044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1442432076027847044'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/08/moosexattributecloner-v021.html' title='MooseX::AttributeCloner v0.21'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3103578471036571142</id><published>2010-08-16T08:27:00.000-07:00</published><updated>2010-08-16T08:37:34.184-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='pomodoro technique'/><category scheme='http://www.blogger.com/atom/ns#' term='agile development'/><title type='text'>3 Days of Tomatos</title><content type='html'>I've been umming and aahing over looking at the Pomodoro Technique as a way of personally increasing my capacity to focus on the task at hand, having heard bits about it on Twitter feeds.&lt;br /&gt;&lt;br /&gt;Finally, I bit the bullet, and spent some money on the book 'Pomodoro Technique Illustrated' by Staffan Noteberg (Pragmatic Bookshelf) and read it.&lt;br /&gt;&lt;br /&gt;So, all I need is a timer, pen, paper and that's it. Well, that I can achieve.&lt;br /&gt;&lt;br /&gt;I had a go the first day after reading the book, and thought I had done reasonably well, but then it all went out the window.&lt;br /&gt;&lt;br /&gt;So, two weeks later, (last Thursday to be exact) I decide to try again. In earnest.&lt;br /&gt;&lt;br /&gt;What a success!&lt;br /&gt;&lt;br /&gt;Although I have made a few errors in judging how many pomodoro certain tasks will take (which actually shows that some jobs needed cutting into more smaller tasks) I have felt more focused throughout the day and felt at the end, I have achieved what I needed to.&lt;br /&gt;&lt;br /&gt;I have averaged 10 pomodoro over the last 3 working days, and feel in that this is a realistic target. Tomorrow, plan for 10 - lets see if I can keep at this.&lt;br /&gt;&lt;br /&gt;Anyone who is reading this, and finds themselves procrastinating, then I recommend this at the moment. A 5 minute break every 25 minutes really helps, and it is very easy to concentrate on task for just 25 minutes at a time.&lt;br /&gt;&lt;br /&gt;The biggest downside I find - my alarm goes off, and the song on my iTunes hasn't finished.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3103578471036571142?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3103578471036571142/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3103578471036571142' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3103578471036571142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3103578471036571142'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/08/3-days-of-tomatos.html' title='3 Days of Tomatos'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-6663550585962836970</id><published>2010-07-29T07:29:00.000-07:00</published><updated>2010-07-29T07:34:00.087-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perlcritic'/><category scheme='http://www.blogger.com/atom/ns#' term='cpan'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><title type='text'>A word to the Mooses out there (Miice?)</title><content type='html'>Just found today with upgrade to latest Perl::Critic&lt;br /&gt;&lt;br /&gt;Subroutines::ProhibitUnusedPrivateSubroutines&lt;br /&gt;&lt;br /&gt;this throws a problem with all the many&lt;br /&gt;&lt;br /&gt;_build_&lt;lazy_attribute&gt;&lt;br /&gt;&lt;br /&gt;as it thinks they are unused private subroutines, not seeing them elsewhere in the code&lt;br /&gt;&lt;br /&gt;To fix (thanks to the CPAN documentation for helping me get to this) add the following in your (.)perlcriticrc&lt;br /&gt;&lt;br /&gt;[Subroutines::ProhibitUnusedPrivateSubroutines]&lt;br /&gt;private_name_regex = _(?!build_)\w+&lt;br /&gt;&lt;br /&gt;This will pattern match and allow anything beginning _build_&lt;br /&gt;&lt;br /&gt;Cheers&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-6663550585962836970?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/6663550585962836970/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=6663550585962836970' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6663550585962836970'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6663550585962836970'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/07/word-to-mooses-out-there-miice.html' title='A word to the Mooses out there (Miice?)'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8274735532655519194</id><published>2010-07-22T00:18:00.000-07:00</published><updated>2010-07-22T00:24:45.312-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='concat'/><title type='text'>Difficult to track bug</title><content type='html'>Here was a difficult bug to spot (using 5.8.8 and 5.10.1, not tried on 5.12):&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;my $output_path = $self-&gt;output_path();&lt;br /&gt;my $bam_filename = $self-&gt;bam_filename_root();&lt;br /&gt;&lt;br /&gt;if ( some condition ) {&lt;br /&gt;&lt;br /&gt;  $output_path .= q{lane} . $self-&gt;position_decode_string() . q{/}.&lt;br /&gt;  $bam_filename .=  $self-&gt;position_decode_string();&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This was caused by a bit of lack of due care and attention after a bit of copy and paste refactoring.&lt;br /&gt;&lt;br /&gt;Unfortunately, the code is perfectly legit, and parses as though the .= after $bam_filename is just a ., without also doing the concat to $bam_filename.&lt;br /&gt;&lt;br /&gt;Bit of a pain in the backside to find this one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8274735532655519194?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8274735532655519194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8274735532655519194' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8274735532655519194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8274735532655519194'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/07/difficult-to-track-bug.html' title='Difficult to track bug'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3408852403118180353</id><published>2010-07-12T08:10:00.000-07:00</published><updated>2010-07-12T08:17:23.495-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='regex'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='benchmark'/><title type='text'>A small journey in Benchmarking</title><content type='html'>20ish lines of verbose code involving hashes, arrays and grouping, in comparison to a magic piece of regex which does in 3 lines the same thing.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;use Benchmark q{:all};&lt;br /&gt;&lt;br /&gt;my @lsf_indices = ( 1000,1001,1002,1003,1006,3000,3300,3301,3302,3303,3304,3305,3306,3998,3999,4000,4001,4002);&lt;br /&gt;&lt;br /&gt;my %methods = (&lt;br /&gt;&lt;br /&gt;  regex =&gt; sub {&lt;br /&gt;    my $array_string = join q{,}, @lsf_indices;&lt;br /&gt;    $array_string =~ s/\b(\d+)(,((??{$+ + 1}))\b)+/$1-$+/g;&lt;br /&gt;    $array_string = q{[} . $array_string . q{]};&lt;br /&gt;  },&lt;br /&gt;  verbose =&gt; sub {&lt;br /&gt;      my ( $previous, $current_working_index );&lt;br /&gt;      my %consecutive;&lt;br /&gt;&lt;br /&gt;       foreach my $index ( @lsf_indices ) {&lt;br /&gt;&lt;br /&gt;         if ( $previous &amp;&amp; ( $index == $previous + 1 ) ) {&lt;br /&gt;           push @{ $consecutive{ $current_working_index } }, $index;&lt;br /&gt;           $previous = $index;&lt;br /&gt;         } else {&lt;br /&gt;           $previous = $index;&lt;br /&gt;           $current_working_index = $index;&lt;br /&gt;           push @{ $consecutive{ $current_working_index } }, $index;&lt;br /&gt;         }&lt;br /&gt;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       my @array;&lt;br /&gt;       foreach my $index ( sort { $a &lt;=&gt; $b } keys %consecutive ) {&lt;br /&gt;&lt;br /&gt;         if ( scalar @{ $consecutive{$index} } == 1 ) {&lt;br /&gt;&lt;br /&gt;           push @array, qq{$consecutive{$index}-&gt;[0]};&lt;br /&gt;&lt;br /&gt;         } else {&lt;br /&gt;&lt;br /&gt;           my $last  = pop   @{ $consecutive{$index} };&lt;br /&gt;           my $first = shift @{ $consecutive{$index} };&lt;br /&gt;           push @array, $first . q{-} . $last;&lt;br /&gt;&lt;br /&gt;         }&lt;br /&gt;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       my $array_string = q{[} . ( join q{,}, @array ) . q{]};&lt;br /&gt;     },&lt;br /&gt;&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;cmpthese(  30_000, \%methods);&lt;br /&gt;timethese( 30_000, \%methods);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Result&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;home$ ./benchmark.pl &lt;br /&gt;           Rate   regex verbose&lt;br /&gt;regex    6048/s      --    -72%&lt;br /&gt;verbose 21898/s    262%      --&lt;br /&gt;Benchmark: timing 30000 iterations of regex, verbose...&lt;br /&gt;     regex:  5 wallclock secs ( 4.96 usr +  0.01 sys =  4.97 CPU) @ 6036.22/s (n=30000)&lt;br /&gt;   verbose:  1 wallclock secs ( 1.37 usr +  0.00 sys =  1.37 CPU) @ 21897.81/s (n=30000)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Verbose lines of code is around 5 times faster. Happy :) Code I can read, and speed benefits as well.&lt;br /&gt;&lt;br /&gt;Admittedly, a bit of an exercise, since this isn't really a bottleneck. ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3408852403118180353?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3408852403118180353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3408852403118180353' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3408852403118180353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3408852403118180353'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/07/small-journey-in-benchmarking.html' title='A small journey in Benchmarking'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5723649035408728178</id><published>2010-06-24T06:09:00.001-07:00</published><updated>2010-06-24T06:17:20.213-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='write to file'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl6::Slurp'/><title type='text'>File::Spit</title><content type='html'>John in my office a couple of days ago said we need the function 'spit' for writing to files much like we have 'slurp' in Perl6 (or by using one of my 3 fave cpan module Perl6::Slurp)&lt;br /&gt;&lt;br /&gt;Since I have just had a couple of hours, I have just done this. File::Spit exports automatically the symbol 'spit'.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This method takes a file_path, $data and an optional delimiter flag.&lt;br /&gt;&lt;br /&gt;From the POD:&lt;br /&gt;&lt;br /&gt;spit:&lt;br /&gt;&lt;br /&gt;will croak if once it has a file_path and a possible delimiter, it finds no data (note, this is different from an empty string or 0)&lt;br /&gt;&lt;br /&gt;delimiter - if the delimiter matches against /\A[\s:=,.\/;]+\z/xms, it will be used, else it will just append to the file&lt;br /&gt;perl false values (undef, q{} or 0) will be classified as though there is no delimiter given&lt;br /&gt;&lt;br /&gt;will croak if it fails to write, with value of $EVAL_ERROR&lt;br /&gt;&lt;br /&gt;Write to file, killing any previous versions of file (note, no warnings)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  eval {&lt;br /&gt;    spit ( $FilePath, $data );&lt;br /&gt;  } or do {&lt;br /&gt;    your error handling here...&lt;br /&gt;  };&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Append to file, creating if needed, but no delimiter&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  eval {&lt;br /&gt;    spit ( $FilePath, $string, 1 ); # note, it is just a true value, any perl false values will use write and kill previous file&lt;br /&gt;  } or do {&lt;br /&gt;    your error handling here...&lt;br /&gt;  };&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Append as above, but with delimiter&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  eval {&lt;br /&gt;    spit ( $FilePath, $string, qq{\n\n} );&lt;br /&gt;  } or do {&lt;br /&gt;    your error handling here...&lt;br /&gt;  };&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;On success, this returns a true value (1)&lt;br /&gt;&lt;br /&gt;This is probably completely unnecessary, or already exists on CPAN. If it doesn't, I'm happy to push to it, but for now, you can get it from github&lt;br /&gt;&lt;br /&gt;http://github.com/setitesuk/File--Spit/tree/v0.1&lt;br /&gt;&lt;br /&gt;This in theory could work with any type of data, but the tests only check strings.&lt;br /&gt;&lt;br /&gt;I'm off on holiday now.&lt;br /&gt;&lt;br /&gt;Happy Coding&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5723649035408728178?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5723649035408728178/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5723649035408728178' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5723649035408728178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5723649035408728178'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/06/filespit.html' title='File::Spit'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-47130089777615958</id><published>2010-06-16T01:21:00.000-07:00</published><updated>2010-06-16T01:33:09.387-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='eval'/><category scheme='http://www.blogger.com/atom/ns#' term='attributes'/><title type='text'>Is there anything wrong with this?</title><content type='html'>So, we all think reusing other code and not reinventing the wheel is generally a good thing.&lt;br /&gt;&lt;br /&gt;And using Moose is generally good.&lt;br /&gt;&lt;br /&gt;So, I want to test if something is an object. I am using Moose.&lt;br /&gt;&lt;br /&gt;1) Create a large hash to compare keys against, and then do&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;my $not_object_ref = {&lt;br /&gt;  HASH =&gt; 1, ARRAY =&gt; 1, GLOB =&gt; 1,...&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;my $is_object;&lt;br /&gt;if ( my $ref = ref $object ) {&lt;br /&gt;  if ( ! $not_object_ref-&gt;{$ref} ) {&lt;br /&gt;    $is_object++;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# stuff that uses boolean value of $is_object&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2) Use a Moose Attribute and eval&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;has q{_i_am_an_object} =&gt; (&lt;br /&gt;  isa =&gt; q{Object},&lt;br /&gt;  is =&gt; q{rw},&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;sub _is_object {&lt;br /&gt;  my ( $self, $object ) = @_;&lt;br /&gt;  my $is_object = 0;&lt;br /&gt;  eval {&lt;br /&gt;    $self-&gt;_i_am_an_object( $object ); # test if this is an object&lt;br /&gt;    $is_object++;&lt;br /&gt;  } or do {}; # I like PBP and perl critic :)&lt;br /&gt;  return $is_object;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;my $is_object = $self-&gt;_is_object( $object );&lt;br /&gt;# stuff that uses boolean value of $is_object&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I don't know if this is a pure abuse of the Moose Attribute system, or if there is a much neater way of doing it, but certainly it has merit over the whole keeping track of what refs are not objects. And certainly, if you want to check against the object type, you change the isa to the class name, and give it a better method name.&lt;br /&gt;&lt;br /&gt;It's probably more of an abuse of eval :)&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-47130089777615958?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/47130089777615958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=47130089777615958' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/47130089777615958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/47130089777615958'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/06/is-there-anything-wrong-with-this.html' title='Is there anything wrong with this?'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-6837020935561285901</id><published>2010-06-10T04:39:00.000-07:00</published><updated>2010-06-10T04:55:43.862-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='array'/><category scheme='http://www.blogger.com/atom/ns#' term='Test::MockObject'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='shift'/><title type='text'>When not to shift...</title><content type='html'>I'm not a big fan of 'shift @array'. And I have just sealed why I think that is.&lt;br /&gt;&lt;br /&gt;Anyone who has looked at my code will see that my methods always use array assignments&lt;br /&gt;&lt;pre&gt;sub my_method {&lt;br /&gt;  my ( $self, $arg_refs ) = @_;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Even if it is just $self, rather than&lt;br /&gt;&lt;pre&gt;sub my_method {&lt;br /&gt;  my $self = shift;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Today, I further found another bug whilst testing. I'm trying out Test::MockObject, and know that I want to return an arrayref, with 1 element in the array.&lt;br /&gt;&lt;br /&gt;The code loops through 8 times, and I expect a result from this array each time, since the webservices I am mocking would provide this.&lt;br /&gt;&lt;br /&gt;However, in my tests, I get a result of 1 true, rather than 8.&lt;br /&gt;&lt;br /&gt;Why is this?&lt;br /&gt;&lt;br /&gt;I check the arrayrefs are the same with some debug (they are). I check the array length each time through. 1st time, 1. All other 7, 0.&lt;br /&gt;&lt;br /&gt;The reason is that instead of just assigning&lt;br /&gt;&lt;pre&gt;my $ref_seq = $arrayref-&gt;[0];&lt;/pre&gt;&lt;br /&gt;I have shifted&lt;br /&gt;&lt;pre&gt;my $ref_seq = shift @{ $arrayref };&lt;/pre&gt;&lt;br /&gt;So of course, I have within the code removed the element off the array, rather than leave it there, and since $arrayref goes out of scope in the code as soon as $ref_seq is assigned, either works.&lt;br /&gt;&lt;br /&gt;This is one time when I would expect the 'real world' to have still been fine, but my 'test environment' can't cope.&lt;br /&gt;&lt;br /&gt;Obviously, there are times when shift is most appropriate, i.e. you deliberately need to truncate out the first element because you must not allow that to get somewhere else as the array carries on, but that niggling little voice that keeps telling me never to shift has finally had a reason to give me.&lt;br /&gt;&lt;br /&gt;Still, liking Test::MockObject :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-6837020935561285901?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/6837020935561285901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=6837020935561285901' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6837020935561285901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6837020935561285901'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/06/when-not-to-shift.html' title='When not to shift...'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4634093395307693135</id><published>2010-04-21T08:19:00.000-07:00</published><updated>2010-04-21T08:32:38.979-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='test::builder'/><title type='text'>It's been a while</title><content type='html'>It has been a while since I last blogged. Unfortunately, family health problems are still not in a resolved state, so it probably will be so again.&lt;br /&gt;&lt;br /&gt;However, last week, I went on Dave Cross's advanced perl course/seminar. In fact, most of my team did.&lt;br /&gt;&lt;br /&gt;Dave knew his stuff (and apologised that he was talking about the 'new' 5.10 when 5.12 was released less than 36 hours earlier).&lt;br /&gt;&lt;br /&gt;The course was interesting, however, due to the limitations of time (1 day isn't long enough, and I do like Lab-time) we didn't cover as much about Moose/DBIx::Class/Catalyst than I would have liked.&lt;br /&gt;&lt;br /&gt;However, he did cover Test::Builder, which I hadn't really seen before, and how to create your own bespoke tests which will fit in with the plan.&lt;br /&gt;&lt;br /&gt;And so I just have.&lt;br /&gt;&lt;br /&gt;I have released Test::Structures::Data today. At the moment it only exposes one method&lt;br /&gt;&lt;br /&gt;is_value_found_in_hash_values&lt;br /&gt;&lt;br /&gt;Now, this is not to detract from Test::Deeply (which I like a lot!) or Test::Data::Hash, however, I could not find any methods which go to see if a value is present in a data structure, and this was particularly what I was after.&lt;br /&gt;&lt;br /&gt;It is on CPAN&lt;br /&gt;&lt;br /&gt;http://tinyurl.com/y5owods&lt;br /&gt;&lt;br /&gt;and my github repository is&lt;br /&gt;&lt;br /&gt;http://github.com/setitesuk/Test--Data--Structures&lt;br /&gt;&lt;br /&gt;I plan to add some other methods shortly. These are meant to be simple things though. For example, you may have a hash, and a value. You don't know which key the value should be for, or even care which key it belongs to. The above test just sees if it can find it in the hash values.&lt;br /&gt;&lt;br /&gt;Hoping this might be of use to someone (anyone?)&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-4634093395307693135?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4634093395307693135/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4634093395307693135' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4634093395307693135'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4634093395307693135'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/04/its-been-while.html' title='It&apos;s been a while'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-694799554236520682</id><published>2010-04-08T04:05:00.000-07:00</published><updated>2010-04-08T04:15:19.578-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DBIx::Class'/><category scheme='http://www.blogger.com/atom/ns#' term='catalyst'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Retrieving the $schema object from the resultset object</title><content type='html'>More for my own personal reference&lt;br /&gt;&lt;br /&gt;Within a resultset (row) object&lt;br /&gt;&lt;br /&gt;my $schema = $self-&gt;result_source-&gt;schema();&lt;br /&gt;&lt;br /&gt;Example reasoning&lt;br /&gt;&lt;br /&gt;User wants to be able to obtain (via helper method) all the public groups s/he doesn't belong to&lt;br /&gt;&lt;br /&gt;sub public_usergroups {&lt;br /&gt;  my ($self) = @_;&lt;br /&gt;&lt;br /&gt;  my $schema = $self-&gt;result_source-&gt;schema();&lt;br /&gt;&lt;br /&gt;  my @public_usergroups = $schema-&gt;resultset('Usergroup')-&gt;search({&lt;br /&gt;    id_usergroup =&gt; {&lt;br /&gt;      'NOT IN' =&gt; $self-&gt;user2usergroups-&gt;get_column('id_usergroup')-&gt;as_query,&lt;br /&gt;    },&lt;br /&gt;    is_public =&gt; 1,&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;  return @public_usergroups;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The returned set are then also Usergroup objects, so they have the methods relating to those.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-694799554236520682?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/694799554236520682/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=694799554236520682' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/694799554236520682'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/694799554236520682'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/04/retrieving-schema-object-from-resultset.html' title='Retrieving the $schema object from the resultset object'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-9110759913319201156</id><published>2010-03-19T03:17:00.001-07:00</published><updated>2010-03-19T04:16:12.916-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Duran Duran'/><category scheme='http://www.blogger.com/atom/ns#' term='barcamb'/><category scheme='http://www.blogger.com/atom/ns#' term='unconference'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='ironman'/><title type='text'>BarCamb3</title><content type='html'>Ive just been one of the lucky 20 to get a shiny ticket to BarCamb3 in the first batch of tickets released. WooHoo! Just hope now that my wife is better so that I can go.&lt;br /&gt;&lt;br /&gt;More tickets are to be released next week, and the following week.&lt;br /&gt;&lt;br /&gt;I went to the first 2 BarCambs, which where hosted by Matt Wood at the Wellcome Trust Sanger Institute. They were very interesting events, and I spoke at the 2nd one with a presentation called 'It's Too Much Information For Me', which came to me when I was 5 minutes away from the event in the car, thinking about a Duran Duran song and even the group hadn't been played on the radio.&lt;br /&gt;&lt;br /&gt;That's the great thing about BarCamps. They are unconferences, so pretty much anything goes. You turn up on the day, with or without anything to talk about. Mill around having coffee, and put your name up to speak with a topic, or don't, or join some others.&lt;br /&gt;&lt;br /&gt;The website for BarCamb is&lt;br /&gt;&lt;br /&gt;&lt;a href="http://barcamb.ltheobald.co.uk"&gt;http://barcamb.ltheobald.co.uk/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;and the sponsors this year are Red Gate Software, Paypal and Taylor Vinters Solicitors.&lt;br /&gt;&lt;br /&gt;I don't yet know what to talk about/discuss yet. Perhaps it will again come to me in the car. Also, I don't know how 2 days will be filled up, but whatever happens, it is bound to be interesting, and I'm hoping Simon turns up with Mbed again. (I want to play this year please!)&lt;br /&gt;&lt;br /&gt;Andy&lt;br /&gt;&lt;br /&gt;&lt;a href="http://ironman.enlightenedperl.org/signup/new_feed"&gt;&lt;img alt="signup banner" src="http://enlightenedperl.org/images/ironsignup.png"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-9110759913319201156?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/9110759913319201156/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=9110759913319201156' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/9110759913319201156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/9110759913319201156'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/03/barcamb3.html' title='BarCamb3'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1711534881757063827</id><published>2010-03-11T05:07:00.000-08:00</published><updated>2010-03-13T03:54:30.165-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='mental health'/><category scheme='http://www.blogger.com/atom/ns#' term='R-mode'/><category scheme='http://www.blogger.com/atom/ns#' term='Pragmatic Thinking and Learning'/><category scheme='http://www.blogger.com/atom/ns#' term='L-mode'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pragprog'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='ironman'/><title type='text'>Artificial Intelligence vs the human brain</title><content type='html'>Here is a plea to all people working on Artificial Intelligence.&lt;br /&gt;&lt;br /&gt;Please think about what you are doing, and try to avoid loose wiring.&lt;br /&gt;&lt;br /&gt;My wife has suffered from Post-Natal depression ever since the birth of our son 4 and half years ago. Yesterday, I took her into hospital again to ensure that she is safe (she is plagued by voices at the moment) whilst the new medication ramps up to a level which is therapeutic for her.&lt;br /&gt;&lt;br /&gt;This, as you may expect, is quite distressing for all of us. We have support systems in place though, and my work are being frankly fantastic about it all.&lt;br /&gt;&lt;br /&gt;It gets me thinking though. I can program computers to get them to behave in particular ways, but how do you rewrite the programming in a brain.&lt;br /&gt;&lt;br /&gt;I recently read 'Pragmatic Thinking and Learning - Refactor your Wetware' by Andy Hunt (www.pragprog.com).&lt;br /&gt;&lt;br /&gt;This is great for yourself, or even trying to suggest to others. I have found some easy to apply tips and some which I need to get round to trying, but one thing it shows is that we have set ways of doing things. Our Logic/Linear-mode (L-mode) tends to be dominant, and seems to have the most influence, unless, as the book suggests, we deliberately try to get information from our Rich-mode (R-mode).&lt;br /&gt;&lt;br /&gt;The key thing with this though is that we control the flow of information, we determine if we will do it. We seem to have a controlling Sensible-mode (S-mode).&lt;br /&gt;&lt;br /&gt;And this is what I like about Computer Programming. A computer really only sources from L-mode, and it allows the L-mode to control it. So I can write a program that is logical, instructs the computer, and it doesn't get anything else influencing it (well, assuming I have taken care of external running factors such as OS, file locations...)&lt;br /&gt;&lt;br /&gt;My wife though, is not controlling her own thoughts. What is, we don't know, but not her. Her S-mode seems to lose it's control. Whether the thoughts are L-mode or R-mode based (traditionally, it would look like those things are "Right-brain" thought processes, although when she explains her thoughts, you could argue the Logic-mode is having a say).&lt;br /&gt;&lt;br /&gt;Luckily, this time, my wife's S-mode seems to be working enough to stop her finishing the act, but she is getting somewhere close to it (popping enough pills out of a blister pack, but at the last minute, throwing them in the bin instead of taking them).&lt;br /&gt;&lt;br /&gt;So where am I going with this.&lt;br /&gt;&lt;br /&gt;My opening request is to think about AI, and not introduce loose wiring. Truly Artificial Intelligence should be able to think both in L-mode and have R-mode, but with an overriding S-mode to control all the thoughts.&lt;br /&gt;&lt;br /&gt;I like programming because what I produce only has to act logically. I am in turmoil because my wife has some 'loose wires' which are not allowing her to act completely rationally, and I can't fix the bug in the program to correct it.&lt;br /&gt;&lt;br /&gt;If AI is a true goal of the computer technology industry, then lets hope that the coding which goes into it won't allow for the loose wires which screw up the controller, or else we might just have a lot of depressive machines which need careful looking after whilst we feed them lots of pill programs to try to make them better.&lt;br /&gt;&lt;br /&gt;Here's hoping that the pills my wife is now taking soon start to sort out her programming. I'm glad I never became a psychiatrist.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://ironman.enlightenedperl.org/signup/new_feed"&gt;&lt;img alt="signup banner" src="http://enlightenedperl.org/images/ironsignup.png"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1711534881757063827?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1711534881757063827/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1711534881757063827' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1711534881757063827'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1711534881757063827'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/03/artificial-intelligence-vs-human-brain.html' title='Artificial Intelligence vs the human brain'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3882170311150275323</id><published>2010-03-04T07:45:00.000-08:00</published><updated>2010-03-04T07:57:56.714-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='daemon'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='crontab'/><title type='text'>crontab or daemon</title><content type='html'>So here is an interesting choice I need to make.&lt;br /&gt;&lt;br /&gt;I have just rewritten a bit of code to email interested parties when a run with their data on it reaches 2 points. One when it reaches run complete (i.e. the instrument has done all it's processing) and then again when the data has been post-processed and qc'd and deposited in the central archive space for them to obtain.&lt;br /&gt;&lt;br /&gt;I'm quite pleased with the code. It is more robust than the previous hack which we had never intended to be all encompassing, and actually mails the parties that should be interested (rather than some 'user' which may or may not be the right person).&lt;br /&gt;&lt;br /&gt;It is, of course, also written using Moose.&lt;br /&gt;&lt;br /&gt;However, now I have to decide, which do I choose, a cronjob, or a daemon process.&lt;br /&gt;&lt;br /&gt;Cronjob:&lt;br /&gt;&lt;br /&gt;Pros - very quick. Just decide how often to launch it, and run the script.&lt;br /&gt;Cons - need to remember which node the cronjob is running, need to do something with the outputs (logs, etc), need to ensure that jobs don't relaunch on top of each other&lt;br /&gt;&lt;br /&gt;Daemon:&lt;br /&gt;&lt;br /&gt;Pros - Use a monitor to keep us informed it is still running, cyclical so won't launch over each other, write to a log file easy&lt;br /&gt;Cons - Need to write a daemon controller script&lt;br /&gt;&lt;br /&gt;I'm sure that there are others, I'm mostly babbling and writing this down as I think. Certainly, for the first release of this, I will start it as a cronjob, but down the line, I think I will move this to a Daemon, once the script has been in a production environment for a while. (i.e. we know it is working correctly!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3882170311150275323?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3882170311150275323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3882170311150275323' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3882170311150275323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3882170311150275323'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/03/crontab-or-daemon.html' title='crontab or daemon'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5251755962116919686</id><published>2010-02-25T06:38:00.000-08:00</published><updated>2010-02-25T06:41:49.497-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Module::Install'/><category scheme='http://www.blogger.com/atom/ns#' term='cpan'/><category scheme='http://www.blogger.com/atom/ns#' term='Readonly'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Module::Build'/><title type='text'>Readonly::Scalar, $VERSION and Module::Build/Install</title><content type='html'>As I blogged before, I have just rebuilt my development area. Within this, I installed the latest version of Module::Build (0.3603).&lt;br /&gt;&lt;br /&gt;Now, two things have happened since this. The first, I mentioned in my post on the latest release of MooseX::AttributeCloner, about the fact that 'passthrough' is being deprecated as an option for dist producing a Makefile.PL.&lt;br /&gt;&lt;br /&gt;I won't go into that one now, but the second is potentially more disastrous.&lt;br /&gt;&lt;br /&gt;Before I go any further, I would like to make it clear that this is not an attempt to slag off the people who write/maintain Module::Build. The tool is extremely useful, and I got a very helpful response from David Golden about my RT ticket. I would like to say Thankyou for producing the tool. The below is possibly more our abuse of it than their failings with it.&lt;br /&gt;&lt;br /&gt;We use Test::Perl::Critic to monitor our coding standards. (OK, I accept that it is a set of guidelines, and is in no way compulsory, but it is a start.) Perlcritic wants $VERSION to be a constant, and to do this within the code, we use (as for all constants)&lt;br /&gt;&lt;br /&gt;Readonly::Scalar our $VERSION =&gt; do { my ($r) = q$LastChangedRevision: 8362 $ =~ /(\d+)/mxs; $r; };&lt;br /&gt;&lt;br /&gt;(Again, yes, taking the version number from a version control system is supposedly 'not good', but it works for us, and others I know)&lt;br /&gt;&lt;br /&gt;When I run perl Makefile.PL, I get the following:&lt;br /&gt;(note, I use the passthrough or small Makefile.PL and perl Makefile.PL to run Build.PL)&lt;br /&gt;&lt;br /&gt;Creating new 'MYMETA.yml' with configuration results&lt;br /&gt;Error evaling version line 'BEGIN { q#  Hide from _packages_inside()&lt;br /&gt;    #; package Module::Build::ModuleInfo::_version::p56;&lt;br /&gt;    use Module::Build::Version;&lt;br /&gt;    no strict;&lt;br /&gt;&lt;br /&gt;    local $VERSION;&lt;br /&gt;    $VERSION=undef;&lt;br /&gt;      $vsub = sub {&lt;br /&gt;        Readonly::Scalar our $VERSION =&gt; do { my ($r) = q$LastChangedRevision: 8362 $ =~ /(\d+)/mxs; $r; };;&lt;br /&gt;        $VERSION&lt;br /&gt;      };&lt;br /&gt;  }' in /this/package/lib/module.pm: syntax error at (eval 92) line 9, near "Readonly::Scalar our "&lt;br /&gt;BEGIN not safe after errors--compilation aborted at (eval 92) line 11, &lt;GEN64&gt; line 36.&lt;br /&gt;&lt;br /&gt;failed to build version sub for /this/package/lib/module.pm at /Users/ajb/dev/perl/5.10.1/lib/5.10.1/Module/Build/ModuleInfo.pm line 332, &lt;GEN64&gt; line 36.&lt;br /&gt;&lt;br /&gt;WARNING: Possible missing or corrupt 'MANIFEST' file.&lt;br /&gt;Nothing to enter for 'provides' field in metafile.&lt;br /&gt;Creating new 'Build' script for 'my_project' version '8391.'&lt;br /&gt;&lt;br /&gt;OK, in this case, it's a warning, but the Build file is created, and I can do what I need to.&lt;br /&gt;&lt;br /&gt;However, updating the Build.PL 'requires' to include some other package libs, the problem becomes more serious.&lt;br /&gt;&lt;br /&gt;Error evaling version line 'BEGIN { q#  Hide from _packages_inside()&lt;br /&gt;    #; package Module::Build::ModuleInfo::_version::p5;&lt;br /&gt;    use Module::Build::Version;&lt;br /&gt;    no strict;&lt;br /&gt;&lt;br /&gt;    local $VERSION;&lt;br /&gt;    $VERSION=undef;&lt;br /&gt;      $vsub = sub {&lt;br /&gt;        Readonly::Scalar our $VERSION =&gt; do { my ($r) = q$LastChangedRevision: 8212 $ =~ /(\d+)/mxs; $r; };;&lt;br /&gt;        $VERSION&lt;br /&gt;      };&lt;br /&gt;  }' in /another/package/lib/module.pm: syntax error at (eval 28) line 9, near "Readonly::Scalar our "&lt;br /&gt;BEGIN not safe after errors--compilation aborted at (eval 28) line 11, &lt;GEN4&gt; line 19.&lt;br /&gt;&lt;br /&gt;failed to build version sub for /another/package/lib/module.pm at /Users/ajb/dev/perl/5.10.1/lib/5.10.1/Module/Build/ModuleInfo.pm line 332, &lt;GEN4&gt; line 19.&lt;br /&gt;Couldn't run Build.PL: No such file or directory at /Users/ajb/dev/perl/5.10.1/lib/5.10.1/Module/Build/Compat.pm line 335.&lt;br /&gt;&lt;br /&gt;Here, because it can't identify the version of the 'external' module, it has croaked out.&lt;br /&gt;&lt;br /&gt;I submitted a bug report, and David Golden (Big thanks to him for responding) suggested that before the line could perhaps be made&lt;br /&gt;&lt;br /&gt;use Readonly; Readonly::Scalar our $VERSION =&gt; do { my ($r) = q$LastChangedRevision: 8212 $ =~ /(\d+)/mxs; $r; };&lt;br /&gt;&lt;br /&gt;since the problem is in the eval block.&lt;br /&gt;&lt;br /&gt;This is fine to do for internal modules, but a problem for anything we install centrally from CPAN (maintenance of code, root access, etc).&lt;br /&gt;&lt;br /&gt;I tried using Module::Install as an alternative. This carps errors in both cases, but in both cases doesn't cause the Makefile not to be created.&lt;br /&gt;&lt;br /&gt;This post therefore comes down to 2 things.&lt;br /&gt;&lt;br /&gt;1) Information to anyone who really cares or reads this blog.&lt;br /&gt;2) Are we the only people who use Readonly::Scalar to declare 'our $VERSION'? (in which case, MooseX::AttributeCloner is possibly the only module on CPAN which does this)&lt;br /&gt;&lt;br /&gt;Thoughts and comments welcome, although I would appreciate people not calling us Idiots (or stronger) for using Readonly::Scalar to 'constant'ify the variable (or at least not without good reason). We have looked at use version;, but it doesn't seem to be right with using a version control value.&lt;br /&gt;&lt;br /&gt;Again, thanks to the authors/maintainers of Module::Build and Module::Install for these wonderful tools.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5251755962116919686?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5251755962116919686/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5251755962116919686' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5251755962116919686'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5251755962116919686'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/02/readonlyscalar-version-and.html' title='Readonly::Scalar, $VERSION and Module::Build/Install'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-986196087199166943</id><published>2010-02-19T00:35:00.000-08:00</published><updated>2010-02-19T00:46:06.466-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MooseX::AttributeCloner'/><category scheme='http://www.blogger.com/atom/ns#' term='cpan'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Module::Build'/><title type='text'>MooseX::AttributeCloner v0.2</title><content type='html'>Yesterday I released v0.2 of MooseX::AttributeCloner. This is just a bugfix release, thanks to those lovely people over at CPANTs.&lt;br /&gt;&lt;br /&gt;The problem was that I missed a file in my MANIFEST, so when I built my distribution package, it left it out. Upshot - tests failed.&lt;br /&gt;&lt;br /&gt;This has now been fixed, however, I since discovered a deprecation on Module::Build, which I have fixed.&lt;br /&gt;&lt;br /&gt;I had initially set up my Build.PL file to use&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;create_makefile_pl =&gt; 'passthrough',&lt;br /&gt;&lt;br /&gt;which generated a Makefile.PL, which loaded Module::Build if not installed.&lt;br /&gt;&lt;br /&gt;However, this feature is deprecated, and may be removed, since newer versions of CPAN.pm/CPANPLUS and 5.10.1 accept the 'configure_requires' option. So, I have converted to using&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;create_makefile_pl =&gt; 'small',&lt;br /&gt;&amp;nbsp;&amp;nbsp;configure_requires =&gt; { 'Module::Build' =&gt; 0.3603 }&lt;br /&gt;&lt;br /&gt;in Build.PL. This is the new way to do it. It is mentioned in the POD and README.&lt;br /&gt;&lt;br /&gt;The new version can be found on CPAN here&lt;br /&gt;&lt;br /&gt;http://tinyurl.com/ylztfvz&lt;br /&gt;&lt;br /&gt;Cheers&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-986196087199166943?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/986196087199166943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=986196087199166943' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/986196087199166943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/986196087199166943'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/02/moosexattributecloner-v02.html' title='MooseX::AttributeCloner v0.2'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-2003946129243455188</id><published>2010-02-17T03:59:00.000-08:00</published><updated>2010-02-17T04:11:26.222-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perlcritic'/><category scheme='http://www.blogger.com/atom/ns#' term='DBIx::Class'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Generated code from DBIx::Class and Test::Perl::Critic</title><content type='html'>We have hit an interesting thing in our ever changing code base.&lt;br /&gt;&lt;br /&gt;We want to start managing the database schema and use the power of DBIx::Class to keep the code and database in sync by auto generation.&lt;br /&gt;&lt;br /&gt;However, we also want to keep to coding standards by running Test::Perl::Critic at maximum severity over the code base.&lt;br /&gt;&lt;br /&gt;Problem - the auto generated code fails tests, but if we modify above the comment line&lt;br /&gt;&lt;br /&gt;#DO NOT MODIFY ANYTHING ABOVE THIS LINE&lt;br /&gt;&lt;br /&gt;we lose the ability to run regeneration of the code if we make any database changes.&lt;br /&gt;&lt;br /&gt;One solution is to say, don't run Perl::Critic over the resultset files. However, this means we need to separate out any manually written code, which should be here (running Catalyst, etc), or we never Test::Perl::Critic our own code. It also means additional maintenance to our auto test each time we add more files.&lt;br /&gt;&lt;br /&gt;Another is never to be able to auto-regenerate and manually keep the database and code in sync.&lt;br /&gt;&lt;br /&gt;So, has anyone come across any solutions to this problem? All comments gratefully received.&lt;br /&gt;&lt;br /&gt;BTW - we understand that&lt;br /&gt;&lt;br /&gt;1) TIMTOADY - some people write good maintainable code which doesn't follow Perl::Critic standards.&lt;br /&gt;2) It would be very difficult for maintainers of code which generates code to constantly keep up with the latest Perl::Critic.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-2003946129243455188?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/2003946129243455188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=2003946129243455188' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2003946129243455188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2003946129243455188'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/02/generated-code-from-dbixclass-and.html' title='Generated code from DBIx::Class and Test::Perl::Critic'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8089665275188130410</id><published>2010-02-16T00:39:00.001-08:00</published><updated>2010-02-16T00:44:53.699-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MooseX::AttributeCloner'/><category scheme='http://www.blogger.com/atom/ns#' term='cpan'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose::Role'/><title type='text'>New release of MooseX::AttributeCloner</title><content type='html'>I released a new version of MooseX:AttributeCloner yesterday to CPAN.&lt;br /&gt;&lt;br /&gt;Here are the changes:&lt;br /&gt;&lt;br /&gt;1) BugFix - CPANTs put in a bug report that MooseX::Getopt was not in the dependencies list in the Build.PL module&lt;br /&gt;&lt;br /&gt;2) You can now do&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;my $NewObject = new::object-&gt;new_with_cloned_attributes($CurrentObject);&lt;br /&gt;&lt;br /&gt;instead of only&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;my $NewObject = $CurrentObject-&gt;new_with_cloned_attributes(q{new::object);&lt;br /&gt;&lt;br /&gt;However, to do this, both objects need to use the MooseX:AttributeCloner role. This is on my TODO list that $CurrentObject would only need to be a Moose object, and not have to utilise the MooseX:AttributeCloner role.&lt;br /&gt;&lt;br /&gt;It's out there now. Any feedback appreciated.&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8089665275188130410?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8089665275188130410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8089665275188130410' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8089665275188130410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8089665275188130410'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/02/new-release-of-moosexattributecloner.html' title='New release of MooseX::AttributeCloner'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3434785237321969582</id><published>2010-02-16T00:29:00.000-08:00</published><updated>2010-02-16T00:36:08.817-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='List-MoreUtils'/><category scheme='http://www.blogger.com/atom/ns#' term='dev'/><category scheme='http://www.blogger.com/atom/ns#' term='cpan'/><category scheme='http://www.blogger.com/atom/ns#' term='List-AllUtils'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>It's amazing - how many more CPAN dependencies</title><content type='html'>It's true, if you need something done, someone may well have done it before and released a CPAN module. However, there are some that I think might have not been necessary.&lt;br /&gt;&lt;br /&gt;After the initial install of my dev area (see last post), I checkout of svn and git my projects again, and started work. However, over the last week, I found a number of other lacking CPAN modules, which I then needed to download.&lt;br /&gt;&lt;br /&gt;LibXSLT:&lt;br /&gt;&lt;br /&gt;Download Latest version from ftp://xmlsoft.org/libxslt/&lt;br /&gt;&lt;br /&gt;./configure --prefix=$HOME/dev&lt;br /&gt;make&lt;br /&gt;make install&lt;br /&gt;&lt;br /&gt;Further CPAN modules:&lt;br /&gt;&lt;br /&gt;i Cache::Memcached&lt;br /&gt;i XML::Generator&lt;br /&gt;i File::Type&lt;br /&gt;i Math::Round&lt;br /&gt;i Data::ICal&lt;br /&gt;i Date::ICal&lt;br /&gt;i Statistics::Lite&lt;br /&gt;i MIME::Parser&lt;br /&gt;i Perl6::Slurp&lt;br /&gt;i Sys::Filesystem::MountPoint&lt;br /&gt;i XSLT::Cache&lt;br /&gt;i Net::Stomp&lt;br /&gt;i Net::Stomp::Receipt&lt;br /&gt;i GD::Graph::bars3d&lt;br /&gt;i IO::Prompt&lt;br /&gt;i Parallel::ForkManager&lt;br /&gt;i HTML::Tidy&lt;br /&gt;i SQL::Translator&lt;br /&gt;i http://search.cpan.org/CPAN/authors/id/D/DR/DROLSKY/List-AllUtils-0.02.tar.gz (dodgy md5, so use full url)&lt;br /&gt;i Fey::ORM&lt;br /&gt;i Fey&lt;br /&gt;i Fey::DBIManager&lt;br /&gt;i Fey::Loader&lt;br /&gt;&lt;br /&gt;I'm not really sure why we particularly needed List-AllUtils, since List-Utils and List-MoreUtils are already being used, but someone has done so. Also, I am not sure why this (and List-MoreUtils before it) had dodgy md5sums. (I should probably put in an RT ticket).&lt;br /&gt;&lt;br /&gt;Anyway, just an update.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3434785237321969582?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3434785237321969582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3434785237321969582' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3434785237321969582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3434785237321969582'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/02/its-amazing-how-many-more-cpan.html' title='It&apos;s amazing - how many more CPAN dependencies'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7571128117128325026</id><published>2010-02-08T00:51:00.001-08:00</published><updated>2010-02-09T01:16:39.402-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gd'/><category scheme='http://www.blogger.com/atom/ns#' term='catalyst'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='cpan'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><category scheme='http://www.blogger.com/atom/ns#' term='bio'/><title type='text'>Rebuilding my development area</title><content type='html'>I thought it was about time to get around to rebuilding my development area, for a few reasons:&lt;br /&gt;&lt;br /&gt;1) Housekeeping - My dev area was getting a lot of junk floating around, and rather than just go through and delete, I thought it better to restart&lt;br /&gt;&lt;br /&gt;2) About time I upgraded to perl-5.10.1&lt;br /&gt;&lt;br /&gt;3) I need to start up a VM soon, and thought it a good opportunity to set make notes about what was needed&lt;br /&gt;&lt;br /&gt;4) cpan/cpanplus wasn't working for me&lt;br /&gt;&lt;br /&gt;5) I've never had GD working properly, and I could be about to lose my desktop at work&lt;br /&gt;&lt;br /&gt;So plenty of reasons. I also wanted to try to structure how I setup various apps, so that it should (in theory) be easier to upgrade an of them. Getting further on, I think this might not be so worthwhile, but at least it's a try.&lt;br /&gt;&lt;br /&gt;Here is 'What I Have Done' so far&lt;br /&gt;&lt;br /&gt;INITIAL SETUP:&lt;br /&gt;&lt;br /&gt;In $HOME&lt;br /&gt;&lt;br /&gt;mkdir dev&lt;br /&gt;cd dev&lt;br /&gt;&lt;br /&gt;This gives me a base dev directory to use&lt;br /&gt;&lt;br /&gt;PERL:&lt;br /&gt;&lt;br /&gt;mkdir perl&lt;br /&gt;&lt;br /&gt;Download the version of perl you want to install&lt;br /&gt;&lt;br /&gt;mkdir perl/&lt;perl_version_no&gt; (i.e. mkdir perl/5.10.1)&lt;br /&gt;&lt;br /&gt;Unarchive the download and go into the directory for created from unarchiving and do the following&lt;br /&gt;&lt;br /&gt;./Configure -des -Dprefix=$HOME/dev/perl/&lt;perl_version_no&gt;&lt;br /&gt;make&lt;br /&gt;make test (go away and make a cup of tea)&lt;br /&gt;make install&lt;br /&gt;&lt;br /&gt;This will now give you in $HOME/dev/perl/&lt;perl_version_no&gt; the bin/, lib/ and man/ directories&lt;br /&gt;&lt;br /&gt;Once you have done this, symlink this version to $HOME/dev/perl/current, and add $HOME/dev/perl/current/bin to $PATH&lt;br /&gt;&lt;br /&gt;This should make your default perl $HOME/dev/perl/current/bin/perl&lt;br /&gt;&lt;br /&gt;Since you have done this, if you now want to download and try another perl, then you can do the same, and just switch the current softlink&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;LIBGD:&lt;br /&gt;&lt;br /&gt;You need to download&lt;br /&gt;freetype-2.3.11.tar.gz&lt;br /&gt;jpegsrc.v8.tar.gz&lt;br /&gt;libpng-1.2.23.tar.gz&lt;br /&gt;zlib-1.2.3.tar.gz&lt;br /&gt;gd-2.0.35.tar.gz&lt;br /&gt;&lt;br /&gt;unpack and install&lt;br /&gt;&lt;br /&gt;freetype&lt;br /&gt;&lt;br /&gt;cd freetype-2.3.11&lt;br /&gt;./configure --prefix=$HOME/dev&lt;br /&gt;make install&lt;br /&gt;cd ..&lt;br /&gt;&lt;br /&gt;jpeg-8&lt;br /&gt;(need to look at)&lt;br /&gt;cd jpeg-8&lt;br /&gt;./configure --prefix=$HOME/dev --enable-shared --enable-static&lt;br /&gt;make&lt;br /&gt;make install&lt;br /&gt;cd ..&lt;br /&gt;&lt;br /&gt;zlib-1.2.3&lt;br /&gt;&lt;br /&gt;cd zlib-1.2.3&lt;br /&gt;./configure --prefix=$HOME/dev&lt;br /&gt;make&lt;br /&gt;make install&lt;br /&gt;cd ..&lt;br /&gt;&lt;br /&gt;[edit] libpng-1.2.x&lt;br /&gt;&lt;br /&gt;cd libpng-1.2.x&lt;br /&gt;CFLAGS="-I$HOME/dev/include" LDFLAGS="-L$HOME/dev/lib" ./configure --prefix=$HOME/dev&lt;br /&gt;make&lt;br /&gt;make install &lt;br /&gt;cd ..&lt;br /&gt;&lt;br /&gt;[edit] gd-2.0.35&lt;br /&gt;&lt;br /&gt;cd gd-2.0.35&lt;br /&gt;CFLAGS="-I$HOME/dev/include" LDFLAGS="-L$HOME/dev/lib" ./configure --prefix=$HOME/dev --with-png=$HOME/dev --with-freetype=$HOME/dev --with-jpeg=$HOME/dev&lt;br /&gt;make INCLUDEDIRS="-I. -I$HOME/dev" LIBDIRS="-L$HOME/dev" LIBS="-lgd -lpng -lz -lm" CFLAGS="-O -DHAVE_LIBPNG"&lt;br /&gt;make install&lt;br /&gt;cd..&lt;br /&gt;&lt;br /&gt;For these, I chose not to create individual versions of them. You will also note that libpng is 1.2 and jpeg is V8 but says need to look at, since this doesn't seem to work with this version of gd. However, since I mostly create png images, I'm not too concerned at this time. Must sort it though eventually.&lt;br /&gt;&lt;br /&gt;Graphviz:&lt;br /&gt;&lt;br /&gt;This is needed for installation of some CPAN modules&lt;br /&gt;http://www.graphviz.org&lt;br /&gt;&lt;br /&gt;Follow instructions on how to install, using $HOME/dev as the prefix&lt;br /&gt;&lt;br /&gt;Again, no version specific route taken&lt;br /&gt;&lt;br /&gt;SLEEPYCAT libdb-4:&lt;br /&gt;&lt;br /&gt;Download from Oracle&lt;br /&gt;&lt;br /&gt;unarchive latest version and install&lt;br /&gt;&lt;br /&gt;cd build_unix/&lt;br /&gt;../dist/configure --prefix=$HOME/dev&lt;br /&gt;make&lt;br /&gt;make install&lt;br /&gt;&lt;br /&gt;Again, no version specific route, and needed for some CPAN modules&lt;br /&gt;&lt;br /&gt;CPAN modules:&lt;br /&gt;&lt;br /&gt;cpanp is the recommended method to download and install modules from cpan&lt;br /&gt;&lt;br /&gt;type &lt;br /&gt;&lt;br /&gt;cpanp&lt;br /&gt;&lt;br /&gt;and the interactive shell will be launched&lt;br /&gt;&lt;br /&gt;If this is the first time, then enter the following&lt;br /&gt;&lt;br /&gt;s conf prereqs 1; s save&lt;br /&gt;&lt;br /&gt;This will save some of the hassle of needing to confirm installation of required modules&lt;br /&gt;&lt;br /&gt;These are chosen because I need to set up a webserver, I work in a Bio place, and some are personal choice. Obviously, if you need others, or not some of these, then pick and choose. They are also loaded in this order for convenience and dependencies.&lt;br /&gt;&lt;br /&gt;To install a cpan module, just type &lt;br /&gt;&lt;br /&gt;i &lt;module&gt;&lt;br /&gt;&lt;br /&gt;i Bundle::LWP&lt;br /&gt;i LWP::Parallel::UserAgent&lt;br /&gt;i YAML::Tiny&lt;br /&gt;i Module::Build&lt;br /&gt;i Module::PortablePath&lt;br /&gt;i Task::Moose (select all the optional loads)&lt;br /&gt;i IO::Stringy&lt;br /&gt;i Calendar::Simple&lt;br /&gt;i List::MoreUtils (This had a checksum error, so manually downloaded)&lt;br /&gt;i DateTime&lt;br /&gt;i DateTime::Format::ICal&lt;br /&gt;i iCal::Parser&lt;br /&gt;i Digest::SHA1&lt;br /&gt;i Class::Std&lt;br /&gt;i Crypt::CBC&lt;br /&gt;i Crypt::Blowfish&lt;br /&gt;i MIME::Lite&lt;br /&gt;i DBI&lt;br /&gt;i DBD::mysql  # force install if you've no test database available, also requires the mysql client development headers - mysql_config needs to be in your $PATH (probably ~/dev/bin).&lt;br /&gt;i DBD::SQLite&lt;br /&gt;i Tie::IxHash&lt;br /&gt;i XML::XPathEngine&lt;br /&gt;i XML::Parser (again, I got a dodgy md5)&lt;br /&gt;i XML::XPath&lt;br /&gt;i HTML::TreeBuilder&lt;br /&gt;i XML::SAX&lt;br /&gt;i XML::Simple&lt;br /&gt;i XML::Handler::YAWriter&lt;br /&gt;i XML::Filter::BufferText&lt;br /&gt;i MLDBM&lt;br /&gt;i Jcode&lt;br /&gt;i Spreadsheet::WriteExcel&lt;br /&gt;i Unicode::Map&lt;br /&gt;i Apache::DBI&lt;br /&gt;i Readonly (another dodgy md5)&lt;br /&gt;i XML::FeedLite (causes lots of prereqs to be installed - would suggest a cup of tea if you have selected auto download of prereqs)&lt;br /&gt;i Chart::OFC&lt;br /&gt;i YAML&lt;br /&gt;i Digest::SHA&lt;br /&gt;i Ace  # force install if fails to make as it may have problems connecting to Ace database during tests&lt;br /&gt;i Bio::ASN1::EntrezGene  # force install if fails as it looks as though for tests it needs a non-existent CPAN module&lt;br /&gt;i Bundle::BioPerl&lt;br /&gt;i GD (Why has this failed tests?)&lt;br /&gt;i B/BI/BIRNEY/bioperl-1.4.tar.gz   # Requires sleepycat libdb-4 to pass tests&lt;br /&gt;i Bio::Das&lt;br /&gt;i Bio::Das::Lite&lt;br /&gt;i App::Ack&lt;br /&gt;&lt;br /&gt;manually download and install DB_File - as you need to Change config.in to point at dev/lib and dev/include&lt;br /&gt;manually download and install BerkeleyDB - as you need to Change config.in as above.&lt;br /&gt;&lt;br /&gt;APACHE and MOD-PERL:&lt;br /&gt;&lt;br /&gt;Apache = httpd-2.2.14;&lt;br /&gt;&lt;br /&gt;http://httpd.apache.org/download.cgi&lt;br /&gt;&lt;br /&gt;in dev, mkdir -p apache/2.2.14&lt;br /&gt;cd apache&lt;br /&gt;ln -s 2.2.14/ current&lt;br /&gt;&lt;br /&gt;This gives space to install this version of apache into, and a softlink to the version we want to use (similar to perl above)&lt;br /&gt;&lt;br /&gt;export LD_LIBRARY_PATH=$HOME/dev/lib&lt;br /&gt;./configure --prefix=$HOME/dev/apache/2.2.14 LDFLAGS="-L/$HOME/dev/lib"&lt;br /&gt;&lt;br /&gt;add $HOME/dev/apache/current/bin to $PATH&lt;br /&gt;&lt;br /&gt;mod_perl 2.0:&lt;br /&gt;&lt;br /&gt;http://perl.apache.org/download/index.html&lt;br /&gt;&lt;br /&gt;Get latest version of 2.0&lt;br /&gt;$HOME/dev/bin/perl Makefile.PL&lt;br /&gt;# follow instructions, e.g. apxs is at $HOME/dev/apache/current/bin/apxs &lt;br /&gt;&lt;br /&gt;make&lt;br /&gt;make install&lt;br /&gt;&lt;br /&gt;Change/create the $HOME/dev/apache/current/conf/httpd.conf and $HOME/dev/apache/current/conf/perlconfig.ini as you need to.&lt;br /&gt;&lt;br /&gt;Catalyst:&lt;br /&gt;&lt;br /&gt;Now the biggie. Catalyst has lots of dependencies. It will take some time, plus it is interactive.&lt;br /&gt;Just install everything - except the extra DBD supports.&lt;br /&gt;You can do them in your own time, but they may make the Install fall over now, which you don't want.&lt;br /&gt;&lt;br /&gt;cpanp&lt;br /&gt;i Task::Catalyst&lt;br /&gt;&lt;br /&gt;If you have got through this, then congrats. &lt;br /&gt;&lt;br /&gt;I have also downloaded into my dev area subversion and git, and have tried to do ImageMagick (although this is erroring that my C compiler won't compile executables, even though it has done svn and git).&lt;br /&gt;&lt;br /&gt;subversion:&lt;br /&gt;&lt;br /&gt;Retrieve the latest version and dependency from http://subversion.apache.org/source-code.html&lt;br /&gt;unpack both, the dependency folder should end up in the same directory, and will then be installed with svn&lt;br /&gt;&lt;br /&gt;mkdir -p $HOME/dev/subversion/&lt;version_number&gt;&lt;br /&gt;cd $HOME/dev/subversion&lt;br /&gt;ln -s &lt;version_number&gt; current&lt;br /&gt;&lt;br /&gt;cd into unpacked folder&lt;br /&gt;&lt;br /&gt;./configure --prefix=$HOME/dev/subversion/&lt;version_number&gt; --eprefix=$HOME/dev/subversion/&lt;version_number&gt;&lt;br /&gt;make&lt;br /&gt;make install&lt;br /&gt;&lt;br /&gt;add $HOME/dev/subversion/current/bin to your $PATH&lt;br /&gt;&lt;br /&gt;This will enable you to have/try multiple versions of svn in the same way as Perl and Apache above&lt;br /&gt;&lt;br /&gt;git:&lt;br /&gt;&lt;br /&gt;Retrieve the latest version from http://git-scm.com/&lt;br /&gt;unpack&lt;br /&gt;&lt;br /&gt;mkdir -p $HOME/dev/git/&lt;version_number&gt;&lt;br /&gt;cd $HOME/dev/git&lt;br /&gt;ln -s &lt;version_number&gt; current&lt;br /&gt;&lt;br /&gt;make configure&lt;br /&gt;./configure --prefix=$HOME/dev/git/&lt;version_number&gt;&lt;br /&gt;make&lt;br /&gt;make install (had to do as root)&lt;br /&gt;&lt;br /&gt;ImageMagick:&lt;br /&gt;&lt;br /&gt;problem with my gcc version at this time&lt;br /&gt;&lt;br /&gt;mkdir -p $HOME/dev/imageMagick/&lt;version_number&gt;&lt;br /&gt;cd $HOME/dev/imageMagick&lt;br /&gt;ln -s &lt;version_number&gt; current&lt;br /&gt;&lt;br /&gt;./configure PREFIX=/Users/ajb/dev/imageMagick/6.5.9 EXEC-PREFIX=/Users/ajb/dev/imageMagick/6.5.9 LIBS=-l/Users/ajb/dev/lib --enable-shared --disable-static&lt;br /&gt;&lt;br /&gt;After this, you should have a nice fairly 'clean' version of a dev area. If you want ot install other stuff, then I would recommend the method suggested for versioning the download you have. (I can also recommend the MOCA installation idea for mysql, although I choose to not have that in my dev area).&lt;br /&gt;&lt;br /&gt;Once inside this, I then create folders for my projects, using svn or git to version control within those folders, just adding the directories to my path as I need to.&lt;br /&gt;&lt;br /&gt;Note: I am using MAC OSX Leopard. At times for the make install, I have needed to sudo make install. I accept no liability for anything that happens should you follow these instructions on any system, but hope that they might be useful for anyone who would like to set up a dev/test area, but are not sure how to go about it.&lt;br /&gt;&lt;br /&gt;Cheers&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7571128117128325026?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7571128117128325026/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7571128117128325026' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7571128117128325026'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7571128117128325026'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/02/rebuilding-my-development-area.html' title='Rebuilding my development area'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8080471948724280210</id><published>2010-01-29T06:55:00.000-08:00</published><updated>2010-01-29T07:31:21.691-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Roles'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='breakfast'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='inheritance'/><category scheme='http://www.blogger.com/atom/ns#' term='API'/><category scheme='http://www.blogger.com/atom/ns#' term='Food'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='cornflakes'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='banana'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='Weetabix'/><title type='text'>Call a Spade a SPADE!</title><content type='html'>AAARRGGGHHHH! Once again, I have found myself trawling around some code (ours) which sits on top of someone elses API representation because someone just can't call a spade a spade.&lt;br /&gt;&lt;br /&gt;Has the world gone mad over the last year? What is the benefit of making people trawl through some huge long chain of calls just to find out that your asset is (or is not) what you thought it was.&lt;br /&gt;&lt;br /&gt;OK, so what is the issue?&lt;br /&gt;&lt;br /&gt;Imagine you have Food that you want to describe. Now, each and every item of food is an asset to you.&lt;br /&gt;&lt;br /&gt;On top of that, some of those have child 'assets'. So I want to some Weetabix and Banana for Breakfast.&lt;br /&gt;&lt;br /&gt;I look in my 'cupboard' (an asset I have) for the Weetabix. What I find is a whole store cupboard full of further 'assets'. So, I check each asset to see if it is Weetabix. I find an asset which is a 'cereal box', containing 24 'assets'. I check its contents, expecting to find Weetabix.&lt;br /&gt;&lt;br /&gt;But wait. It also contains 'assets'. Eh? I interrogate the first of these assets, and find that it is in fact a Weetabix biscuit, but what did I miss? Why not have a subgroup or collection name to the cereal box which describes the assets it contains.&lt;br /&gt;&lt;br /&gt;It goes further. Someone has taken my fruit bowl away, and instead replaced that with an 'asset'. In this case it tells me it's a bowl, but not what of? Again, I find myself searching down to discover where my Banana is.&lt;br /&gt;&lt;br /&gt;And don't get me started on the milk...&lt;br /&gt;&lt;br /&gt;I work in a fairly good object oriented world. I accept that objects can have different (and sometimes multiple) class inheritances, (Weetabix is a 'Cereal', which can also be 'Breakfast', 'Food') but why try to force someone to go down from the top - I have an Asset, it is food, that is also Cereal, that I might eat for Breakfast, it contains further assets, which are CornFlakes. That's not the box containing Weetabix then, Great, lets try the next one.&lt;br /&gt;(Of course, finding CornFlakes might make me suddenly change my mind, but my mind 'asset' has had Weetabix coded into it, so I will only accept that today).&lt;br /&gt;&lt;br /&gt;There is a reason that Food comes in well labelled cartons (usually). It is so that you don't need to open that carton to find out what is in it (I would imagine Supermarkets not being too happy about that!). I'm all for class inheritance (or Role inheritance in the fantastic case of Moose), but I should be able to get my Weetabix, and then ask questions (should I want to) like: Are you a cereal, breakfast, (an Asset?)?&lt;br /&gt;&lt;br /&gt;(It is true that Supermarkets tend to put food into groups, in aisles, but they are usually well labelled as to what you will find in the aisle, you don't need to go down each one).&lt;br /&gt;&lt;br /&gt;Rant over. Now, how should I describe Chocolate? Asset, Food, Meal, Essential...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8080471948724280210?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8080471948724280210/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8080471948724280210' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8080471948724280210'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8080471948724280210'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/01/call-spade-spade.html' title='Call a Spade a SPADE!'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7760889477465836070</id><published>2010-01-25T00:55:00.000-08:00</published><updated>2010-01-25T01:05:41.562-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wii'/><category scheme='http://www.blogger.com/atom/ns#' term='lightsabers'/><category scheme='http://www.blogger.com/atom/ns#' term='burns'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='haggis'/><category scheme='http://www.blogger.com/atom/ns#' term='gaming'/><category scheme='http://www.blogger.com/atom/ns#' term='whisky'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Fighting with a LightSaber</title><content type='html'>Today's coding could be fun, as soon as the Ibuprofen wears off.&lt;br /&gt;&lt;br /&gt;Yesterday I hit 35, and my son, already a huge star wars fanatic at 4, gave me LightSaber Duels for the Wii, and a Lightsaber attachment for the WiiMote.&lt;br /&gt;&lt;br /&gt;Fantastic. This was what I wanted the Wii for. It took my wife to want it for WiiFit to actually have a valid reason to get it, but I can finally be a Jedi Knight.&lt;br /&gt;&lt;br /&gt;Or so I thought. I hadn't quite accounted for my fitness levels. I play Badminton at least once a week, and have been doing some exercise, but after 2x45 minutes of Duelling, my Bicep is starting to seriously complain.&lt;br /&gt;&lt;br /&gt;So, what has this told me about todays work?&lt;br /&gt;&lt;br /&gt;1) Write as little code as possible. Always the mantra of a coder, since you can't have bugs in code that was never written.&lt;br /&gt;&lt;br /&gt;2) Try to take those microbreaks that H&amp;S are going on about.&lt;br /&gt;&lt;br /&gt;3) Don't sit there wishing to play your latest computer game, when you should be coding. You need to take a break from that!&lt;br /&gt;&lt;br /&gt;Some inane ramblings from me today. If you have got this far, then don't forget to raise a glass of whisky to Robert Burns today, and eat plenty of Haggis and Orkney Clapshot.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7760889477465836070?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7760889477465836070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7760889477465836070' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7760889477465836070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7760889477465836070'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/01/fighting-with-lightsaber.html' title='Fighting with a LightSaber'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3024906347469472333</id><published>2010-01-18T00:28:00.001-08:00</published><updated>2010-01-18T00:56:47.780-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='catalyst'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='clearpress'/><title type='text'>From Clearpress to Catalyst</title><content type='html'>We have had a minor problem with our tracking system which has made it difficult to give out completely to the outside world. It is due to the deployment onto our webserver setup.&lt;br /&gt;&lt;br /&gt;In order to be able to 'give away' responsibility for running servers, and some design aspects, we have tied it to the our works internal modules for locating lib files (WorkPaths) and content/page styling and authentication (WorkWeb).&lt;br /&gt;&lt;br /&gt;Unfortunately, because of this, without a lot of work, we are rather stuck with it's limitations, and so there is 'work to be done' if outside people want to use it.&lt;br /&gt;&lt;br /&gt;So, now, I am taking a break from working just on the pipeline, and attempting to break it into a standalone application, using Catalyst. Sorry Roger, ClearPress is great, but we need something with a bigger support network.&lt;br /&gt;&lt;br /&gt;Now, ClearPress works. I still am a fan of, it it ain't broken, don't fix it. And Catalyst has many methods of the MODEL being used, so for this reason, I have worked on the following strategy to move it.&lt;br /&gt;&lt;br /&gt;1) Leave the models alone.&lt;br /&gt;&lt;br /&gt;ClearPress already has a good way of talking to the database, the models already have most of the supporting code in them, so why change this. Catalyst can use other models as a back end, so I am going to leave these as they are for now. This does mean that to operate, you need two frameworks installed from CPAN, but ClearPress is pretty lightweight in it's dependencies, so I don't feel that this is an issue for now.&lt;br /&gt;&lt;br /&gt;2) Keep the templates.&lt;br /&gt;&lt;br /&gt;The preferred templating system for both ClearPress and Catalyst is Template Toolkit (much to my new bosses wish that Mason might be better - i.e. using PERL, but I have wittered on about this in the past, so won't here). This is great, because all I have to do is move the templates to the Catalyst structure, with a small amount of recoding.&lt;br /&gt;&lt;br /&gt;Here, I have also gone for the approach of putting the files into directories named like their parent Controller/View, so the directories are easier to navigate.&lt;br /&gt;&lt;br /&gt;3) Views/Controllers.&lt;br /&gt;&lt;br /&gt;This is a semantic thing. In ClearPress, they are called Views, in Catalyst - Controllers. This is the main part of recoding, because the methods are often autogenerated for you in ClearPress, or at least must follow a strict pattern. Catalyst operates differently here (Chained dispatch, different location to find form params, etc), so this is the slow part.&lt;br /&gt;&lt;br /&gt;However, again, here is an opportunity not to be missed. We got a little out of the Fat Model, Thin Controller/View philosophy in the ClearPress app (not a failing of ClearPress, it is very easy to do the same in Catalyst/Rails or probably any other MVC framework, it was pure laziness on our part as developers). So, I have taken it upon myself, to move code to the relevant Model where necessary.&lt;br /&gt;&lt;br /&gt;Some URLs have changed, and this is again a clear difference between the two. Particularly trying to make the URLs RESTful, but I am managing it.&lt;br /&gt;&lt;br /&gt;4) Styling and JavaScript&lt;br /&gt;&lt;br /&gt;Most of of our styling uses css, so I am taking the opportunity to create a css sheet with everything that is needed, and hopefully not any more. Our internal web styling is changing, and also, for external release to other users, they need a css sheet which will work for them.&lt;br /&gt;&lt;br /&gt;A similar thign with JavaScript. However, a big change is the fact that the core web here is moving from Prototype/Scriptaculous to jQuery. I have already seen some instances that are a problem, since we have coded against P/S and there are conflict with jQuery. So again, I am trying to 'shield' the app against conflict here.&lt;br /&gt;&lt;br /&gt;So far, this move is going well, and pretty rapidly. The CSS/JavaScript is pretty much complete. The templates take minutes to alter. So, I can concentrate on the Controllers, and refactoring some code to the Model, where it belongs. Ultimately, the intention is to move the Models as well. We have already run DBIx:Class over the tracking database, so it shouldn't take much to 'fix' them with the code needed, but small steps, and they ain't broken.&lt;br /&gt;&lt;br /&gt;The only thing I have left off here is authentication. The new boss said he'll deal with that. Currently, I am permanently signed in as an admin, so we shall see how that goes, when I have migrated all that I can.&lt;br /&gt;&lt;br /&gt;Speaking of which, better get to it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3024906347469472333?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3024906347469472333/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3024906347469472333' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3024906347469472333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3024906347469472333'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/01/from-clearpress-to-catalyst.html' title='From Clearpress to Catalyst'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3472735317704983982</id><published>2010-01-11T00:33:00.000-08:00</published><updated>2010-01-11T00:53:07.222-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='phishing'/><category scheme='http://www.blogger.com/atom/ns#' term='Blizzard'/><category scheme='http://www.blogger.com/atom/ns#' term='World of Warcraft'/><category scheme='http://www.blogger.com/atom/ns#' term='spam'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='gaming'/><category scheme='http://www.blogger.com/atom/ns#' term='WoW'/><title type='text'>I Don't Play WoW</title><content type='html'>Over the last few weeks, I have noticed a surprising large number of phishing emails in my inbox. All allegedly coming from blizzard.com relating to my World of Warcraft account.&lt;br /&gt;&lt;br /&gt;They have improved as well.&lt;br /&gt;&lt;br /&gt;1) The first ones where every 3-4 days, telling me that I appear to be trying to sell my WoW account. This is against the rules, and I am in danger of having the account shutdown as per guidelines if this can be proven.&lt;br /&gt;&lt;br /&gt;- I find this difficult to believe for 2 reasons:&lt;br /&gt;i) I don't have WoW account or even own/play a copy of the game&lt;br /&gt;ii) If I did, I doubt (from past gaming experience) I'd ever have one worthy enough to actually sell.&lt;br /&gt;&lt;br /&gt;2) Then came the ones with all the misspellings that you 'would never notice'.&lt;br /&gt;&lt;br /&gt;- I wouldn't follow these links because:&lt;br /&gt;i) See 1i&lt;br /&gt;ii) I have a pretty good grasp of the English Language, and I can spell (I only ever failed 1 spelling test at school, and the word did have more than 3 syllables)&lt;br /&gt;&lt;br /&gt;3) Then a ramp up of number - 2 a day, and the misspellings disappeared.&lt;br /&gt;&lt;br /&gt;- I wouldn't follow because:&lt;br /&gt;i) See 1i&lt;br /&gt;ii) I am not as thick as two short planks&lt;br /&gt;&lt;br /&gt;Now, Blizzard, if you are trying to contact me, then please feel free to cancel my account. My email address is already being fraudulently used, and so I am happy for you to ban it (please note, the email address used is different to this one).&lt;br /&gt;&lt;br /&gt;If, however, guys who are phishing for the account details - please give up. I am getting rather tired of picking apart your emails for potential problems. You need to realise that, if this is to work, you need to be a little bit more subtle about your approach than you are. &lt;span style="font-weight:bold;"&gt;People are waking up to the fact that these emails are simply not real - the Ebay/Paypal ones have stopped, just give it a rest.&lt;/span&gt; Why don't you take up real 'Fishing'? Perhaps you could make money selling whatever you catch, surplus to what you eat. Start a business, and become a useful member of whichever society you live in.&lt;br /&gt;&lt;br /&gt;For anyone who does read this blog, and thinks that I should be playing WoW, especially as a Perl Software Developer/Techie/Geek/Nerd, please don't spam/flame me. I chose the life I live, and I am happy with Lego StarWars and other simple Wii games.&lt;br /&gt;&lt;br /&gt;Rant over.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3472735317704983982?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3472735317704983982/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3472735317704983982' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3472735317704983982'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3472735317704983982'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/01/i-dont-play-wow.html' title='I Don&apos;t Play WoW'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5629451092104533120</id><published>2010-01-08T08:13:00.000-08:00</published><updated>2010-01-08T08:16:35.813-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='pipelines'/><title type='text'>I managed it.</title><content type='html'>So, finishing from the last post, I had to do another bit of file manipulation, both internally and readname, but I got the files back to how they should be. Now, hopefully, we have a pipeline that does what it is supposed to again.&lt;br /&gt;&lt;br /&gt;No-one would ever break it again, no would they?&lt;br /&gt;&lt;br /&gt;:)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5629451092104533120?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5629451092104533120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5629451092104533120' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5629451092104533120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5629451092104533120'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/01/i-managed-it.html' title='I managed it.'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-901485931295786860</id><published>2010-01-07T08:11:00.000-08:00</published><updated>2010-01-07T08:33:21.742-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='changing'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='panic'/><title type='text'>Finally, I get somewhere, I hope</title><content type='html'>We can't expect things to stay static, but you would hope that when they don't, you find out with time to make changes, and that they tell you!&lt;br /&gt;&lt;br /&gt;However, this doesn't always happen, and sometimes the changes are quite major. As any regular readers know, I work at a high throughput sequencing facility, with a major analysis pipeline running all the time. We need to be adaptable, and have become more so, but something changed which hit us hard.&lt;br /&gt;&lt;br /&gt;All of a sudden, something that previously expected 3 inputs, suddenly wanted 2. Also, those two inputs are 1 and 3. And the description needed the middle part stripped out. And the files needed changing both name and internally. And ...&lt;br /&gt;&lt;br /&gt;You get the picture.&lt;br /&gt;&lt;br /&gt;The problem occurs because we are expecting certain outputs, in a certain way to produce the final files that our researchers are expecting. This was no longer going to be what we would get. AAHHH!&lt;br /&gt;&lt;br /&gt;So, a think about it, a look around the code producing the files, and we come up with a strategy.&lt;br /&gt;&lt;br /&gt;1) Rename the 3 files such that the middle file only psuedo exists to the next part of the pipeline.&lt;br /&gt;&lt;br /&gt;2) Run the next part of the pipeline with only those two files.&lt;br /&gt;&lt;br /&gt;3) Rename the files back to the original expected names, moving in the middle file again.&lt;br /&gt;&lt;br /&gt;4) Keep our fingers crossed :)&lt;br /&gt;&lt;br /&gt;So, first things first, rename the files. Now at this point, we think there are only 3 input files to 'Stage 2' as I'll call it. By stripping out the middle descriptor, and renaming, we run the pipeline.&lt;br /&gt;&lt;br /&gt;Error...&lt;br /&gt;&lt;br /&gt;The error, 'Stage 2' believes there to be a middle descriptor still. It turns out two files are created before 'Stage 1', in prep for 'Stage 2', still with the middle descriptor in it. 'Stage 2' then reads this file, and submits the middle descriptor to the scripts it kicks off. Pants.&lt;br /&gt;&lt;br /&gt;So, change these created files. Unfortunately, this can't be done from the config file we create, so after creating the other files, go in and physically change them&lt;br /&gt;&lt;br /&gt;Result! But then...&lt;br /&gt;&lt;br /&gt;We thought it wanted 3 files, reduced to 2. Actually, it wants 6 reduced to 4. Another set of 3 files, are required.&lt;br /&gt;&lt;br /&gt;So, rename those and Result.&lt;br /&gt;&lt;br /&gt;I have now successfully run 'Stage 2'.&lt;br /&gt;&lt;br /&gt;The next step is to get the stuff back to how we expect for the production of our own output files. Wish me luck, I'm going in.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-901485931295786860?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/901485931295786860/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=901485931295786860' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/901485931295786860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/901485931295786860'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2010/01/finally-i-get-somewhere-i-hope.html' title='Finally, I get somewhere, I hope'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8130684175962705196</id><published>2009-12-29T00:40:00.000-08:00</published><updated>2009-12-29T01:09:34.738-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pragmatic'/><category scheme='http://www.blogger.com/atom/ns#' term='Pragmatic Thinking and Learning'/><category scheme='http://www.blogger.com/atom/ns#' term='pragprog'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><title type='text'>Pragmatic Thinking and Learning</title><content type='html'>I have just finished reading Pragmatic Thinking and Learning - Refactor your Wetware by Andy Hunt. This is a very interesting book, which is well worth a read by anyone who is interested in:&lt;br /&gt;&lt;br /&gt;a) Different ways of learning new skills&lt;br /&gt;b) The way the mind works&lt;br /&gt;c) The whole left side/right side debate&lt;br /&gt;d) You just fancy a change from an ordinary software development textbook.&lt;br /&gt;&lt;br /&gt;As I was reading it, a few things came to light that I did 'right' and 'wrong' last year.&lt;br /&gt;&lt;br /&gt;Learning Moose and Erlang.&lt;br /&gt;&lt;br /&gt;Last year I learnt Moose, and didn't learn Erlang. I set out to do both, but didn't manage it. Why? I am capable. I managed to learn to use oo perl, ruby and rails in 3 months, why not Moose and Erlang.&lt;br /&gt;&lt;br /&gt;Simple, I had no opportunity to play with Erlang, and I had no defined targets (I want to write &lt;an application&gt; in Erlang using concurrency by &lt;some achievable time period&gt;).&lt;br /&gt;&lt;br /&gt;However, with Moose, it was completely different. I wanted to write a pluggable pipeline system using a modern oo perl system which others would be able to use easily. I had time to play as I learnt the basics, and then start building the structure of the framework as I learnt more.&lt;br /&gt;&lt;br /&gt;Allowing time and scribbling.&lt;br /&gt;&lt;br /&gt;I started to turn away from my screen and think a bit more. I also scribbled more. I created more scrap paper this year than the previous. I roughly mapped out where the code went. This worked. Thinking back to when I set up the QC application and database, I just got on and did it. That took a lot longer to achieve than it should (I got a stern warning from my bosses boss about that!) whereas the pipeline was in theory tougher, but took less than a quarter of the time in the end to get to it's initial start.&lt;br /&gt;&lt;br /&gt;Both of these positives are described in the book as using R-mode to lead L-mode. Since I have seen some of this in action last year, I am going to try to use that in guiding me forward next year. I really need to learn some C. If there is likely to be space, I would like to learn Erlang still. However, the key is to be able to set a reason for doing so. I will.&lt;br /&gt;&lt;br /&gt;So, first off C. Erlang isn't required for my job at this time, so I'll leave it for now.&lt;br /&gt;&lt;br /&gt;My target for C. Finish going through the C book I have and then take a C script that my pluggable pipeline sets off, and try to work out how it all works, and see if I can refactor it. In the process of this, I should also try to write a small program to sort a user inputted list. This shoudl probably be my first mini-target once completing the book.&lt;br /&gt;&lt;br /&gt;There are lots of other things in this book to follow. I think I probably need to reread it. I'll not write about more here now (I have a 4 year old desperate to assemble a Lego Star Wars toy with me) but I shall try to keep up to date my blogging more, and detail when it is something in from this book which has helped guide me towards it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8130684175962705196?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8130684175962705196/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8130684175962705196' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8130684175962705196'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8130684175962705196'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/12/pragmatic-thinking-and-learning.html' title='Pragmatic Thinking and Learning'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7369455703062220680</id><published>2009-12-18T23:06:00.000-08:00</published><updated>2009-12-19T00:10:11.728-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='year review'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='agile development'/><title type='text'>Another year passes...</title><content type='html'>So, another work year has passed. With it including my 10th anniversary at The Sanger Institute, and 2 years as officially a software developer, and a change of team leader, its been a pretty full year.&lt;br /&gt;&lt;br /&gt;So, what have I achieved:&lt;br /&gt;&lt;br /&gt;Well, first off, I decided to &lt;a href="http://tinyurl.com/yjtwu4v"&gt;split up a project&lt;/a&gt; which helped us a lot, as we have been able to be more agile in changes and bug fixes when dealing with parts of our pipelines and code.&lt;br /&gt;&lt;br /&gt;Following the change of leader in our team, we started to spend some time looking at new technologies. I started to take a look message queues. See &lt;a href="http://tinyurl.com/ykz25pb"&gt;Stomping on the Rabbit&lt;/a&gt; and &lt;a href="http://tinyurl.com/yhqpbmc"&gt;Pt2&lt;/a&gt;.&lt;br /&gt;This was my first time at actually properly failing something. It felt good. I got lots of help from monadic and their team down in London relating to RabbitMQ, and we tried ActiveMQ as well. But the reason we failed it: after attempting a comprehensive test suite to test cases where servers go down, we decided that (at this time) it wasn't stable enough for passing around the code as reliably as we hoped. I spent about 3 weeks looking at this, but we believe that is time well spent. (Another team started to employ it directly, but continually had problems with their poller of a queue.)&lt;br /&gt;&lt;br /&gt;I also spent some time at this time taking a look at Erlang, but since there didn't seem to be much opportunity to apply this at work, I stopped for the time being.&lt;br /&gt;&lt;br /&gt;I then deployed the badminton ladder I was working on for the Sports and Social Club. See &lt;a href="http://vampiresoftware.blogspot.com/2009/06/playing-badminton-through-clearpress.html"&gt;Playing Badminton through Clearpress&lt;/a&gt;. This has gone down well, although I do need to make some improvements to the interface.&lt;br /&gt;&lt;br /&gt;We had been deliberating about making the &lt;a href="http://tinyurl.com/yjg8ys9"&gt;analysis pipeline pluggable&lt;/a&gt;, so I took this task on. I also used it as a chance to take a good look at &lt;a href="http://tinyurl.com/ygx82ad"&gt;Moose&lt;/a&gt; as an O-O framework. I have pretty much spent the rest of the year doing this.&lt;br /&gt;&lt;br /&gt;This had had me really excited, and probably the thing which has kept me most interested in my job for the last 6 months, and also one thing which proved I didn't waste my time splitting up the project earlier in the year.&lt;br /&gt;&lt;br /&gt;After much discussion of whether to relaunch scripts, or load everything to LSF in one go, we went for something that does as much as it can, then submits jobs sequentially to LSF, but with code being as decoupled as possible. You can see in the powerpoint presentation &lt;a href="http://tinyurl.com/yjg8ys9"&gt;Pluggable Pipelines&lt;/a&gt; the style we took. With the power of the Moose behind it utilising Roles and even developing my own CPAN extensions &lt;a href="http://tinyurl.com/po5voa"&gt;MooseX::File_or_DB::Storage&lt;/a&gt; and &lt;a href="http://tinyurl.com/yk3t62l"&gt;MooseX::AttributeCloner&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;With this pluggable pipeline setup, of course it is now multiple times easier to add new stuff, particularly QC testing. So, we need a way to display the results. The new boss is keen on Catalyst, so I decided now is the time to look at this. Whilst this project has moved onto another team member, I gave it a bit of a start, initially to see how it would deal with getting stuff from a file server rather than a database '&lt;a href="http://tinyurl.com/yzf5as5"&gt;Backending Catalyst&lt;/a&gt;'. A couple of book sources later and I get a working app up. In fact, really, the biggest challenge was tring to apply some css that I stole from two Rails apps.&lt;br /&gt;&lt;br /&gt;I pass my 10 year anniversary without a hitch, it seems. It is funny to find that I have worked somewhere for 10 years. Although saying that, I have done a number of different roles here, from washing glassware to sequencing dna,  to instituting the widespread use of Transposon Insertion Libraries in finishing, to Quality Control, to Software Development.&lt;br /&gt;&lt;br /&gt;And then the slope to Christmas. The pipeline systems aim to be flexible has now been pushed to the limit, with virtually unreported changes to the 3rd party pipeline which it has had to keep up with - and whilst the code unavoidably has to change, we have spent most time discussing the planned changes to cope, and testing it, rather than coding.&lt;br /&gt;&lt;br /&gt;I've also had to come to the decision that I think Class::Std is well and truly dead. RIP. You helped me become a better, O-O programmer, and you will be remembered with a fondness, but Moose has just dealt you too many blows.&lt;br /&gt;&lt;br /&gt;Overall, it has been quite a productive agile year. I'm taking a look at the C language and trying to lobby to get a course run at work on Moose and Catalyst. Hopefully, these will be big things for me next year. I look forward to it.&lt;br /&gt;&lt;br /&gt;Merry Christmas, and a Happy New Year to all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7369455703062220680?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7369455703062220680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7369455703062220680' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7369455703062220680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7369455703062220680'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/12/another-year-passes.html' title='Another year passes...'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5951791128190637266</id><published>2009-12-12T00:38:00.000-08:00</published><updated>2009-12-12T00:56:41.650-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><title type='text'>Speedy does it.</title><content type='html'>In order to extend the flexibility to the pipeline, I developed MooseX::AttributeCloner (see CPAN) in order to pass around variables set on the original command line.&lt;br /&gt;&lt;br /&gt;However, this has led to the need to redevelop some further loading scripts that had been originally written with Class::Std and Class::Accessor. Switching them to Moose has had a two-fold effect that I have been rather happy with.&lt;br /&gt;&lt;br /&gt;1) Less code. Since the Roles that I had written for the pipeline (but made sure I left as potentially 'common code'), consuming these ditched a substantial amount of code. Woohoo!&lt;br /&gt;&lt;br /&gt;2) Faster. The loading of the data runs exceptionally fast now. The dropping of the need to generate extra objects (since code was refactored into consumed roles) has increased the speed of data lookups. I haven't truly benchmarked the loading of the data, but something that was taking a couple of minutes to run through completely, now takes mere seconds. In fact, the logs don't show any change in the timestamp used in each of the print statements.&lt;br /&gt;&lt;br /&gt;The longer of the two refactored scripts had a reduction of nearly half the code. The shorter about 45%. The slow parts (conversion to xml and database loading) of the long script needs some refactoring of how it loads. (drop xml mid stage) but now it seems that some improvements have certainly been achieved with the switch to Moose.&lt;br /&gt;&lt;br /&gt;Another factor which has also been able to speed this up is the fact that, if the pipeline has told the scripts lots of information (such as file system paths, filenames, etc) then the code doesn't need to work it out again. A definite advantage of the combination of ustilising MooseX::Getopt and MooseX::AttributeCloner.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5951791128190637266?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5951791128190637266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5951791128190637266' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5951791128190637266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5951791128190637266'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/12/speedy-does-it.html' title='Speedy does it.'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8376486656608599443</id><published>2009-12-05T00:42:00.000-08:00</published><updated>2009-12-05T00:58:23.049-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='numpty'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='agile development'/><title type='text'>Always change the branch!</title><content type='html'>Again, this one of those blogs which have shown some numptiness by me.&lt;br /&gt;&lt;br /&gt;Why did some things break, when they were working yesterday.&lt;br /&gt;&lt;br /&gt;We have a lot of file passing to do, and unfortunately, a third party analysis pipeline starts changing file locations and filenames, such that we have to remain agile to cope with the third party changes.&lt;br /&gt;&lt;br /&gt;So, we discover this happening and quickly fix our pipeline to cope with a filename change. Deploy and go, everything is working as expected.&lt;br /&gt;&lt;br /&gt;A new release of the pipeline occurs from the development branch into trunk, and then deploy. But suddenly, the files aren't being found as expected. What has happened?&lt;br /&gt;&lt;br /&gt;Quite simply, I didn't make a corresponding change in the development branch for the original bugfix. 4 hours of developer time spent trying to track this down for something that I broke my own rule.&lt;br /&gt;&lt;br /&gt;Always change the branch/backport trunk to ensure that the bug is fixed everywhere.&lt;br /&gt;&lt;br /&gt;Sometimes, just trying to be too agile, sorry read trying to fix a bug fast, just causes the bug to get reimplemented further down the line. Take your time Brown! A little caution and doing it right will make it better.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8376486656608599443?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8376486656608599443/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8376486656608599443' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8376486656608599443'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8376486656608599443'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/12/always-change-branch.html' title='Always change the branch!'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7947379696681231447</id><published>2009-11-26T02:12:00.000-08:00</published><updated>2009-11-26T02:25:32.729-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perlcritic'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Schoolboy error teaches a lesson.</title><content type='html'>This is not a slander of Perl::Critic, but more a need to ensure that I remember the following.&lt;br /&gt;&lt;br /&gt;Things break when there are no tests!&lt;br /&gt;&lt;br /&gt;perl critic threw a paddy over&lt;br /&gt;&lt;br /&gt;print STDERR q{Hello World!} or carp;&lt;br /&gt;&lt;br /&gt;Because STDERR is not preceded by a *&lt;br /&gt;&lt;br /&gt;So in my program I preceded to be a good little programmer, and put a * in front of all my STDERRs. (Before people ask, this is in a help statement to be outputted to the user if they didn't provide a run id, not really Hello World!, and was actually a quickie script to fix an external bug initially).&lt;br /&gt;&lt;br /&gt;In PBP, it mentions the good practice of surrounding *STDERR with braces, but in my case, Perl::Critic didn't throw for this.&lt;br /&gt;&lt;br /&gt;Now, here is my problem (my fault). I didn't have a test which at least compiled this script.&lt;br /&gt;&lt;br /&gt;Can you guess what happens next? It goes into production, is launched and fails to compile, since&lt;br /&gt;&lt;br /&gt;print *STDERR q{Hello World!} or carp;&lt;br /&gt;&lt;br /&gt;isn't allowed. Here it is in a one liner:&lt;br /&gt;&lt;br /&gt;&gt;perl -e 'print *STDERR qq{hello world};'&lt;br /&gt;syntax error at -e line 1, near "*STDERR qq{hello world}"&lt;br /&gt;Execution of -e aborted due to compilation errors.&lt;br /&gt;&lt;br /&gt;AAAGGHHH! It is a basic schoolboy error, I'm sure, but certainly teaches a lesson after around an hour of digging through logs to try to work out exactly why it worked last week, and not this.&lt;br /&gt;&lt;br /&gt;All braced and ready to fly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7947379696681231447?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7947379696681231447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7947379696681231447' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7947379696681231447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7947379696681231447'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/11/schoolboy-error-teaches-lesson.html' title='Schoolboy error teaches a lesson.'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-974586493517089910</id><published>2009-11-25T05:15:00.000-08:00</published><updated>2009-11-25T05:27:58.510-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MooseX::AttributeCloner'/><category scheme='http://www.blogger.com/atom/ns#' term='cpan'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Cloning via the command line</title><content type='html'>I have update MooseX::AttributeCloner today on CPAN - v0.17&lt;br /&gt;&lt;br /&gt;It had a few revisions over the last few days. Some due to the CPAN testers firing me back errors in my tests, some due to development requirements.&lt;br /&gt;&lt;br /&gt;The last new thing about it though is the method 'attributes_as_command_options'.&lt;br /&gt;&lt;br /&gt;This method goes through all of your built non-private attributes which have an init_arg, and builds a string of them for using in a command line.&lt;br /&gt;&lt;br /&gt;The default is to render them as follows&lt;br /&gt;&lt;br /&gt;--Boolean --attr1 val1 --hash_attr key1=val1 --hash_attr key2=val2&lt;br /&gt;&lt;br /&gt;but you can request that the values be quoted, an = sign be placed between arg and val, and that only a single dash is used.&lt;br /&gt;&lt;br /&gt;You can also exclude some attributes by supplying an arrayref of 'init_arg's to be excluded.&lt;br /&gt;&lt;br /&gt;For more info, see the &lt;a href="http://search.cpan.org/~setitesuk/MooseX-AttributeCloner-0.17/lib/MooseX/AttributeCloner.pm"&gt;CPAN page&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;or once installed, perldoc MooseX::AttributeCloner&lt;br /&gt;&lt;br /&gt;Unlike the new_from_cloned_attributes, you cannot supply additional key/value pairs to be added, but then, if you are generating a command line you can always add these yourself.&lt;br /&gt;&lt;br /&gt;I hope that this proves of use to some people. It is certainly helping to reduce code and increase the flexibility of the pluggable pipeline system I have been developing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-974586493517089910?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/974586493517089910/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=974586493517089910' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/974586493517089910'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/974586493517089910'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/11/cloning-via-command-line.html' title='Cloning via the command line'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4087498925565078319</id><published>2009-11-17T00:06:00.000-08:00</published><updated>2009-11-17T00:55:48.223-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MooseX::Getopt'/><category scheme='http://www.blogger.com/atom/ns#' term='MooseX::AttributeCloner'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose::Role'/><title type='text'>Refactoring into Roles</title><content type='html'>So, the pipeline moves along, and then the bossman says:&lt;br /&gt;&lt;br /&gt;'I want to be able to run it outside of the normal pipeline locations - how do I do it?'&lt;br /&gt;&lt;br /&gt;After a discussion as to why you want to do this (since the normal person responsible for testing out new Illumina pipelines often does it in the usual place, moving softlinks as necessary), it is that he wanted to run some stuff to dump the output into his home directory.&lt;br /&gt;&lt;br /&gt;OK, if that is what you want.&lt;br /&gt;&lt;br /&gt;'Oh yes, also, can we make it more generic. A lot of things we have done imply the setup we have here (most of which is generated by the Illumina pipeline) and I would like it to move away from that'.&lt;br /&gt;&lt;br /&gt;This is a major refactor then. Separate Business logic from Generic logic, and allow everything to be user defined, should the user want to.&lt;br /&gt;&lt;br /&gt;Looking at it, it seems an obvious thing really, but at the time I have to say, I panicked a bit. The pipeline has been written to be pluggable, so new 'components' can be switched in or out quickly, but I hadn't really planned it to be used outside of the current setup. The principle is easy enough to apply elsewhere, and should not take long to set up, but the components were specialised to apply to what they represent.&lt;br /&gt;&lt;br /&gt;So, the first thing I did - go and get a drink. I don't drink Tea or Coffee, but popping off for a break seemed like the right thing to do. This break lasted a while, as I thought about strategies to take. Should I go for all new objects, passing them around. How about pushing them from the command line? MooseX::Getopt seemed to give an option to put any attributes onto the command line, but what about subsequent component objects from the pipeline?&lt;br /&gt;&lt;br /&gt;In the end, I wondered about using Moose Roles. The Manual seemed to suggest that this could be a good way forward. A 'class' that doesn't need to be instantiated or extended, but instead is consumed to become part of the class that you want. So, everything could have all the same attributes, in exactly the same way.&lt;br /&gt;&lt;br /&gt;Now, I have to say, I could see an immediate danger here. If everything can be given to everything, then what makes anything different to be a component, or the pipeline 'flag waver'. So, I needed to be sensible, in that the pipelines need to match the components that are going to be launched from them, but not others.&lt;br /&gt;&lt;br /&gt;First job: Sort out the pipelines. This was quite simple, and needed doing anyway, since the flag waver had a component launcher for every possible component, but most components are only used for 1 of 3 pipelines, so separate out the 3 pipeline components into subclasses. This had the added advantage of naming the 3 pipeline flag wavers as well.&lt;br /&gt;&lt;br /&gt;OK, so what's the next thing.&lt;br /&gt;&lt;br /&gt;Go through all components looking for common attributes and methods, and labelling them as generic, or specific. Once I had done this, I created some roles which I refactored these into. Sometimes leaving them in the class if they were unnecessary to be available in multiple classes, otherwise putting them in a role which described if they were generic/business logic and which described the type of feature they give.&lt;br /&gt;&lt;br /&gt;After that, I just needed to apply roles to classes which meant that the flag waver had the same attributes as any component classes it would launch. This enables me to have the attribute value in the flag waver, and pass it to anything launched from it.&lt;br /&gt;&lt;br /&gt;This left a headache in that new instantiation would need me to loop through all attributes and pass them through. So, I put together another role to do this (see http://vampiresoftware.blogspot.com/2009/11/moosexattributecloner.html for details on this).&lt;br /&gt;&lt;br /&gt;So, now I'm left with the final problem, how to allow the user to run a pipeline with the options they want. A quick solution to this was already being used by another of my team, MooseX::Getopt. This is a great Role to apply to a class, which then enables any attribute to instantly become a command line option. Since the the Roles created above give attributes to a class, then they become command line options. Hurrah, problem solved.&lt;br /&gt;&lt;br /&gt;So, after I initially thought that Roles would not really be worth it much, considering how much subclassing I have normally done and have been used to, now I'm convinced that this is a very useful technology.&lt;br /&gt;&lt;br /&gt;Result of all this refactoring - over 900 lines of code lost. The value according to sloccount if $35,000 less. The code is now more maintainable, and more useful for users who want to define their own parameters.&lt;br /&gt;&lt;br /&gt;Sorry Class::Std, you have just had another nail forcefully bashed into your coffin.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-4087498925565078319?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4087498925565078319/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4087498925565078319' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4087498925565078319'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4087498925565078319'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/11/refactoring-into-roles.html' title='Refactoring into Roles'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8721801591594183266</id><published>2009-11-08T14:05:00.000-08:00</published><updated>2009-11-08T14:31:16.746-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MooseX'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose::Role'/><title type='text'>MooseX::AttributeCloner</title><content type='html'>I have just released a MooseX::AttributeCloner to the CPAN. The purpose of this role is to go pass all built attribute values from one class object to a newly instantiated object.&lt;br /&gt;&lt;br /&gt;The reason for the creation of this role is for our pluggable pipeline system, where we want to be able to pass any parameters from the command line to any of the objects which are responsible for submitting jobs.&lt;br /&gt;&lt;br /&gt;The Role inspects all the attributes the class has via meta, and checks that the attribute is defined. If it is, it passes the value into a hash, which it then uses in the object instantiation of the new object.&lt;br /&gt;&lt;br /&gt;my $object_with_attributes = $self-&gt;new_with_cloned_attributes('Package::Name');&lt;br /&gt;&lt;br /&gt;This will take $self's built attributes, and uses them in a hash_ref to create a new object instance of 'Package::Name'.&lt;br /&gt;&lt;br /&gt;It does check the init_arg of the attribute in $self, and uses that as the key for the value. If you have a Ref in the attribute, then it will be the same Ref in the new object. (So, in this case, it isn't strictly cloned, please don't be pedantic).&lt;br /&gt;&lt;br /&gt;If you wish to pass through additional information, or use a different value for an attribute that you already have a value for in the current object, then a hash_ref can also be provided with key/value pairs so that these can be used/added as as well&lt;br /&gt;&lt;br /&gt;my $object_with_attributes = $self-&gt;new_with_cloned_attributes('Package::Name',{&lt;br /&gt;  attr1 =&gt; q{val1},...&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;There are also two additional methods provided by this role, 'attributes_as_json' and attributes_as_escaped_json'. With these methods, you can get a JSON string with the values of all built attributes, which you could then use elsewhere, such as just building a data hash or pushing into a commandline. More info can be found in the Documentation, but it is worth noting, objects are not stringified, throughout the whole data structure. In the case where this would be in an array, a null will be found instead.&lt;br /&gt;&lt;br /&gt;I hope that this will be found to be useful. Please give me any feedback for this that you wish.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8721801591594183266?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8721801591594183266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8721801591594183266' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8721801591594183266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8721801591594183266'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/11/moosexattributecloner.html' title='MooseX::AttributeCloner'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7627226201832328586</id><published>2009-11-06T03:27:00.000-08:00</published><updated>2009-11-06T03:42:52.421-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose::Role'/><title type='text'>Extending a Role attribute</title><content type='html'>Not so much a blog post, more a blog question.&lt;br /&gt;&lt;br /&gt;When you are 'extend'ing a Moose base class, Attributes can be extended&lt;br /&gt;&lt;br /&gt;package Super::Hero;&lt;br /&gt;use Moose;&lt;br /&gt;&lt;br /&gt;has q{super_abilitity} =&gt; (isa =&gt; q{Str}, is =&gt; q{rw});&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;package My::Hero;&lt;br /&gt;use Moose;&lt;br /&gt;extends qw{Super::Hero};&lt;br /&gt;&lt;br /&gt;has q{+super_abilitity} =&gt; (required =&gt; 1);&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;However, in this case, I would like Super::Hero to be a Role to be consumed by some class.&lt;br /&gt;&lt;br /&gt;package Super::Hero;&lt;br /&gt;use Moose::Role;&lt;br /&gt;&lt;br /&gt;has q{super_abilitity} =&gt; (isa =&gt; q{Str}, is =&gt; q{rw});&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;However, the following doesn't work:&lt;br /&gt;&lt;br /&gt;package My::Hero;&lt;br /&gt;use Moose;&lt;br /&gt;&lt;br /&gt;with qw{Super::Hero};&lt;br /&gt;&lt;br /&gt;has q{+super_abilitity} =&gt; (required =&gt; 1);&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;The required 'extension' is just ignored. I have to actually declare the whole attribute again.&lt;br /&gt;&lt;br /&gt;package My::Hero;&lt;br /&gt;use Moose;&lt;br /&gt;&lt;br /&gt;with qw{Super::Hero};&lt;br /&gt;&lt;br /&gt;has q{super_abilitity} =&gt; (isa =&gt; q{Str}, is =&gt; q{ro}, required =&gt; 1);&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;I can't find anything in CPAN Documentation to confirm or deny that this the deliberate design. Does anyone have any suggestions as to any solutions to this?&lt;br /&gt;&lt;br /&gt;Any help appreciated.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7627226201832328586?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7627226201832328586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7627226201832328586' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7627226201832328586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7627226201832328586'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/11/extending-role-attribute.html' title='Extending a Role attribute'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3896868163255082330</id><published>2009-10-31T00:58:00.000-07:00</published><updated>2009-10-31T01:08:57.973-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='human genome project'/><category scheme='http://www.blogger.com/atom/ns#' term='teaching'/><category scheme='http://www.blogger.com/atom/ns#' term='life'/><category scheme='http://www.blogger.com/atom/ns#' term='teacher'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='career'/><title type='text'>10 years at The Sanger - Have I finally achieved my career goal</title><content type='html'>Friday the 30th was my last working day of year 10 at the Sanger Institute. I have worked in 4 teams whilst there, plus I had been there for 2.5 months before officially starting temping in the glasswash team. Also, in January, I hit the momentous age of 35. (Which, according to bible references to 3score and 10 being the life of a man, makes me middle aged).&lt;br /&gt;&lt;br /&gt;So I wonder if I have finally reached what I wanted my career to really be. Over the next month, NaNoWriMo (http://www.nanowrimo.org/) is on and I have decided that I might just spend the next month putting to paper my life so far. I don't claim it will be great (I'm fairly average in most things), but I think it will be good to recap and look back, coming from a middle class background, who wanted to be a bricklayer when I played with Lego aged 5 and a computer programmer aged 13 (but couldn't find anything at school to help him), to being a teacher, scientist on Human Genome Project and a Perl developer.&lt;br /&gt;&lt;br /&gt;Who knows, maybe it will interest people, maybe it won't. But a life recap can't be bad.&lt;br /&gt;And maybe it will just make me think, life really isn't too bad, you know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3896868163255082330?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3896868163255082330/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3896868163255082330' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3896868163255082330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3896868163255082330'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/10/10-years-at-sanger-have-i-finally.html' title='10 years at The Sanger - Have I finally achieved my career goal'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1710249108987327032</id><published>2009-10-25T00:44:00.000-07:00</published><updated>2009-10-25T00:50:33.994-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='catalyst'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><title type='text'>Course on Moose and Catalyst</title><content type='html'>I work at the Wellcome Trust Sanger Institute. In my team, New Pipeline Development, we have identified a need for some training in Moose and Catalyst. If anyone has any course recommendations, I'd be interested in hearing about them to propose to my team and training co-ordinator.&lt;br /&gt;&lt;br /&gt;The course can be aimed at people who are experienced Perl developers, but only have a small amount of experience of Moose and Catalyst (as anyone who reads my blog will probably identify).&lt;br /&gt;&lt;br /&gt;Replies as Comments, or emails will be appreciated. Many Thanks in advance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1710249108987327032?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1710249108987327032/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1710249108987327032' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1710249108987327032'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1710249108987327032'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/10/course-on-moose-and-catalyst.html' title='Course on Moose and Catalyst'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-690200582342572067</id><published>2009-10-21T06:20:00.000-07:00</published><updated>2009-10-21T06:23:31.022-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perlcritic'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Does anyone else do this?</title><content type='html'>I've been away on holiday, and I'm just going back over some comments left on my blog, and got this one?&lt;br /&gt;&lt;br /&gt;"Is that a new fashion to use q{string} instead of 'string'?"&lt;br /&gt;&lt;br /&gt;My response:&lt;br /&gt;&lt;br /&gt;"Just a habit I have gotten into since using perl::critic and PBP.&lt;br /&gt;&lt;br /&gt;As '' and ' ' are not allowed by 'the rulez' and you should use q{} and q{ }, (and their double quote equivalents}, I have the coding habit of just using then straight off."&lt;br /&gt;&lt;br /&gt;Anyone else doing this as a matter of course, or is it just me? I notice going through a number of recent books, ' and " are used in most of the code examples.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-690200582342572067?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/690200582342572067/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=690200582342572067' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/690200582342572067'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/690200582342572067'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/10/does-anyone-else-do-this.html' title='Does anyone else do this?'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-2536593134978701709</id><published>2009-10-12T00:09:00.000-07:00</published><updated>2009-10-12T00:17:12.860-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='ironman'/><title type='text'>How to put your perl ironman pic on blogspot</title><content type='html'>Go to your dashboard, and under the chosen blog, select layout&lt;br /&gt;&lt;br /&gt;Under Add and arrange page elements, select one of the two add a gadget depending if you want it on your sidebar, or at the bottom.&lt;br /&gt;&lt;br /&gt;Select the html/javascript gadget.&lt;br /&gt;&lt;br /&gt;Give it a title.&lt;br /&gt;&lt;br /&gt;In content type a html img tag containing the following&lt;br /&gt;&lt;br /&gt;http://ironman.enlightenedperl.org/munger/mybadge/male/setitesuk.png&lt;br /&gt;&lt;br /&gt;replacing setitesuk with your username or nickname (male can be switched to female).&lt;br /&gt;&lt;br /&gt;click save and hey presto, it will now appear the next time you load your blog.&lt;br /&gt;&lt;br /&gt;Note: I am sure I am duplicating this post from somewhere. Partly, this is for my own info, partly to help anyone who also can't find the details easily (apart from mst's blog about the url for the image.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-2536593134978701709?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/2536593134978701709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=2536593134978701709' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2536593134978701709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2536593134978701709'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/10/how-to-put-your-perl-ironman-pic-on.html' title='How to put your perl ironman pic on blogspot'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-6996068882457541965</id><published>2009-10-11T00:32:00.000-07:00</published><updated>2009-10-11T00:36:45.726-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><title type='text'>Is it easier to be specific?</title><content type='html'>I'm re-assessing some code, trying to refactor it into a re-usable role. Simple, you might think? But is it?&lt;br /&gt;&lt;br /&gt;It is very easy to write your code to perform a task on a specific item, or in a specific way. From variable names which mean something tangible, to methods designed to act on a pathway which is unique to your production setup. But, how do you make it more usable?&lt;br /&gt;&lt;br /&gt;1 - Variable-Method names&lt;br /&gt;&lt;br /&gt;I was taught to make my variable/method names mean something. This makes the code more readable.&lt;br /&gt;&lt;br /&gt;$donut  = q{jam donut};&lt;br /&gt;...&lt;br /&gt;eat($donut);&lt;br /&gt;&lt;br /&gt;instead of&lt;br /&gt;&lt;br /&gt;$d = q{jam donut};&lt;br /&gt;...&lt;br /&gt;eat($d);&lt;br /&gt;&lt;br /&gt;This is a trivial example, but the principle is there.&lt;br /&gt;&lt;br /&gt;However, you (read I) can take it too far. One such point is in directories. For the analysis pipeline, we end up with a directory, after a step called GERALD, called GERALD-date.&lt;br /&gt;&lt;br /&gt;In the code, we put this into $gerald_dir. Sounds reasonable. Everywhere I read $gerald_dir, I know exactly what it represents.&lt;br /&gt;&lt;br /&gt;However, here is the problem. What happens when the step and directory are renamed Harold. Whilst the principle is the same, and the same files are there, suddenly the variable name is wrong. Just grepping the filesystem won't find something like Gerald. At this point you are probably screaming at me, give it a semantic name, and document what it represents.&lt;br /&gt;&lt;br /&gt;Exactly, but that is easy with internal local variable names, not so with public exposed method names. Suddenly I need the role to include a deprecation cycle for the replacement method names, aaahhh!&lt;br /&gt;&lt;br /&gt;2 - Application/Locally specific&lt;br /&gt;&lt;br /&gt;Anyone should argue that locally specific logic should exist as far up as possible, leaving it out of the generic process end logic as much as possible. No arguements there.&lt;br /&gt;&lt;br /&gt;But how do you determine which is app specific, and which is generic.&lt;br /&gt;&lt;br /&gt;Obviously, naming conventions are app specific. Or are they? Many things might need to know how to construct a filename in a particular way.&lt;br /&gt;&lt;br /&gt;Ok, then how about directory structure? Again, you may find many apps wanting to access the Recalibrated data dir. They all need to know how to do get there.&lt;br /&gt;&lt;br /&gt;This is, as you can see, quite a grey area. One where, as I am finding, I think the refactor into a generic role still needs to be a little more specific than might be first thought. You can't get to a directory without at least some knowledge of where it is likely to be. You can't open a file without at least some knowledge of how it is named.&lt;br /&gt;&lt;br /&gt;Determine a row in a database - Some way of constructing a query to get it...&lt;br /&gt;&lt;br /&gt;Paul Weller said "No one ever said it was gonna be easy", and I wouldnt want it any other way, but remember, if you want your code to be reusable, try to make names semantic, but not specific, and document what they represent.&lt;br /&gt;&lt;br /&gt;Also, ensure you document why you process with particular assumptions. At least then, no-one can say you didn't tell them.&lt;br /&gt;&lt;br /&gt;That is my plan at least (where I can) from now on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-6996068882457541965?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/6996068882457541965/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=6996068882457541965' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6996068882457541965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6996068882457541965'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/10/is-it-easier-to-be-specific.html' title='Is it easier to be specific?'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-639659399673538310</id><published>2009-10-10T01:09:00.000-07:00</published><updated>2009-10-10T01:26:22.723-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rw/ro'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='attributes'/><title type='text'>Just a bit of info</title><content type='html'>Whilst I don't have the code here to show this, an interesting thing that we found this week regarding a Moose attribute.&lt;br /&gt;&lt;br /&gt;has q{attr} =&gt; (isa =&gt; q{Str}, is =&gt; q{rw});&lt;br /&gt;&lt;br /&gt;This makes $class-&gt;attr() both reader and writer.&lt;br /&gt;&lt;br /&gt;Now, it is mentioned in the manual that if you specify an attribute option, it will override the default by Moose, but what I was interested in was the error message.&lt;br /&gt;&lt;br /&gt;has q{attr} =&gt; (isa =&gt; q{Str}, is =&gt; q{rw}, writer =&gt; q{_set_attr});&lt;br /&gt;&lt;br /&gt;The error message you get here, if you try to use&lt;br /&gt;&lt;br /&gt;$class-&gt;attr($some_string);&lt;br /&gt;&lt;br /&gt;Is that you are trying to modify a read_only attribute. I was expecting it to have something different to this, since 'is =&gt; q{rw}'.&lt;br /&gt;&lt;br /&gt;Using 'is =&gt; q{ro}' still allows you to use your private writer to set the value.&lt;br /&gt;&lt;br /&gt;Personally, I always use 'is =&gt; q{ro}' unless I want the attr name to be the writer, but this is mildly interesting that setting a writer effectively overwrites the declaration of rw to be ro. Does this therefore make it more or less confusing to the user?&lt;br /&gt;&lt;br /&gt;Looking at it from two different perspectives:&lt;br /&gt;&lt;br /&gt;Public attribute setting:&lt;br /&gt;&lt;br /&gt;Someone inspecting the code for this attribute would see rw and this would tell them, hey you can set this. Hopefully they would look for a writer option before diving in to use the attr name itself. But I can see the use of declaring rw in order to give some hint.&lt;br /&gt;&lt;br /&gt;Private attribute setting:&lt;br /&gt;&lt;br /&gt;Someone inspecting the code would see rw, and may assume that this means they should be able to set this attribute. However, the writer should only be used internally to the class, therefore giving a hint that it can, and perhaps should, be overwritten if needed. Of course, as with any situation, let the buyer beware, this functionality may change.&lt;br /&gt;However, declaring ro, you are dropping the hint that outside of the class, if you didn't set it on construction, you really shouldn't be thinking of doing so.&lt;br /&gt;&lt;br /&gt;I'll leave it up to someone else to determine a best practice. It was an interesting thing to discover. I shall stick with declaring 'is =&gt; q{ro}, writer =&gt; q{_set_attr}' since most of the time, it is private to the class to set anyway.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-639659399673538310?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/639659399673538310/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=639659399673538310' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/639659399673538310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/639659399673538310'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/10/just-bit-of-info.html' title='Just a bit of info'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5555702960642206972</id><published>2009-10-05T01:43:00.000-07:00</published><updated>2009-10-05T01:45:05.763-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='DBIx::Class'/><category scheme='http://www.blogger.com/atom/ns#' term='catalyst'/><category scheme='http://www.blogger.com/atom/ns#' term='webapps'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='Reaction'/><title type='text'>The Definitive Guide to Catalyst</title><content type='html'>I have just finished reading The Definitive Guide to Catalyst - Diment and Trout (Apress). As Catalyst Newbie, all I can say is buy and read this book.&lt;br /&gt;&lt;br /&gt;It has lots of helpful advice and guidance, from creating your first application to extending Catalyst, and lots in between.&lt;br /&gt;&lt;br /&gt;It also takes a good look at DBIx::Class, Moose and the Reaction framework, plus a good explanation of the MVC pattern.&lt;br /&gt;&lt;br /&gt;I think this book is going to prove a valuable addition to our teams bookshelf, and be referenced for some time to come.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5555702960642206972?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5555702960642206972/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5555702960642206972' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5555702960642206972'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5555702960642206972'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/10/definitive-guide-to-catalyst.html' title='The Definitive Guide to Catalyst'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-2151876092011928263</id><published>2009-10-05T00:27:00.000-07:00</published><updated>2009-10-05T00:30:52.924-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LSF'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='pluggable'/><category scheme='http://www.blogger.com/atom/ns#' term='agile development'/><category scheme='http://www.blogger.com/atom/ns#' term='pipelines'/><title type='text'>Pluggable pipelines - the return</title><content type='html'>On Friday, we deployed v5.0 of the new pluggable style analysis pipeline. This now includes a pluggable analysis pipeline, the archival file creation, and qc pipeline, and archival pipline.&lt;br /&gt;&lt;br /&gt;With this new setup, we can now add additional steps (or remove them) easily in a matter of a few hours (usually actually minutes, but then we need those tests).&lt;br /&gt;&lt;br /&gt;If you look back to my previous post, the style is there, and we have found it really works well. It is keeping the idea of individual application code separate from the pipeline code which runs it, and even loosely couples individual apps so they run more smoothly.&lt;br /&gt;&lt;br /&gt;An additional bonus which has occured, without even trying, is that we have a significant improvement in run time. The reason is that, since we have queued all as separate jobs, instead of as batched makefiles, LSF runs them as it finds space. This means it doesn't need to find, or earmark, 8processors with max memory allowance, but just one or two with smaller memory, so they can be assigned more efficiently. This passes stuff through faster (assuming dependency trees pan out correctly), and so users can get the data quicker. Fantastic, and even better since we didn't plan for it.&lt;br /&gt;&lt;br /&gt;No time to stop yet though, we have lots more features to add, but since we have managed to move successfully to this more agile structure, I don't see them taking long to deploy.&lt;br /&gt;&lt;br /&gt;Here's to agile structures and development.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-2151876092011928263?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/2151876092011928263/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=2151876092011928263' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2151876092011928263'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2151876092011928263'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/10/pluggable-pipelines-return.html' title='Pluggable pipelines - the return'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1383809912863217575</id><published>2009-10-03T01:28:00.000-07:00</published><updated>2009-10-03T01:29:07.828-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose::Role'/><title type='text'>Role reversal</title><content type='html'>So, in our further investigations into using Moose, I have started to look at using Roles to produce reusable code instead of base classes.&lt;br /&gt;&lt;br /&gt;You can find out in the Moose::Manual pages on CPAN about setting them up, but what we had found initially confusing was how much we should make a role do. So our initial investigations just led us to produce ordinary Moose objects to do the functions, but not import them into the consuming objects.&lt;br /&gt;&lt;br /&gt;So, we move further on, and find ourselves repeating code as we don't want to import some objects which mostly do other things, so what is the solution.&lt;br /&gt;&lt;br /&gt;I'm now taking another look at Roles, to see if that might be the solution, but with another key factor. Keep the role doing as specific a thing as possible.&lt;br /&gt;&lt;br /&gt;An example of this is that we often need an object to have methods to expose a run id, a short run name and a run_folder. We do have an object which can provide this, along with other functions, but it is not really reusable in some situations. So I have just done a very small role run::short_info, which can then be used to import just these methods in.&lt;br /&gt;&lt;br /&gt;The result, a small amount of (what should be) very reusable code over most, if not all, of our frameworks.&lt;br /&gt;&lt;br /&gt;The next one I am doing is run::path_info, which will hopefully give just the next level of features that many need, but not all.&lt;br /&gt;&lt;br /&gt;This feels very much the right way to go now, and I hope that we will have a good amount of reusable code which will increase flexibility, and keep the code low maintenance. (Well, let's keep our fingers crossed).&lt;br /&gt;&lt;br /&gt;Just now to hope that someone fixes the requires attribute/method in Moose:Role soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1383809912863217575?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1383809912863217575/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1383809912863217575' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1383809912863217575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1383809912863217575'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/10/role-reversal.html' title='Role reversal'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-2877301037982110656</id><published>2009-09-29T03:41:00.000-07:00</published><updated>2009-09-29T04:02:52.518-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='qx//'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><category scheme='http://www.blogger.com/atom/ns#' term='system'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>system($cmd) vs qx/$cmd/ in tests</title><content type='html'>I have just found an interesting thing in my test suite.&lt;br /&gt;&lt;br /&gt;The other day, I had some silent failures since I was capturing the output, but not checking the error code, of some commands.&lt;br /&gt;&lt;br /&gt;my $output = `/some/command/which/normally/works -params`;&lt;br /&gt;&lt;br /&gt;Since the output was unimportant, but the command working was, I thought I'd switch as follows&lt;br /&gt;&lt;br /&gt;my $rc = system(q{/some/command/which/normally/works -params});&lt;br /&gt;if ($rc != 0) {&lt;br /&gt;  croak q{Meaningfull message};&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;All seems fine, until I run the test suite.&lt;br /&gt;t/20-mytests.t .. &lt;br /&gt;1..9&lt;br /&gt;ok 1&lt;br /&gt;ok 2&lt;br /&gt;ok 3&lt;br /&gt;ok 4&lt;br /&gt;ok 5&lt;br /&gt;ok 6&lt;br /&gt;ok 7&lt;br /&gt;ok 8&lt;br /&gt;00000000ok 9&lt;br /&gt;Failed 1/9 subtests &lt;br /&gt;&lt;br /&gt;Test Summary Report&lt;br /&gt;-------------------&lt;br /&gt;t/20-mytests.t (Wstat: 0 Tests: 8 Failed: 0)&lt;br /&gt;  Parse errors: Bad plan.  You planned 9 tests but ran 8.&lt;br /&gt;&lt;br /&gt;So what is wrong here.&lt;br /&gt;&lt;br /&gt;I don't know the ins and outs, but after a bit of debugging, it is that the system command under the test framework causes the TAP not to count the test, regardless of it passing or not.&lt;br /&gt;&lt;br /&gt;Solution to this:&lt;br /&gt;&lt;br /&gt;Go back to&lt;br /&gt;&lt;br /&gt;my $output = `/some/command/which/normally/works -params`;&lt;br /&gt;if ($CHILD_ERROR != 0) {&lt;br /&gt;  croak q{Meaningfull message};&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Everything is now fine again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-2877301037982110656?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/2877301037982110656/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=2877301037982110656' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2877301037982110656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2877301037982110656'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/09/systemcmd-vs-qxcmd-in-tests.html' title='system($cmd) vs qx/$cmd/ in tests'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5335332050088873695</id><published>2009-09-26T00:24:00.000-07:00</published><updated>2009-09-26T00:45:37.378-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='debug'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Carp'/><category scheme='http://www.blogger.com/atom/ns#' term='error messages'/><title type='text'>Sometimes there just isn't enough</title><content type='html'>I'm starting to get confused. (OK, starting is wrong). What is the problem with more error/debug than less.&lt;br /&gt;&lt;br /&gt;I mean, sure, your average user is not going to have the foggiest idea what your error information is, and so a nice helpful message is good. But what about in a system,(which has many many many tests) where the average time to run in production takes 1000 times longer than any of your tests. And you get for Joe User&lt;br /&gt;&lt;br /&gt;HI,&lt;br /&gt;&lt;br /&gt;I received this error message:&lt;br /&gt;&lt;br /&gt;An error occured in processing your request. Please let us know at line 360 in something.pl&lt;br /&gt;&lt;br /&gt;Could you do something about this. I had been running this process for 10 hours.&lt;br /&gt;&lt;br /&gt;Cheers&lt;br /&gt;&lt;br /&gt;Joe&lt;br /&gt;&lt;br /&gt;This is great, except, I know absolutely nothing further. Joe, understandably, reported the error he got (all of it!) and so thinks he has been helpful, but when you look at line 360, it's a croak of an eval block, where all your code is in multiple modules running inside it. AAAHHHH!&lt;br /&gt;&lt;br /&gt;From looking around (and I may only have looked at a relatively small subset) people seem to think that letting the user see all of the error stack is a bad thing. Why? Speaking with some users, they don't understand the output, and would like an additional friendly message, but want to be helpful. They just don't know what to say.&lt;br /&gt;&lt;br /&gt;My response to get info from Joe above would be:&lt;br /&gt;&lt;br /&gt;On which node did you run this?&lt;br /&gt;What parameters did you set/include?&lt;br /&gt;At what time did you start and stop this process?&lt;br /&gt;Did you have an error log?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To which Joe replies:&lt;br /&gt;&lt;br /&gt;Parameters: a,b,c&lt;br /&gt;I submitted it to the farm from work_cluster2&lt;br /&gt;My bsub command: ....&lt;br /&gt;Error log: dev/null (because, that is what he was instructed)&lt;br /&gt;&lt;br /&gt;Great, no further output. I can't reproduce, I can't write a test, I can't track down the problem without running the whole thing myself.&lt;br /&gt;&lt;br /&gt;So what is the potential solution:&lt;br /&gt;&lt;br /&gt;1) Don't be afraid to show your user what looks like the internal workings of the script. Give them the whole stack output. With a friendly message too, of course.&lt;br /&gt;2) Training to ensure that they write an error log (dev/null is not a log, but users don't know that often).&lt;br /&gt;3) Training to ensure that they email you/bug report through rt the whole stack. If they know it will help you solve their problem, they ought to be happy. (Certainly users I spoke to said they would be happy to provide 200 lines of 'garbage output' if it meant that I could solve a problem even 25% faster.&lt;br /&gt;&lt;br /&gt;I don't know who suggested it, or who started the culture. It might not be great for commercially sold apps, but certainly where you have internal or free (in it's many forms) software, then surely you shouldn't be worried about 'what the user might find out', because, I would be 95% certain, they don't actually care!&lt;br /&gt;&lt;br /&gt;So big it up for confess and cluck, or even occasional print 'debug info trace points' as they ought to be the way of the future, in dealing with those bugs in a timely fashion.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5335332050088873695?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5335332050088873695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5335332050088873695' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5335332050088873695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5335332050088873695'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/09/sometimes-there-just-isnt-enough.html' title='Sometimes there just isn&apos;t enough'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4666360461162309028</id><published>2009-09-18T23:55:00.000-07:00</published><updated>2009-09-19T00:16:23.827-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='readdir'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='sort'/><title type='text'>readdir in the wrong order</title><content type='html'>This week we found an interesting bug (which seems quite obvious really, but still threw a spanner in the works) which was that using the &lt;span style="font-weight:bold;"&gt;readdir&lt;/span&gt; function doesn't equate to &lt;span style="font-style:italic;"&gt;ls&lt;/span&gt;'ing a directory, i.e. you can't be sure that the files will be in alphanumeric order.&lt;br /&gt;&lt;br /&gt;So, as you can probably work out, I had a method using this function to obtain a list of files in a directory, and then was passing the list to another program.&lt;br /&gt;&lt;br /&gt;This program, although we make a promise that if two(or three if multpilexed) files for a lane generated with this program everything would be in the correct order, doesn't actually do any internal ordering of the list of files passed to it (where was that documented?) and so just &lt;span style="font-weight:bold;"&gt;readdir&lt;/span&gt; alone, passing the list of files, meant that they weren't guaranteed to be in the correct order needed. AAHHHH!&lt;br /&gt;&lt;br /&gt;So, a quick &lt;span style="font-weight:bold;"&gt;sort&lt;/span&gt; to the list, and everything is now fine, but that was a bit of a surprise. So was spending 2 days trying to reorder the files that had been created wrongly, although I now have a script that can do this should it happen again, which on the farm only takes about 10 minutes.&lt;br /&gt;&lt;br /&gt;So, as I said, an interesting bug. I will just have to remember that if I do anything with a list from &lt;span style="font-weight:bold;"&gt;readdir&lt;/span&gt; in future, run a &lt;span style="font-weight:bold;"&gt;sort&lt;/span&gt; on it afterwards, just in case.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-4666360461162309028?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4666360461162309028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4666360461162309028' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4666360461162309028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4666360461162309028'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/09/readdir-in-wrong-order.html' title='readdir in the wrong order'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7388640893630840758</id><published>2009-09-12T23:41:00.000-07:00</published><updated>2009-09-12T23:45:49.370-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MooseX'/><category scheme='http://www.blogger.com/atom/ns#' term='MooseX::Storage'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>My first CPAN module - update</title><content type='html'>I have been politely requested to rename my first module to MooseX::File_or_DB::Storage, so I have done so and it can now be found here:&lt;br /&gt;&lt;br /&gt;http://tinyurl.com/po5voa&lt;br /&gt;&lt;br /&gt;It is exactly the same as v0.2, just a different package name. MooseX::Storage::File_or_DB is scheduled for deletion off CPAN on Wed 16th, so I urge anyone to update now.&lt;br /&gt;&lt;br /&gt;Cheers&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7388640893630840758?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7388640893630840758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7388640893630840758' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7388640893630840758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7388640893630840758'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/09/my-first-cpan-module-update.html' title='My first CPAN module - update'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8995734476613055989</id><published>2009-09-07T05:33:00.000-07:00</published><updated>2009-09-07T06:16:46.933-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl Moose MooseX JSON'/><title type='text'>My first CPAN module</title><content type='html'>So I have pushed my first ever CPAN module, MooseX::Storage::File_or_DB&lt;br /&gt;&lt;br /&gt;http://tinyurl.com/l3xfkc&lt;br /&gt;&lt;br /&gt;I blogged about this as I started the module before&lt;br /&gt;&lt;br /&gt;http://vampiresoftware.blogspot.com/2009/08/filesystemdatabase-or-both.html&lt;br /&gt;&lt;br /&gt;and this weekend I was finally able to finish the first release.&lt;br /&gt;&lt;br /&gt;As of the moment, you need to use this by extending it&lt;br /&gt;&lt;br /&gt;package MyClass;&lt;br /&gt;use Moose;&lt;br /&gt;extends q{MooseX::Storage::File_or_DB};&lt;br /&gt;&lt;br /&gt;But I hope for a future release just to be able to&lt;br /&gt;&lt;br /&gt;use MooseX::Storage::File_or_DB;&lt;br /&gt;&lt;br /&gt;This gives functionality to enable you to write to either a file a JSON string, or a database, and re-instantiate the object from either.&lt;br /&gt;&lt;br /&gt;It makes heavy use of MooseX::Storage ( http://tinyurl.com/nujf4c ) - a big thanks to Tomas Doran for writing this - for inspecting the object, and providing the ability to write out to a file as a JSON string.&lt;br /&gt;&lt;br /&gt;I hope that this will prove useful. Please do read the POD/CPAN page before use, and contact me about anything you feel relating to this - all constructive comments gratefully received.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8995734476613055989?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8995734476613055989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8995734476613055989' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8995734476613055989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8995734476613055989'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/09/my-first-cpan-module.html' title='My first CPAN module'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7648934067643494435</id><published>2009-09-06T02:08:00.000-07:00</published><updated>2009-09-06T02:09:18.792-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perl catalyst Moose MooseX'/><title type='text'>Backending Catalyst</title><content type='html'>I am starting to look at Catalyst as a method to display some quality control results that are coming out of our analysis pipeline.&lt;br /&gt;ClearPress is a nice MVC webapp builder, but is quite a light weight framework, and uses Class::Accessor for object base. We would like to move towards using Moose based objects and need a way to integrate these into Catalyst.&lt;br /&gt;&lt;br /&gt;I am currently working my way through the latest Catalyst book (Diment &amp; Trout), but before this arrived I found we had the following book on our Safari Subscription - Catalyst: Accelerating Perl Web Application Development: Design, develop, test and deploy applications with the open-source Catalyst MVC framework - Jonathan Rockway.&lt;br /&gt;&lt;br /&gt;Now, note, I had been through the Tutorial on CPAN, but couldn't find on there anything about using a Filesystem as a source for the model (Did I miss something?), but this book luckily had a section on doing so.&lt;br /&gt;&lt;br /&gt;Firstly, why have we QC data in a filesystem?&lt;br /&gt;&lt;br /&gt;When we run the pipeline, this all happens on a staging area, which we write everything to, and then copy all our data into long term archival databases. The QC data is no exception, but we only want to archive the final agreed data. Bioinformaticians don't seem to ever be happy with a first pass that fails, if there is any chance it could be improved (i.e. a new test pipeline version, could rerunning jut squeeze 2% more...). As such we want to view the data in exactly the same way from the filesystem as from the database, because we don't want it stored until the last possible moment.&lt;br /&gt;&lt;br /&gt;What have we done for this?&lt;br /&gt;&lt;br /&gt;My team have been producing Moose objects which are:&lt;br /&gt;&lt;br /&gt;1) Producing the data&lt;br /&gt;2) Storing in JSON files (MooseX::Storage)&lt;br /&gt;3) Reading in JSON files (MooseX::Storage) to re-instantiate the object&lt;br /&gt;4) Saving to a Database (Fey)&lt;br /&gt;5) Re-instantiating from a Database&lt;br /&gt;&lt;br /&gt;I've been working with iterations of the objects, using the files, but want the objects to just sort it themselves - I shouldn't know where the data has come from, and these objects should be used in (in fact are being written for) other applications.&lt;br /&gt;&lt;br /&gt;Catalyst very much guides you to using a Database, and seems to prefer using DBIx::Class for this, so I need a way of guiding the Model to provide the correct objects, which are not generated directly from Catalyst helpers.&lt;br /&gt;&lt;br /&gt;What did I do?&lt;br /&gt;&lt;br /&gt;So in the above book, I found the section 'Implementing a FileSystem model'. This shows us how to create a Backend, which takes us out of the ordinary Model style, and the call the the Model returns this Backend object instead. We then use this Backend object to contain the logic which can be used to obtain the objects from somewhere outside of the Catalyst application, de-coupling the data models from the app, and therefore increasing flexibility and maintainability. As I said, these objects are actually being written within another application project.&lt;br /&gt;&lt;br /&gt;This has been an interesting venture, which has enabled me to write a web application which only concentrates on the logic for the view, and leave the data handling completely to someone else. We should be production ready with the application within the week, and displaying data for the users quickly and simply.&lt;br /&gt;&lt;br /&gt;What the betting someone asks if we can regenerate the data for all previous runs? I won't be betting against it, that's for sure.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7648934067643494435?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7648934067643494435/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7648934067643494435' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7648934067643494435'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7648934067643494435'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/09/backending-catalyst.html' title='Backending Catalyst'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5781517335369128437</id><published>2009-08-30T09:42:00.001-07:00</published><updated>2009-08-30T09:53:07.651-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MooseX'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><title type='text'>FileSystem/DataBase - or both?</title><content type='html'>I'm starting a new MooseX::Storage module called MooseX::Storage::File_or_DB.&lt;br /&gt;&lt;br /&gt;The objective is that you can use the standard MooseX::Storage to serialize out the Moose&lt;br /&gt;object as a JSON string to a file, and read it back again, but also save it out to&lt;br /&gt;a Database so that it can be used in a usual database way (i.e. interrogate the db using&lt;br /&gt;sql directly, so an attribute maps directly to a column).&lt;br /&gt;&lt;br /&gt;There are a number of ORM or object db modules on CPAN (DBIx::Class, Fey, KiokuDB) but all seem a bit difficult to give the option to also obtain and save out the object to a filesystem as well, for more short term storage prior to archival to the database.&lt;br /&gt;&lt;br /&gt;This is clearly something we need. MooseX::Storage is just the job for storing and retrieval from a filesystem, but linking both in one.&lt;br /&gt;&lt;br /&gt;So, I'm setting to work on something that will do both. The current work in progress is on GitHub here&lt;br /&gt;&lt;br /&gt;http://github.com/setitesuk/MooseX--Storage--File_or_DB&lt;br /&gt;&lt;br /&gt;I would ask people to take a look and see what they think. The POD is currently where I want to end up, but the tests are working, and I think it is going in the right direction. Looking forward to more work on this to make it ready to submit to CPAN.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5781517335369128437?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5781517335369128437/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5781517335369128437' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5781517335369128437'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5781517335369128437'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/08/filesystemdatabase-or-both.html' title='FileSystem/DataBase - or both?'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1210136576036228712</id><published>2009-08-28T04:06:00.000-07:00</published><updated>2009-08-28T04:20:16.247-07:00</updated><title type='text'>Am I too good</title><content type='html'>For our projects that are released through the Sanger Website as Open Source (most of our code is, but we don't have a specific release policy to putting it out there) we run David A. Wheelers sloccount to get a count of the lines of code.&lt;br /&gt;&lt;br /&gt;I have just done release-3.0 of the pluggable pipeline system, so I thought it might be fun to get the stats of this.&lt;br /&gt;&lt;br /&gt;Total Physical Source Lines of Code (SLOC) = 3,884&lt;br /&gt;&lt;br /&gt;That's good, I've been working on this for 7 weeks, with other projects.&lt;br /&gt;&lt;br /&gt;However, sloccount gives you further info:&lt;br /&gt;&lt;br /&gt;Development Effort Estimate, Person-Years (Person-Months) = 0.83 (9.98)&lt;br /&gt; (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))&lt;br /&gt;&lt;br /&gt;About 10 months development - I'd have been shot if what I have produced had taken that long :)&lt;br /&gt;&lt;br /&gt;Schedule Estimate, Years (Months)                         = 0.50 (5.99)&lt;br /&gt; (Basic COCOMO model, Months = 2.5 * (person-months**0.38))&lt;br /&gt;&lt;br /&gt;6 months scheduling.&lt;br /&gt;&lt;br /&gt;Estimated Average Number of Developers (Effort/Schedule)  = 1.66&lt;br /&gt;&lt;br /&gt;There's only me, and I've only been working for 7 weeks on this project!&lt;br /&gt;&lt;br /&gt;Total Estimated Cost to Develop                           = $ 112,301&lt;br /&gt; (average salary = $56,286/year, overhead = 2.40).&lt;br /&gt;&lt;br /&gt;I need to ask for a raise! In fact, my boss covers his ears and refuses to listen when I mention these numbers.&lt;br /&gt;&lt;br /&gt;Obviously, there has been discussion with other people about where the project is heading, but I think the Basic COCOMO model clearly doesn't quite cut the mustard with Agile Development practices. Or maybe I'm just too good.&lt;br /&gt;&lt;br /&gt;Still, it is fun to watch my boss run screaming, refusing to listen when I quote the estimated cost to develop. At least I think I am worth at least what I am paid :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1210136576036228712?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1210136576036228712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1210136576036228712' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1210136576036228712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1210136576036228712'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/08/am-i-too-good.html' title='Am I too good'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-6831570809314606717</id><published>2009-08-28T03:54:00.000-07:00</published><updated>2009-08-28T04:01:51.155-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='index'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>'Non-'Unique indexing</title><content type='html'>We wanted to make a column in our MySQL database table (using InnoDB) nullable, but use the column as part of a composite key.&lt;br /&gt;&lt;br /&gt;unique key C1,C2,C3,C4&lt;br /&gt;&lt;br /&gt;C4 can be null&lt;br /&gt;&lt;br /&gt;C1 C2 C3 C4&lt;br /&gt;&lt;br /&gt;enter the following:&lt;br /&gt;&lt;br /&gt;x  y  z  a&lt;br /&gt;&lt;br /&gt;goes into the table ok&lt;br /&gt;&lt;br /&gt;enter those again - error that we break unique constraint.&lt;br /&gt;&lt;br /&gt;This is as expected.&lt;br /&gt;&lt;br /&gt;enter&lt;br /&gt;&lt;br /&gt;x y z null&lt;br /&gt;&lt;br /&gt;goes into table ok&lt;br /&gt;&lt;br /&gt;enter those again - they also enter fine, and select * from table shows two separate row entries.&lt;br /&gt;&lt;br /&gt;So, basically, you can have a nullable field in a composite unique index, but if the column is null, you loose the unique index checking.&lt;br /&gt;&lt;br /&gt;I don't know about other DB's, but it is a shame that the null can't be part of the uniqueness of the index.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-6831570809314606717?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/6831570809314606717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=6831570809314606717' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6831570809314606717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6831570809314606717'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/08/non-unique-indexing.html' title='&apos;Non-&apos;Unique indexing'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3110092712581311249</id><published>2009-08-12T08:55:00.000-07:00</published><updated>2009-08-12T08:59:37.019-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MooseX'/><category scheme='http://www.blogger.com/atom/ns#' term='objects'/><category scheme='http://www.blogger.com/atom/ns#' term='cpan'/><category scheme='http://www.blogger.com/atom/ns#' term='Class::Std'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><category scheme='http://www.blogger.com/atom/ns#' term='Moose'/><category scheme='http://www.blogger.com/atom/ns#' term='clearpress'/><title type='text'>Musings on a Moose</title><content type='html'>come to sweden, see the majestic moose (paraphrased from Monty Python and the Holy Grail).&lt;br /&gt;&lt;br /&gt;I am starting to look at Moose as an alternative to Class::Std and other modules for OO Perl. It has very good support from the community, and looks to be fast becoming the framework of choice.&lt;br /&gt;&lt;br /&gt;I will start off by saying I like it. The setup of my::module is very easy, and reads very cleanly. I like the declaration of variable types on the accessors, and it obvious when an attribute is needed on new, (just set 'required' flag). I especially like the ability to make an attribute ro.&lt;br /&gt;&lt;br /&gt;However, that leads me to a small bugbear I have. I have mentioned before about encapsulation, Class::Std objects are blessed scalars, and as such can't have keys. Your attributes are set up as keys on internal hashes, and as such can only be exposed via a method.&lt;br /&gt;&lt;br /&gt;The Moose object created is a blessed hash, and, the attributes are stored in keys. This means that your user can still override the ro attribute by just using the key. Encapsulation is broken.&lt;br /&gt;&lt;br /&gt;Someone in my office used a good simile here, which I think he attributed to Larry Wall, which is:&lt;br /&gt;&lt;br /&gt;"Your neighbour stays out of your garden, because it is the right thing to do, not because you have a shotgun."&lt;br /&gt;&lt;br /&gt;That might be the case, but I'd rather enforce the use of accessors from the start than run the risk of user's using the key. However, I plan to try out MooseX::InsideOut to see if this can enforce this once I'm more tightly used to the basics.&lt;br /&gt;&lt;br /&gt;Another thing I find very good are the additional modifiers for the accessors. We have found that making use of predicates and lazy_build really improves the code layout and speed of object creation.&lt;br /&gt;&lt;br /&gt;I have now been building the pluggable pipeline project using Moose, and it has been very quick to build the objects and code. I would say that I think once upto speed, and once I have tested out MooseX::InsideOut, I think that development time of code should reduce by about 10% and code maintainability should go up about 25%.&lt;br /&gt;&lt;br /&gt;So a big thumbs up from myself, and my development team also. Whilst I think at the moment there would be no plans to convert our ClearPress based apps to Moose, I think I'm tempted to try to switch my Class::Std modules, and certainly all new projects will go that way.&lt;br /&gt;&lt;br /&gt;Big Thanks to all the people who work on Moose. Is there a book in the works? I for one would get it, I'd even contribute if you'd like.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3110092712581311249?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3110092712581311249/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3110092712581311249' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3110092712581311249'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3110092712581311249'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/08/musings-on-moose.html' title='Musings on a Moose'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5044325887181845917</id><published>2009-07-31T03:49:00.001-07:00</published><updated>2009-07-31T03:49:58.599-07:00</updated><title type='text'>Pluggable Pipelines</title><content type='html'>My current main project is to replace the end of our analysis pipeline. It is currently all in one module, archive.pm. As with all things which are in one single file, this has been extended and changed around, and has become unmanageable.&lt;br /&gt;&lt;br /&gt;We have also decided that we want to make it pluggable, so that we can easily add or remove parts depending on the project requirements.&lt;br /&gt;&lt;br /&gt;For this I looked at creating a flag waver, whose job it is to take an array of function names, and launch each in turn, capturing any return values and submitting them as requirements to be fulfilled that may be needed for the next function.&lt;br /&gt;&lt;br /&gt;The functions have no knowledge of anything except what requirements may come in, what information the object they control requires and how to capture and return further requirements for any processes further down the line.&lt;br /&gt;&lt;br /&gt;Most importantly, the functions know nothing about any other function.&lt;br /&gt;&lt;br /&gt;Each function loads an object, and gives it the parameters it needs. These objects handle doing any real work, which can be updating statuses, submitting jobs to LSF, manipulating files, obtaining data from databases/web services. All they have to return to the function which called them is something the next process might need to know for it's own ability to work. In our case an array of job ids from LSF submissions, to be used as job dependencies.&lt;br /&gt;&lt;br /&gt;The structure is therefore a flagwaver, which calls the functions in a user specified order, which in turn call objects submitting jobs, returning dependencies for the next function.&lt;br /&gt;&lt;br /&gt;What this means is that the flagwaver has very little responsibility itself. It relies on the user ensuring that any individual components will complete successfully (or at least error sensibly), and that the user has specified an order of components which will work (i.e. if 'B' depends upon an output of 'A', then they have put 'A' before 'B' in the array).&lt;br /&gt;&lt;br /&gt;In our case, the flagwaver does have a little specific knowledge in that we have coded a few function which, if the order specified has them next to each other, they could be run at the same time, so we can parallelise as much as possible, but that is in a specific subclass of the pluggable base module, and can be overridden.&lt;div style="width:425px;text-align:left" id="__ss_1794842"&gt;&lt;a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/setitesuk/pluggable-pipelines" title="Pluggable Pipelines"&gt;Pluggable Pipelines&lt;/a&gt;&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=pluggablestructure-090731054146-phpapp01&amp;stripped_title=pluggable-pipelines" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=pluggablestructure-090731054146-phpapp01&amp;stripped_title=pluggable-pipelines" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;"&gt;View more &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/setitesuk"&gt;setitesuk&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5044325887181845917?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5044325887181845917/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5044325887181845917' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5044325887181845917'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5044325887181845917'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/07/pluggable-pipelines.html' title='Pluggable Pipelines'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1932008656835986961</id><published>2009-06-26T00:07:00.000-07:00</published><updated>2009-08-12T09:01:13.595-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='badminton'/><category scheme='http://www.blogger.com/atom/ns#' term='github'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLLite'/><category scheme='http://www.blogger.com/atom/ns#' term='clearpress'/><title type='text'>Playing Badminton through Clearpress</title><content type='html'>Yesterday I launched, finally, my badminton ladder web application for our sports and social club. Initially, I write something about 3 years ago, which used flat csv files and a cgi script for each page, which dealt with generating the html and processing results, and updating the ladder, and....&lt;br /&gt;&lt;br /&gt;All not very practical, but at least showed what I could do at the time.&lt;br /&gt;&lt;br /&gt;Earlier this year we had a change of hardware for our webservers, and we lost the functionality, due to the time lag of all the files being kept up to date as they changed on the multiple servers. A big problem.&lt;br /&gt;&lt;br /&gt;So I decided it was time for a rewrite, which I did using Clearpress (http://clearpress.net/), trialling out git and github at the same time (I like this so much more than sourceforge! and svn).&lt;br /&gt;&lt;br /&gt;I have blogged about Clearpress and its MVC framework before, so I won't spend time doing so, but with a little work, the setup used 5 tables in a database to produce a reliable system.&lt;br /&gt;&lt;br /&gt;team - stores a team name, wins, losses and gives them a unique identifer&lt;br /&gt;player - stores a player name, email and gives them a unique identifier&lt;br /&gt;player_team - join table for a player to a team&lt;br /&gt;ladder_type - our ladder has three sub ladders, to deal with new teams and those which haven't played for a long time&lt;br /&gt;ladder - links team/position/ladder_type&lt;br /&gt;&lt;br /&gt;The whole thing can be found on github&lt;br /&gt;&lt;br /&gt;git://github.com/setitesuk/badminton-ladder.git&lt;br /&gt;&lt;br /&gt;It is currently set up to deploy using a SQLLite database, but in production use, we are using a mysql database, and the schema is there. You just need to modify the config.ini file to use a mysql database, which is supported through clearpress.&lt;br /&gt;&lt;br /&gt;So, if you are after a web app badminton ladder, then take a look. It is all available as Open Source (GNU Public Licence).&lt;br /&gt;&lt;br /&gt;Next, to create a Tennis Competition app.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1932008656835986961?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1932008656835986961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1932008656835986961' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1932008656835986961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1932008656835986961'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/06/playing-badminton-through-clearpress.html' title='Playing Badminton through Clearpress'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-9103065408282062158</id><published>2009-06-11T05:05:00.000-07:00</published><updated>2009-06-11T05:25:17.315-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PBP'/><category scheme='http://www.blogger.com/atom/ns#' term='perlcritic'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>lc(x) - possible bad practice?</title><content type='html'>use strict; use warnings;&lt;br /&gt;&lt;br /&gt;We all use the above, right? Well, certainly we should, and my team does;&lt;br /&gt;&lt;br /&gt;Now this always causes a warning to occur when testing an undefined value (exception - if the test is for it to be undef).&lt;br /&gt;&lt;br /&gt;my $arrayref = $self-&gt;method_returning_array_ref() || [];&lt;br /&gt;&lt;br /&gt;Now, assuming that the method will always return an arrayref or undef, I will have an arrayref.&lt;br /&gt;&lt;br /&gt;Now, it is common enough (returning stuff from an XML dom for example) that there is actually only 1 thing in the array, so I test against it&lt;br /&gt;&lt;br /&gt;if ($arrayref-&gt;[0] eq 'yes') {&lt;br /&gt;  do something..&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Now, if $arrayref-&gt;[0] is undef, this always throws an uninitialised variable warning. This normally leads me to change to&lt;br /&gt;&lt;br /&gt;if ($arrayref-&gt;[0] &amp;&amp; $arrayref-&gt;[0] eq 'yes') {&lt;br /&gt;  do something..&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;so that I don't spam up the logs with warnings.&lt;br /&gt;&lt;br /&gt;However, we discovered today that by lowercasing the $arrayref-&gt;[0] variable, this will turn an undef into an empty string (for the conditional), therefore dispensing with any warnings.&lt;br /&gt;&lt;br /&gt;if (lc$arrayref-&gt;[0] eq 'yes') {&lt;br /&gt;  do something..&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Is this good or bad coding practice?&lt;br /&gt;&lt;br /&gt;Reasons for it to be good&lt;br /&gt; - You do not need an extra conditional just to dispense with the warning&lt;br /&gt; - code less&lt;br /&gt;&lt;br /&gt;Reasons for it to be bad&lt;br /&gt; - It doesn't seem right&lt;br /&gt; - The conditional is no longer testing against the pure result&lt;br /&gt; - Are we getting rid of the warning for the wrong reason?&lt;br /&gt; - Does it read correctly?&lt;br /&gt;&lt;br /&gt;At this time, it is not something that the code police (perlcritic) seem to think is a bad practice, and certainly will make less code for us. It just seems like we are breaking an unwritten coding rule.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-9103065408282062158?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/9103065408282062158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=9103065408282062158' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/9103065408282062158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/9103065408282062158'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/06/lcx-possible-bad-practice.html' title='lc(x) - possible bad practice?'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7253772706308761184</id><published>2009-05-20T02:53:00.001-07:00</published><updated>2009-05-20T03:32:01.830-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='STOMP'/><category scheme='http://www.blogger.com/atom/ns#' term='RabbitMQ'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMQ'/><category scheme='http://www.blogger.com/atom/ns#' term='Net::Stomp'/><title type='text'>Stomping on the Rabbit, Pt2</title><content type='html'>So now I have this all installed, I have to start using it. Net::Stomp (see cpan.org) is the interface I would like to use, but the setup seems very much done for ActiveMQ (as some of the headers are 'activemq.xxxxx'.&lt;br /&gt;&lt;br /&gt;This means that by default, I only seem to be able to send and receive messages to a queue that exists only whilst someone is listening.&lt;br /&gt;&lt;br /&gt;The example scripts that come with the RabbitMQ-Stomp distribution only show this for PERL, however, the Ruby examples show much more (persistent queues, topics, etc).&lt;br /&gt;&lt;br /&gt;Thankfully, I found the following post&lt;br /&gt;&lt;br /&gt;http://tinyurl.com/pdznw9&lt;br /&gt;&lt;br /&gt;which explains the headers for RabbitMQ to produce persistent queues and operating topics. For Net::Stomp, the key is to add these headers to the key-value pairs in your connection/send hash, and happily they go through. Here are some examples:&lt;br /&gt;&lt;br /&gt;Persistent Queues:&lt;br /&gt;&lt;br /&gt;Receiver - &lt;br /&gt;&lt;br /&gt;my $stomp = Net::Stomp-&gt;new({hostname=&gt;'localhost', port=&gt;'61613'});&lt;br /&gt;$stomp-&gt;connect({login=&gt;'guest', passcode=&gt;'guest'}) or croak $EVAL_ERROR;&lt;br /&gt;&lt;br /&gt;$SIG{INT} = sub {&lt;br /&gt;  $stomp-&gt;unsubscribe({&lt;br /&gt;    destination =&gt; q(/queue/father/ted),&lt;br /&gt;  });&lt;br /&gt;  exit;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;$stomp-&gt;subscribe({&lt;br /&gt;  destination =&gt; q(/queue/super/ted),&lt;br /&gt;  q{auto-delete} =&gt; q{false}, # setting these flags will make your queue remain whilst&lt;br /&gt;  q{durable} =&gt; q{true}, # the subscriber(s) go away, and pick up messages afterwards&lt;br /&gt;  ack =&gt; q(client),&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;while (1) {&lt;br /&gt;    my $frame = $stomp-&gt;receive_frame;&lt;br /&gt;    print $frame;&lt;br /&gt;    print $frame-&gt;body . "\n";&lt;br /&gt;    $stomp-&gt;ack({frame=&gt;$frame});&lt;br /&gt;    last if $frame-&gt;body eq 'QUIT';&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;$stomp-&gt;disconnect;&lt;br /&gt;&lt;br /&gt;Sender -&lt;br /&gt;&lt;br /&gt;my $stomp = Net::Stomp-&gt;new({hostname=&gt;'localhost', port=&gt;'61613'});&lt;br /&gt;$stomp-&gt;connect({login=&gt;'guest', passcode=&gt;'guest'}) or croak $EVAL_ERROR;&lt;br /&gt;$stomp-&gt;send({destination =&gt; '/queue/super/ted',&lt;br /&gt;       bytes_message=&gt;1,&lt;br /&gt;       body=&gt;($ARGV[0] or "test\0message")});&lt;br /&gt;$stomp-&gt;disconnect;&lt;br /&gt;&lt;br /&gt;Topics:&lt;br /&gt;&lt;br /&gt;Topics are handled differently in RabbitMQ to ActiveMQ. In ActiveMQ, they sit under a namespace /topic/xxx/yyy and a combination of the client-id, destination, exchange and routing key all make up the subscriber to the topic&lt;br /&gt;&lt;br /&gt;In the case of RabbitMQ, it seems a bit more generic than that, but has some differences which make it (as far as I can see) non-persistent.&lt;br /&gt;&lt;br /&gt;Receiver -&lt;br /&gt;&lt;br /&gt;my $stomp = Net::Stomp-&gt;new({hostname=&gt;'localhost', port=&gt;'61613'});&lt;br /&gt;$stomp-&gt;connect({login=&gt;'guest', passcode=&gt;'guest'}) or croak $EVAL_ERROR;&lt;br /&gt;&lt;br /&gt;$SIG{INT} = sub {&lt;br /&gt;  exit;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;$stomp-&gt;subscribe({&lt;br /&gt;  destination =&gt; q{bananaman}, # needs to be a unique client-id&lt;br /&gt;  exchange =&gt; q{amq.topic},&lt;br /&gt;  routing_key =&gt; q{bananas},&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;while (1) {&lt;br /&gt;    my $frame = $stomp-&gt;receive_frame;&lt;br /&gt;    print $frame;&lt;br /&gt;    print $frame-&gt;body . "\n";&lt;br /&gt;    last if $frame-&gt;body eq 'QUIT';&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;$stomp-&gt;disconnect;&lt;br /&gt;&lt;br /&gt;This sets up a receiver, which if you use rabbitmqctl list_queues shows a queue bananaman which will receive messages (whilst he is listening) to the topic bananas&lt;br /&gt;&lt;br /&gt;using the following gives an anonymous looking receiver queue&lt;br /&gt;&lt;br /&gt;$stomp-&gt;subscribe({&lt;br /&gt;  id =&gt; q{banaman}, # needs to be a unique client-id&lt;br /&gt;  destination =&gt; q{},&lt;br /&gt;  exchange =&gt; q{amq.topic},&lt;br /&gt;  routing_key =&gt; q{bananas},&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;which you can unsubscribe from explicitly&lt;br /&gt;&lt;br /&gt;$stomp-&gt;subscribe({&lt;br /&gt;  id =&gt; q{banaman},&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;However, it would appear that the unsubscription occurs anyway, so unfortunately, any posts to the topic bananas would be lost to bananaman whilst he is away.&lt;br /&gt;&lt;br /&gt;Sender for topics -&lt;br /&gt;&lt;br /&gt;my $stomp = Net::Stomp-&gt;new({hostname=&gt;'localhost', port=&gt;'61613'});&lt;br /&gt;$stomp-&gt;connect({login=&gt;'guest', passcode=&gt;'guest'}) or croak $EVAL_ERROR;&lt;br /&gt;$stomp-&gt;send({destination =&gt; q{bananas},&lt;br /&gt;        exchange =&gt; q{amq.topic},&lt;br /&gt; body=&gt;($ARGV[0] or "test\0message")});&lt;br /&gt;$stomp-&gt;disconnect;&lt;br /&gt;&lt;br /&gt;You will notice here that the destination for this message is the routing_key, and that the exchange flag is set.&lt;br /&gt;&lt;br /&gt;The problem here is that essentially, each receiver to a particular topic (routing_key) forms a temporary queue to which the message is added, and then sent from, but the queue is just that, temporary and goes away when the receiver does, so that receiver will never get the message if they are not present when the message is sent to the message-queue.&lt;br /&gt;&lt;br /&gt;I am very keen to hear from anyone who has found out how to work around this.&lt;br /&gt;&lt;br /&gt;The other main problem that we have seen due to this is MQ agnosticism doesn't exist using Net::Stomp, as essentially the problem is that it is written with ActiveMQ in mind, and just feeds headers through. This means that there is no conversion of the headers between using ActiveMQ-STOMP, RabbitMQ-STOMP or any other message-queue-STOMP that is out there. This means that there is no way to easily hot-swap message-queues without code re-write. Again, I'd be happy to hear form anyone who has worked out how to get around this.&lt;br /&gt;&lt;br /&gt;As for now, it looks as though it is going to be ActiveMQ, as this has been installed centrally for us, and appears to manage the persistence a bit better, along with headers which are documented in Net::Stomp.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7253772706308761184?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7253772706308761184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7253772706308761184' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7253772706308761184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7253772706308761184'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/05/stomping-on-rabbit-pt2.html' title='Stomping on the Rabbit, Pt2'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5734120216810787679</id><published>2009-05-15T07:30:00.000-07:00</published><updated>2009-05-15T08:15:26.967-07:00</updated><title type='text'>Stomping on the Rabbit</title><content type='html'>we are trying to make a move to using message queues in my group to deal with pipelines and talking to other apps.&lt;br /&gt;&lt;br /&gt;There should be some great advantages for us, and I am quite excited by this.&lt;br /&gt;&lt;br /&gt;First thing, set up a STOMP message queue.&lt;br /&gt;&lt;br /&gt;Now, I am currently writing a simple message_queue based on ClearPress, but this is not yet ready, so I have just downloaded and installed RabbitMQ.&lt;br /&gt;&lt;br /&gt;RabbitMQ is written in Erlang, (which I have just set myself the challenge of learning from the Pragprog book Programming Erlang by Joe Armstrong). The first challenge was setting it up.&lt;br /&gt;&lt;br /&gt;First: You need erlang installed. I had already done this, so wasn't a problem. Jsut make sure that erlc is in your path.&lt;br /&gt;&lt;br /&gt;Now, I had dowloaded the latest release tarball, and expanded this, setting up in my sandbox area. However, this threw my a serious curveball with trying to install STOMP.&lt;br /&gt;&lt;br /&gt;Thankfully, Google is my friend, and someone had the same problem, since the STOMP needs installing against the correct version number, so I hereby give the definitive installation guide to getting RabbitMQ up and running in a sandbox on MacOSX.&lt;br /&gt;&lt;br /&gt;(I accept no responsibility for this not working on your machine!)&lt;br /&gt;&lt;br /&gt;1) Install Mercurial (yet another distributed version control system, but the one which RabbitMQ is on).&lt;br /&gt;&lt;br /&gt;2) (Thanks to everyone on this page http://tinyurl.com/pk7xfd)&lt;br /&gt;hg clone http://hg.rabbitmq.com/rabbitmq-server&lt;br /&gt;hg clone http://hg.rabbitmq.com/rabbitmq-codegen&lt;br /&gt;hg clone http://hg.rabbitmq.com/rabbitmq-stomp&lt;br /&gt;(cd rabbitmq-server; hg up rabbitmq_v1_5_4)&lt;br /&gt;(cd rabbitmq-codegen; hg up rabbitmq_v1_5_4)&lt;br /&gt;(cd rabbitmq-stomp; hg up rabbitmq_v1_5_3)&lt;br /&gt;&lt;br /&gt;3) (At this point, you need to check your version of python and simplejson, which needs installing)&lt;br /&gt;&lt;br /&gt;4) In the various MakeFiles, alter source roots to point to where you want the various db and log files to go, where your rabbit source root is, etc..&lt;br /&gt;&lt;br /&gt;Eg.&lt;br /&gt;&lt;br /&gt;I set up in my sandbox a folder 'rabbitmq', in which I put a logs dir, mnesia (db files) dir and rabbit-mnesia dir (into which I put the rabbitmq.conf file)&lt;br /&gt;&lt;br /&gt;In 'rabbitmq-server/Makefile'&lt;br /&gt;&lt;br /&gt;RABBITMQ_NODENAME=rabbit&lt;br /&gt;RABBITMQ_SERVER_START_ARGS=/my/sandbox/rabbitmq/rabbitmq.conf&lt;br /&gt;RABBITMQ_MNESIA_DIR=/my/sandbox/rabbitmq/$(RABBITMQ_NODENAME)-mnesia&lt;br /&gt;RABBITMQ_LOG_BASE=/my/sandbox/rabbitmq/logs&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;4) make -C rabbitmq-server&lt;br /&gt;make -C rabbitmq-stomp run&lt;br /&gt;&lt;br /&gt;Hey presto, you now have a rabbitmq-stomp server up and running.&lt;br /&gt;&lt;br /&gt;Now you have done this, you can try either the perl or ruby tests.&lt;br /&gt;&lt;br /&gt;Hope this guide is of use to someone.&lt;br /&gt;&lt;br /&gt;Cheers&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5734120216810787679?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5734120216810787679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5734120216810787679' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5734120216810787679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5734120216810787679'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/05/stomping-on-rabbit.html' title='Stomping on the Rabbit'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4100601523432072819</id><published>2009-03-27T05:45:00.000-07:00</published><updated>2009-03-27T06:46:54.877-07:00</updated><title type='text'>Splitting a Project</title><content type='html'>As part of the Group I am in, we have had a project sanger-pipeline. Now, early on in the life of the group, it was quite small, and was essentially just a few wrappers around the Illumina Analysis pipeline, so that it hooked into our tracking system, and scripts to manage the movement of images off the individual Genome Analysers to our Farm.&lt;br /&gt;&lt;br /&gt;However, as with most things, it got bigger...&lt;br /&gt;&lt;br /&gt;and bigger...&lt;br /&gt;&lt;br /&gt;and bigger until it was managing not only the wrappers (which were being maintained by someone who was in another group), and the movement of images, but processing other files, loading compressed versions of the images into other databases, deciding when it had everything it expected, what and when exactly to delete outdated files...and managing an apache webservice!&lt;br /&gt;&lt;br /&gt;So, over the last 2 weeks, I have been splitting the project down. This was not a trivial thing to do, as many modules where used in multiple places, but split it I did to the following:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;sanger-pipeline&lt;/span&gt; - this now only has wrapper scripts and modules relating to hooking the Illumina Analysis pipeline in to our system.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;instrument_handling&lt;/span&gt; - this deals with scripts and modules relating to the smooth running and mirroring of data of the GA's, plus our controlcentre script, whose primary use is to run these as a special user (although it does do some other things).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;data_handling&lt;/span&gt; - all these scripts and modules are responsible for managing data on the farm (once it has mirrored) including doing checks to ensure that data that needs long term storage are where they should be&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;sflogin_web_apps&lt;/span&gt; - the management of the apache server and the scripts that we run on the farm (instead of the usual webblades) to have access to files and directories located there.&lt;br /&gt;&lt;br /&gt;At this time, the 4 projects are still linked, in so much as the top-level package namespace for the modules is srpipe:: (which means we need to be careful of ensuring that further down the line we don't accidently create two things which will conflict) and that many modules &lt;span style="font-style:italic;"&gt;use&lt;/span&gt; modules from some of the other projects (code as &lt;span style="font-style:italic;"&gt;DRY&lt;/span&gt; as possible).&lt;br /&gt;&lt;br /&gt;However, these slight drawbacks are minor compared to the ability now to apply patches and new code to a project, without accidently deploying a broken development of something else (which was the tricky thing when the wrapper scripts were managed by someone separate to the group responsible for the (for example) mirroring of data).&lt;br /&gt;&lt;br /&gt;So hopefully, now we have a much more stable codebase, and will be able to keep things running much more smoothly (or at least, develop much more fluidly).&lt;br /&gt;&lt;br /&gt;The test coverage needs improvement, but this is something that we can further work on now. A fair amount of code had no tests at all past one which checked it compiled, so a revamp of the test suite was certainly in order, and with this, we can code directly in to ensure that the functionality doesn't change where code is reused in different projects.&lt;br /&gt;&lt;br /&gt;Exciting times or screams ahead, who knows, but at least with hopefully more manageable project sizes, we should be able to aim for exciting...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-4100601523432072819?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4100601523432072819/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4100601523432072819' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4100601523432072819'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4100601523432072819'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2009/03/splitting-project.html' title='Splitting a Project'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4184792026117854086</id><published>2008-12-18T08:58:00.000-08:00</published><updated>2008-12-18T09:00:19.863-08:00</updated><title type='text'>Running Alone</title><content type='html'>John in my team at work has just pointed me at a great little CPAN module for ensuring that a script can only have one copy running at a time.&lt;br /&gt;&lt;br /&gt;In our team, we have a lot of potentially slow running processes, which are often run as cron jobs. Now, ordinarily, you would pick cron start times&lt;br /&gt;that ensure that you have finished any previous run of the script. But it is quite common for us to want to run as soon as possible, but not if the&lt;br /&gt;previous run hasn't finished.&lt;br /&gt;&lt;br /&gt;One method previously suggested was to touch a file in /tmp and then delete it when finished, wrapping the cron caller in a command not to run if&lt;br /&gt;the touched file is present. However, what happens if /tmp is cleared out (which has happened to me!)&lt;br /&gt;&lt;br /&gt;So, John found Sys::RunAlone. If the format of your script is to run a main method, and you put __END__ or __DATA__ at the bottom of the script,&lt;br /&gt;then this can lock the text, and then knows no to exit if it finds a lock on the section. This means you could have your cronjob running every 30 minutes&lt;br /&gt;and not worry if the script takes 5 minutes in a cycle, or 45 minutes.&lt;br /&gt;&lt;br /&gt;A definite benefit.&lt;br /&gt;&lt;br /&gt;A big thanks to Elizabeth Mattijsen for writing this very useful module.&lt;br /&gt;&lt;br /&gt;(A quick example of this running is below. If you try to run the same script in another terminal before the first has finished it's sleep, you will get&lt;br /&gt;the correct error).&lt;br /&gt;&lt;br /&gt;#!/usr/bin/perl -wT&lt;br /&gt;use strict;&lt;br /&gt;use warnings;&lt;br /&gt;use Sys::RunAlone;&lt;br /&gt;&lt;br /&gt;main();&lt;br /&gt;0;&lt;br /&gt;&lt;br /&gt;sub main {&lt;br /&gt;  warn 'Hello ...';&lt;br /&gt;  go_to_sleep(10);&lt;br /&gt;  warn 'world!';&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;sub go_to_sleep {&lt;br /&gt;  my $sleepytime = shift;&lt;br /&gt;  sleep $sleepytime;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;__END__&lt;br /&gt;&lt;br /&gt;Blog entry&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-4184792026117854086?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4184792026117854086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4184792026117854086' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4184792026117854086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4184792026117854086'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/12/running-alone.html' title='Running Alone'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-6048917072825180896</id><published>2008-12-18T08:17:00.001-08:00</published><updated>2008-12-18T09:01:47.913-08:00</updated><title type='text'>Data Formats</title><content type='html'>Check out this SlideShare Presentation: &lt;div style="width:425px;text-align:left" id="__ss_856855"&gt;&lt;a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/setitesuk/data-formats-presentation?type=powerpoint" title="Data Formats"&gt;Data Formats&lt;/a&gt;&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slideshare.net/swf/ssplayer2.swf?doc=dataformats-1229614080381650-2&amp;stripped_title=data-formats-presentation" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=dataformats-1229614080381650-2&amp;stripped_title=data-formats-presentation" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;"&gt;View SlideShare &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/setitesuk/data-formats-presentation?type=powerpoint" title="View Data Formats on SlideShare"&gt;presentation&lt;/a&gt; or &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/upload?type=powerpoint"&gt;Upload&lt;/a&gt; your own. (tags: &lt;a style="text-decoration:underline;" href="http://slideshare.net/tag/csv"&gt;csv&lt;/a&gt; &lt;a style="text-decoration:underline;" href="http://slideshare.net/tag/tsv"&gt;tsv&lt;/a&gt;)&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This presentation was given by me to foomongers to start off a bit of discussion on different data transfer formats.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-6048917072825180896?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/6048917072825180896/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=6048917072825180896' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6048917072825180896'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6048917072825180896'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/12/data-formats.html' title='Data Formats'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-671395161805207422</id><published>2008-10-18T00:35:00.000-07:00</published><updated>2008-10-18T01:08:51.231-07:00</updated><title type='text'>Disk is cheap?</title><content type='html'>I work at the Wellcome Trust Sanger Institute, where we do a lot of processing of raw sequence data, particularly from the Next Gen sequencing machines. Over any month period we can have 320Tb of data sitting around on on our sequencing farm. The run data then gets tarballed up and moved off into repositories.&lt;br /&gt;&lt;br /&gt;Currently, Gouying and I have been working on a MySQL database to store the QC data from the runs, so that we can get rid of the Tarballs (each themselves around 60Gb), and allow easier access to the data. Note, this is not the DNA sequence, that is being stored elsewhere. The expected annual insert into this is likely to be around 50Tb at current Illumina machine levels (and we are planning to get more!).&lt;br /&gt;&lt;br /&gt;Roger and others have been working on storing the images for the runs longer term. This has meant moving them into an Oracle database, which means that we should have them for perhaps a year, rather than around 3 weeks. This is around 100 (tiles) * 36 (cycles) * 2 (run pair) * 8 (lanes on a chip) images per run tiff images.&lt;br /&gt;&lt;br /&gt;Speaking with Ed at work, we discussed what was mentioned at our last Town meeting. Ed used to work for IBM, and he talked me through a bit of how processors have developed and why they are now likely to be at their limit of capability, hence going to multiple core machines. He therefore raised a good question at the town meeting - is it cheaper to store the data from the runs long term, or just rerun the sequencing?&lt;br /&gt;&lt;br /&gt;At the moment, it costs 2% of the cost of a run to keep the data on disk, so that answers the question. Or does it?&lt;br /&gt;&lt;br /&gt;Torvus Linalds has been quoted as saying that disk is cheap, and yes, upgrading your desk/laptop to 4x the memory is pretty cheap. However, you still need to ensure you have space for it. And there is one thing that is certainly the case, space isn't cheap. All those disks have a physical footprint. (and we could very soon run out of space in the data centre)&lt;br /&gt;&lt;br /&gt;They also have to be managed, and people aren't cheap. We have a very good systems team, that are on 24hr callout, but that costs money.&lt;br /&gt;&lt;br /&gt;So, it is very much a trade off. The cost of resequencing is very expensive at the moment, and storage of data is cheap, but if the next gen sequencing costs come down, then it may become very much the case just to store a plate of DNA and resequence it every time you want data from it, rather than long term storage of 4Tb+ of data.&lt;br /&gt;&lt;br /&gt;This may be a long way off, but if the $1000 genome becomes a reality, then I think that it may change. Is this a good thing? We shall see.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-671395161805207422?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/671395161805207422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=671395161805207422' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/671395161805207422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/671395161805207422'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/10/disk-is-cheap.html' title='Disk is cheap?'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3826369856490861880</id><published>2008-10-08T00:24:00.000-07:00</published><updated>2008-10-08T00:27:20.020-07:00</updated><title type='text'>Ajax - Web 2.0 Primer course.</title><content type='html'>So last week I went on a four day course to find out more about using Ajax to make web2.0 sites. The course was actually 2x4 day courses attempted to be fitted in to 1x4 day course.&lt;br /&gt;&lt;br /&gt;For me, the first course was pretty simple, it was supposedly the prerequisite course fro the Ajax primer. We covered the Javascript basics, but in order to move on, the course instructor assumed our knowledge of HTML and css. For me this was OK, but some people didn't seem too up with the whole css selector specs instead of using inline markup. However, this did 'get' us through the course in a day.&lt;br /&gt;&lt;br /&gt;We then jumped to the Ajax course. As I have blogged before, earlier this year I bought Pragmatic Ajax: A web2.0 primer (www.pragprog.com). Most of the second course was actually summed up in this book (although I did learn more about some Best Practices). We went through from making an xhr object that is cross-browser compatible, to starting our own library, and then we did some examples using jQuery, Prototype and Scriptaculous.&lt;br /&gt;&lt;br /&gt;The course was from Learning Tree, and the instructor was very knowledgable, showing us some other books that we might want to read, primarily Douglas Crockford - Javascript, The best bits (www.oreilly.com) and John Resig - Pro Javascript Techniques (apress).&lt;br /&gt;&lt;br /&gt;The thing that interested me the most was probably something that had the least time spent on it. For my toy app, YADB, I wanted to be able to drag and drop cards from either a card list or inventory list into a deck container. We were shown how to do this using Scriptaculous (quite fortunate, considering YADB is Ruby on Rails).&lt;br /&gt;&lt;br /&gt;Work wise, it was good to be shown exactly how to look at blackboxing functions, and how to pass JSON and objects around.&lt;br /&gt;&lt;br /&gt;Two disappointing features of the course - example code snippets did not follow what 'Best Practices Guy' was saying, sometimes even in the same slide, and some of the exercises on the computer didn't match the code in the book.&lt;br /&gt;&lt;br /&gt;Also, no fault of the instructor, but somewhere along the line, they hadn't explained to Learning Tree that most people on my site primarily use Perl,&lt;br /&gt;so some of us were a bit stuck when it came to modifying some of the server side code, as we had little/no experience of the 3 they had chosen (jsp, php and .net)&lt;br /&gt;&lt;br /&gt;Overall, a good course, that could have been better, but worth going on.&lt;br /&gt;&lt;br /&gt;I must contact Learning Tree though about doing the 2 module tests for them though.&lt;br /&gt;&lt;br /&gt;An aside to this, why is everything PHP. I had a couple of email links come though about web app jobs coming up, and everyone wants PHP. I could be a bit biased, but certainly a lot of people I know think that PHP is probably one of the worst technologies to come along for a while. Certainly one thing that has been blogged on Perl Buzz is that you can't bug report anything from an older version than the current release, which seems a bit pointless to me. However, I am not going to go into it now, I think I just clearly need to learn it. Better hit the bookshelves!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3826369856490861880?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3826369856490861880/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3826369856490861880' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3826369856490861880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3826369856490861880'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/10/ajax-web-20-primer-course.html' title='Ajax - Web 2.0 Primer course.'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5203440305521089145</id><published>2008-08-03T00:08:00.000-07:00</published><updated>2008-08-03T00:45:06.360-07:00</updated><title type='text'>#barcamb</title><content type='html'>So on Friday I attended the second barcamb, at the Wellcome Trust Genome Campus. For those not in the know, a barcamp is an unconference. People turn up with stuff to talk about, and a plan for the day is organised over coffee and biscuits at the start of the day.&lt;br /&gt;&lt;br /&gt;It was very good interesting day. Many of the faces we saw last year came again, and gave either updates, or new talks, and we had soem new people as well, including a chap who was using the internet to help the Neighbourhood Watch in his village.&lt;br /&gt;&lt;br /&gt;Simon Ford brought MBED with him again, showing some more of the exciting stuff he had been doing with it (including something at a 24hr hackathon, which uses a social idea to move packages to their destination).&lt;br /&gt;&lt;br /&gt;Unfortunately, I can't remember many peoples names, but is was great to talk with so many of you, and I was rather surprised that my quick unplanned 10minuter on 'It's Too Much Information for ME!' seemed to generate a lot of little discussions with me. It's nice to see that either other people have had the same problem, or that people realise that you end up needing to program for other peoples lack of foresight/rushing development code into production. My talk followed quite nicely from Nava Whiteford's talk on the Swift Analysis pipeline, and Matthew Astleys ad-hoc talk/discussion on Panic Driven Development.&lt;br /&gt;&lt;br /&gt;I have some photo's which I have uploaded to Facebook. Please feel free to have a look and tag yourself (or anyone else you know) in any of them.&lt;br /&gt;&lt;br /&gt;http://www.facebook.com/photo.php?pid=1183440&amp;l=5b93d&amp;id=665450745&lt;br /&gt;&lt;br /&gt;Cheers&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5203440305521089145?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5203440305521089145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5203440305521089145' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5203440305521089145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5203440305521089145'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/08/barcamb.html' title='#barcamb'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-32455155256496604</id><published>2008-07-18T04:17:00.000-07:00</published><updated>2008-07-18T04:36:02.517-07:00</updated><title type='text'>Perl::Critic - new release</title><content type='html'>So a new release of Perl::Critic was released, and all I want to say is what a faff.&lt;br /&gt;&lt;br /&gt;Some key new features:&lt;br /&gt;&lt;br /&gt;1) You must check the return value of all eval statement - don't rely on $EVAL_ERROR/$@&lt;br /&gt;&lt;br /&gt;Now, this is a good thing(tm) but it does have some pitfalls, such as where you might be evalling a transactional commit.&lt;br /&gt;&lt;br /&gt;eval {&lt;br /&gt; $transaction_state and $dbh-&gt;commit();&lt;br /&gt;} or do {&lt;br /&gt; ...some 'croak' code&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;The problem here is that the eval isn't a croak if $transaction_state is 0 (as it could be inside a much larger transaction), but the return code would be 0, therefore firing off your 'croak' code.&lt;br /&gt;&lt;br /&gt;So, to get round this, you need to return a true value after the statement;&lt;br /&gt;&lt;br /&gt;eval {&lt;br /&gt; $transaction_state and $dbh-&gt;commit();&lt;br /&gt; 1;&lt;br /&gt;} or do {&lt;br /&gt; ...some 'croak' code&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;A bit off a faff, but it is better code for it.&lt;br /&gt;&lt;br /&gt;2) Declaration of all numbers other than 0,1 and 2&lt;br /&gt;&lt;br /&gt;So, as it could be difficult to understand what a number represents when reading the code, you now need to use Readonly to declare any numbers with a named variable at the top of your code.&lt;br /&gt;&lt;br /&gt;So now instead of&lt;br /&gt;&lt;br /&gt;$percentage = $x * 100/$y;&lt;br /&gt;&lt;br /&gt;You need&lt;br /&gt;&lt;br /&gt;use Readonly;&lt;br /&gt;Readonly our $PERCENTAGE_MAKER =&gt; 100;&lt;br /&gt;...&lt;br /&gt;$percentage = $x * $PERCENTAGE_MAKER/$y;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;This 'might' make sense to odd numbers floating around, but it also applies to indices on arrays. So if you want the 5th element on an array intstead of requesting&lt;br /&gt;&lt;br /&gt;$wanted = $array[4];&lt;br /&gt;&lt;br /&gt;You now need&lt;br /&gt;&lt;br /&gt;use Readonly;&lt;br /&gt;Readonly our $FIFTH_ARRAY_ELEMENT =&gt; 4;&lt;br /&gt;...&lt;br /&gt;$wanted = $array[$FIFTH_ARRAY_ELEMENT];&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;Now, I admit it is often bad to pick out many individual elements from an array by specific number, but it will seriously clutter code where this may be necessary. I admit, for a couple of lines in my code, I have now used ##no critic at the end.&lt;br /&gt;&lt;br /&gt;I also wonder why specifically 0,1 and 2 get let off. If the problem is that you don't know if 60 means&lt;br /&gt;&lt;br /&gt;i)   minutes in an hour&lt;br /&gt;ii)  seconds in a minute&lt;br /&gt;iii) degrees in the angle of an equilateral triangle&lt;br /&gt;&lt;br /&gt;then 0 could mean off, false, nothing, 0&lt;br /&gt;1 could mean on, true, positive, 1&lt;br /&gt;2 could mean wheels on a bicycle, eyes on a face, hands, 2&lt;br /&gt;&lt;br /&gt;Just some examples where I imagine it is because they are the most heavily used for certain features, it would almost impossible to change them (especially 1 as the return value of a perl module)&lt;br /&gt;&lt;br /&gt;3) Declaring a variable, which you never (appear to) use&lt;br /&gt;&lt;br /&gt;This is annoying if, like me, you use Class::Std to create inside out objects, as you need to declare a hash for your attributes, but this has is never referred to again in the code.&lt;br /&gt;&lt;br /&gt;Now, whilst I understand that if you don't use a variable, don't declare it, in this case you are using it, just via the Class::Std methods of the accessors created. However, much like the declaration of numbers, it isn't looking at the context in which you use the declared variable. Again, in this case I have had to wrap these with a ## no critic {} ## use critic in order to not have it fail.&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;&lt;br /&gt;So as I said at the beginning, all I want to say is what a faff and the reason is as follows:&lt;br /&gt;&lt;br /&gt;We are using Perl::Critic extensively here in New Pipeline Development, and it does force our hand to a Good Coding Practice, but I can't help but wonder here if a couple of these new standards are just a little too overzealous, and causing some things to be overcritised (i.e. a faff).&lt;br /&gt;&lt;br /&gt;As with many things, it will take time to get used to programming in advance of critic'ing the code, but I think some of these new features need a little tweaking.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-32455155256496604?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/32455155256496604/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=32455155256496604' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/32455155256496604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/32455155256496604'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/07/perlcritic-new-release.html' title='Perl::Critic - new release'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3692464499629091834</id><published>2008-06-20T05:39:00.001-07:00</published><updated>2008-06-20T06:22:17.608-07:00</updated><title type='text'>Class::Std or Blessed Hash</title><content type='html'>Objects, Objects, Objects&lt;br /&gt;&lt;br /&gt;Everything is objects these days, well, certainly in the world of agile, well structured, extensible, easy to maintain BioInformatics software.&lt;br /&gt;&lt;br /&gt;Even Perl6 is aiming to be OO. Probably because of the fact that so many of the modules on CPAN at least expose an OO layer, if not are only OO.&lt;br /&gt;&lt;br /&gt;When I started programming PERL, I was writing straight forward top to bottom scripts.&lt;br /&gt;&lt;br /&gt;I then moved on to using and producing code in modules, but just exporting the subroutines into the script that used it, for simple code reuse.&lt;br /&gt;&lt;br /&gt;Last summer, I got finally taught with hands on development of exactly how OO works and is used. I got a bit confused, but at least I had none of the confusion of&lt;br /&gt;&lt;br /&gt;$him = Person-&gt;new({args});&lt;br /&gt;$her = $him-&gt;new({args});&lt;br /&gt;&lt;br /&gt;Which implies a relationship which 'is not there'.&lt;br /&gt;&lt;br /&gt;Last summer I discovered Class:Std, which I think is probably my favourite CPAN module of all time. Why?&lt;br /&gt;&lt;br /&gt;Well this is the thing. PERL is not an OO language, and it isn't slower because of it. I also learnt Ruby on Rails (as I mentioned in a previous post) and Ruby is slower because everything is an object. Something that clearly sets the two languaged apart.&lt;br /&gt;&lt;br /&gt;Now, that isn't the thing that bugs me about OO. In fact, I have learned to embrace PERL OO, and enjoy programming in it. But what does bug me, is that the vast majority of PERL OO breaks encapsulation because all most objects are are HASHes. You have a new constructor, which blesses the package name around a HASH reference. So, when all is said and done, whilst good packages have constructors written to expose the stored data within the object via method calls, you can just access a lot of it via a key.&lt;br /&gt;&lt;br /&gt;$him-&gt;eye_colour() is equivalent to $him-&gt;{eye_colour}&lt;br /&gt;&lt;br /&gt;and this encourages lazy programming, because the other advantage is that you can just say 'I need to store some data, what should I do with it, as Person doesn't have an address accessor'&lt;br /&gt;&lt;br /&gt;Now, presumably Person does have something that links it to Address. Perhaps Address and Person both have an id_person accessor. But you can cheat. If you want to grab address now, and cache it for later, just do&lt;br /&gt;&lt;br /&gt;$person-&gt;{address} = $address-&gt;house_and_street();&lt;br /&gt;&lt;br /&gt;The you can drop the address object, and person now knows exactly where they live.&lt;br /&gt;&lt;br /&gt;However, this is dangerous, because&lt;br /&gt;&lt;br /&gt;1) Have you deleted something specifically stored in key address&lt;br /&gt;2) What if they move whilst person object is still in memory. You have two places to correct the data.&lt;br /&gt;&lt;br /&gt;Why, I hear you cry - I won't do that with my program. No, but someone else will (or you will forget).&lt;br /&gt;&lt;br /&gt;Solution use Class::Std;&lt;br /&gt;&lt;br /&gt;Class:Std enforces encapsulation. You still get a blessed package, but this time it is a SCALAR, which can't have keys.&lt;br /&gt;&lt;br /&gt;You then in each package declare what accessors you want the object to have, and as such enforce people to only use those accessors. You don't have to worry about AUTOLOAD in the history of used modules, as Class::Std handles creating you accessors. You don't even need a new constructor, although you can add a BUILD method which will operate at construction.&lt;br /&gt;&lt;br /&gt;So in my example&lt;br /&gt;&lt;br /&gt;package Person;&lt;br /&gt;&lt;br /&gt;use Class::Std;&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;  my %eye_colour_of :ATTR( 'init_arg' =&gt; eye_colour, :get&amp;lt;eye_colour&amp;gt;, :set&amp;lt;eye_colour&amp;gt;);&lt;br /&gt;}&lt;br /&gt;1;&lt;br /&gt;&lt;br /&gt;my $him = Person-&gt;new({eye_colour =&gt; 'blue'});&lt;br /&gt;&lt;br /&gt;Job done. Less code for initial construction than blessing via new, and you cannot be tempted to throw the address onto the person when it is being used, as&lt;br /&gt;&lt;br /&gt;print $him = Person=SCALAR(0x9f2c68)&lt;br /&gt;&lt;br /&gt;So, unless you specify in the code (documented and tested, of course) that you want an accessor which allows this object to store the address, it can't be done, and your later code is more robust for it.&lt;br /&gt;&lt;br /&gt;Now, where am I going with all of this?&lt;br /&gt;&lt;br /&gt;Well, I use Clearpress to form a base for my PERL web apps in my current role. It is a good solid platform which I have mentioned before, and I am very happy to work within it. However, I am writing an API to use the services it provides. Clearpress doesn't use Class::Std. My API does. This is no problem as they talk via LWP::UserAgent requests, but it is quite confusing as the live in the same project in subversion. And my big thing is that I am programming both at the same time. This is bad news, as I have been trying to use features of one type of Object with the other. It hasn't really made a significant difference, as the package name reminds me which I should be using, but is is wierd getting the error when you try to cheat, and use a key to cache some info in the Class::Std object, as it is a scalar.&lt;br /&gt;&lt;br /&gt;So, from this, I am going to finish the project in the way I have started it, but I think from now on there is one golden rule:&lt;br /&gt;&lt;br /&gt;Use only one type of object, and just ensure you enforce encapsulation by the way you program - don't get lazy.&lt;br /&gt;&lt;br /&gt;Now, to convince my boss to refactor Clearpress into Class::Std...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3692464499629091834?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3692464499629091834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3692464499629091834' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3692464499629091834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3692464499629091834'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/06/classstd-or-blessed-hash.html' title='Class::Std or Blessed Hash'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-166994573880534862</id><published>2008-05-07T17:52:00.000-07:00</published><updated>2008-05-07T18:29:23.216-07:00</updated><title type='text'>To scroll within or out</title><content type='html'>So an interesting thing arose out of my work recently, both NPG and a personal project, which is how to scroll tables.&lt;br /&gt;&lt;br /&gt;In NPG, we have many tables of data, from run information, to search results, to instrument information. These are generated from templates and render to the browser.&lt;br /&gt;&lt;br /&gt;In V:YaDB, I have the same issue, as well in excess of 2000 cards is quite a bit to scroll through, but again it is templated.&lt;br /&gt;&lt;br /&gt;Following good practice and webstandards, we are all using&lt;br /&gt;&lt;br /&gt;&amp;lt;table&gt;&lt;br /&gt;&amp;lt;thead&gt;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&amp;lt;/thead&gt;&lt;br /&gt;&amp;lt;tfoot&gt;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&amp;lt;/tfoot&gt;&lt;br /&gt;&amp;lt;tbody&gt;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&amp;lt;/tbody&gt;&lt;br /&gt;&amp;lt;/table&gt;&lt;br /&gt;&lt;br /&gt;or at least we should be. Most modern browsers insert this anyway, but putting it in gives you some additional css tags, and ensures you and future developers of your project know what is going where (and for anyone not familiar with this, &amp;lt;tfoot&gt; should be before &amp;lt;tbody&gt;, although it is optional to have a footer to your table).&lt;br /&gt;&lt;br /&gt;Anyway, with this in mind, it should be easy to just put the following css in to make the body part of your table scroll, therefore leaving the head fixed, to keep column header visible&lt;br /&gt;&lt;br /&gt;tbody {height:300px;overflow:auto;overflow-x:hidden;}&lt;br /&gt;&lt;br /&gt;However, this only works in the firefox browser. What is going on there? But it is true. I spent some time this weekend on IE7 and Safari and found this to be true.&lt;br /&gt;&lt;br /&gt;So, back to square one. Searching google found a lot of real hacks, from serious amounts of Javascript, to running different css files dependent on browser (including separate versions for IE6 and IE7). Madness.&lt;br /&gt;&lt;br /&gt;One idea I came across that I liked the most though was the idea of two table renders. Whilst this means it is only really suitable for quick to render tables, this is something that is possible.&lt;br /&gt;&lt;br /&gt;Now, there are two options here.&lt;br /&gt;&lt;br /&gt;1) Using declared fixed width columns, produce one table which only has the thead part of your table. Then immediately beneath it produce the table with the data in. This one could be a bigger table because it only has to render data once, but you have no flexibility should you need to add a new column of data.&lt;br /&gt;&lt;br /&gt;2) In a fixed height div, render two copies of the table using absolute positioning over each other. Then wrap each in it's own div labelled with an id. Using using z-align:1; for the one you want to scroll, and z-align:2; for the head, and fix the height of the head div to that only the head row is shown, and set overflow:hidden; Set the height of the scroll table to the height of the outer div, and set overflow:auto; overflow-x:hidden;&lt;br /&gt;&lt;br /&gt;(with 2, obviously, you could also fix the width, and for both show the overflow-x)&lt;br /&gt;&lt;br /&gt;Also with both, you need to declare a spacer column, which will then ensure room for your scrollbar.&lt;br /&gt;&lt;br /&gt;I personally prefer 2, which I managed to create some css to produce a nice effect with the fact that with 2 tables, I was able to manipulate the header style without needing to worry about if it affected the rest of the data, and also revealed the bottom border only to give a ruled effect.&lt;br /&gt;&lt;br /&gt;The downside to 2 is that anyone with css turned off will end up viewing two copies of your table, but hey, no-one should be turning off css or javascript in their browsers, and if you know someone who does, 'send the boyz round to ave a wurd'.&lt;br /&gt;&lt;br /&gt;I think that I am going to expand scrumptious to have javascript and css effects, and this will be the first css effect in it. I'll let you know when the sourceforge svn trunk is updated.&lt;br /&gt;&lt;br /&gt;However, start lobbying your local MP today to get the simplest option put into your favourite browser, or if your fave is already firefox/iceweasel, then at least IE and Safari.&lt;br /&gt;&lt;br /&gt;Please note: I have nothing against Opera, Camino or any other browsers out there, I just don't use them on a regular basis.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-166994573880534862?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/166994573880534862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=166994573880534862' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/166994573880534862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/166994573880534862'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/05/to-scroll-within-or-out.html' title='To scroll within or out'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3011089608609282261</id><published>2008-04-30T01:24:00.000-07:00</published><updated>2008-04-30T01:29:42.301-07:00</updated><title type='text'>History Meme</title><content type='html'>So I got tagged to do this by my boss Roger, and a lot of my work colleagues are doing it. So here is the result from my macbook:&lt;br /&gt;&lt;br /&gt;history|awk '{print $2}'|sort|uniq -c|sort -rn|head&lt;br /&gt;&lt;br /&gt;167 prove&lt;br /&gt; 62 cd&lt;br /&gt; 55 svn&lt;br /&gt; 45 cover&lt;br /&gt; 42 ls&lt;br /&gt; 32 rake&lt;br /&gt; 28 mate&lt;br /&gt; 11 make&lt;br /&gt;  8 ./bin/apachectl&lt;br /&gt;  7 sudo&lt;br /&gt;&lt;br /&gt;So I am testing quite a lot on my machine (prove and cover) (always trying for test driven development is the reason for this). cd is obvious. svn is vitally important.&lt;br /&gt;&lt;br /&gt;I am surprised rake turns up more than make though&lt;br /&gt;&lt;br /&gt;More service soon.&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3011089608609282261?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3011089608609282261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3011089608609282261' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3011089608609282261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3011089608609282261'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/history-meme.html' title='History Meme'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8161325787079070026</id><published>2008-04-07T04:51:00.000-07:00</published><updated>2008-04-07T04:52:35.269-07:00</updated><title type='text'>Javascript for all</title><content type='html'>So on Friday, as a gentle way of trying to get back into work mode after the Rails course had finished, I started&lt;br /&gt;by trying to refactor out a lot of javascript from the templates.&lt;br /&gt;&lt;br /&gt;I have just bought 'Pragmatic Ajax (A Web 2.0 Primer)' from The Pragmatic Programmers. It is a very interesting read, and &lt;br /&gt;inspired me to 'get the code out of the view'&lt;br /&gt;&lt;br /&gt;It's true to say, that we have been quite lax in simply putting &amp;lt;script&amp;gt; tags in with fairly specialised javascript&lt;br /&gt;functions, which don't really need any variables passed to them (as the function ends up with the paths and div ids hard=coded).&lt;br /&gt;&lt;br /&gt;Well, I managed to refactor out most of the functions that we had written, and with a few additions to variables being passed to them, managed to reduce the number of some function (or make them more genericised for future reference). I even discovered a slight problem with my scrumptious.js which I need to tweak and document.&lt;br /&gt;&lt;br /&gt;The great thing is that we have now reduced the code in the views. This makes the views easier to read and keep upto date.&lt;br /&gt;&lt;br /&gt;There are still a few functions which I should be able to refactor, but I need to find out a couple of extra things first.&lt;br /&gt;&lt;br /&gt;I've only got through the first 3 chapters of 'Pragmatic Ajax' so far, but it has explained a bit that so far I hadn't known from just my experience learning some Ajax through RoRails. Chapter 1 explains about what Ajax is, Chapter 2 shows you how to develop Ajaxian Maps (a google maps clone). Then it has started to go into the Nitty Gritty details of Ajax and Client-side Javascript.&lt;br /&gt;&lt;br /&gt;However, so far the javascript examples have all been written in the html head, rather than in a separate .js file. I imagine (hope) that this will change in a best practice suggestion. I'm also hoping it will show a bit on testing javascript, which so far is something that I haven't done.&lt;br /&gt;&lt;br /&gt;My experience of programming books has led me to find the Pragmatic Programmers books are a great way of finding the information in an easy to read style. So far, Pragmatic Ajax is a good book and hasn't let me down in it's style and (most importantly) content.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8161325787079070026?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8161325787079070026/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8161325787079070026' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8161325787079070026'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8161325787079070026'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/javascript-for-all.html' title='Javascript for all'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1907851136024636961</id><published>2008-04-03T06:59:00.001-07:00</published><updated>2008-04-03T07:01:33.216-07:00</updated><title type='text'>Advancing with Rails course - Day 4 pt 3</title><content type='html'>So, the final bit of the course has been looking at integration testing&lt;br /&gt;&lt;br /&gt;objective is to go through the processes to a conclusion, i.e.&lt;br /&gt;&lt;br /&gt;login &amp;gt;&amp;gt;&lt;br /&gt;attempt to bid on an auction you started &amp;gt;&amp;gt;&lt;br /&gt;fail&lt;br /&gt;&lt;br /&gt;login &amp;gt;&amp;gt;&lt;br /&gt;bid on auction as highest bidder &amp;gt;&amp;gt;&lt;br /&gt;fail&lt;br /&gt;&lt;br /&gt;login &amp;gt;&amp;gt;&lt;br /&gt;bid on auction &amp;gt;&amp;gt;&lt;br /&gt;pass&lt;br /&gt;&lt;br /&gt;and so on&lt;br /&gt;&lt;br /&gt;can cross controllers which is why next level up from functional tests. May be more than one/two asserts as&lt;br /&gt;they are linked and this is useful to ensure it doesn't bother trying tests it can't even get to&lt;br /&gt;&lt;br /&gt;very good place to often refactor heavily&lt;br /&gt;&lt;br /&gt;integration testing routes&lt;br /&gt;&lt;br /&gt;def test_show_route&lt;br /&gt;  assert_recognizes({:controller =&amp;gt; :auctions,&lt;br /&gt;                     :action =&amp;gt; :show,&lt;br /&gt;                     :id =&amp;gt; "1"}, auction_path(1)) &amp;lt;==named routes&lt;br /&gt;end&lt;br /&gt;def test_generate_route&lt;br /&gt;  assert_generates("/auctions/3", :controller =&amp;gt; :auctions,&lt;br /&gt;                     :action =&amp;gt; :show,&lt;br /&gt;                     :id =&amp;gt; "3")&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;also can assert_routes&lt;br /&gt;&lt;br /&gt;you could get back responses to look at when rjs is rendered&lt;br /&gt;&lt;br /&gt;irb&amp;gt;&amp;gt; app.get("/auctions/destroy/1")&lt;br /&gt;&amp;gt;&amp;gt; 200&lt;br /&gt;irb&amp;gt;&amp;gt; app.response.body&lt;br /&gt;(output of rjs file)&lt;br /&gt;&lt;br /&gt;rcov tool&lt;br /&gt;&lt;br /&gt;coverage of code with tests&lt;br /&gt;&lt;br /&gt;ZenTest&lt;br /&gt;&lt;br /&gt;runs in another terminal window, and everytime you make and save a change to a file, it works out the tests which are affected and reruns those tests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1907851136024636961?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1907851136024636961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1907851136024636961' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1907851136024636961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1907851136024636961'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/advancing-with-rails-course-day-4-pt-3.html' title='Advancing with Rails course - Day 4 pt 3'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7407349534612751298</id><published>2008-04-03T03:46:00.001-07:00</published><updated>2008-04-03T03:48:14.028-07:00</updated><title type='text'>Advancing with Rails course - Day 4 pt 2</title><content type='html'>Plugins&lt;br /&gt;&lt;br /&gt;How to install and generate a plugin&lt;br /&gt;&lt;br /&gt;guest095:yadb ajb$ script/plugin install http://repo.pragprog.com/svn/Public/plugins/annotate_models&lt;br /&gt;+ ./ChangeLog&lt;br /&gt;+ ./README&lt;br /&gt;+ ./lib/annotate_models.rb&lt;br /&gt;+ ./tasks/annotate_models_tasks.rake&lt;br /&gt;guest095:yadb ajb$ ls vendor/plugins/&lt;br /&gt;annotate_models/         nested_has_many_through/ &lt;br /&gt;guest095:yadb ajb$ ls vendor/plugins/annotate_models/&lt;br /&gt;ChangeLog  README     lib/       tasks/     &lt;br /&gt;guest095:yadb ajb$ ls vendor/plugins/annotate_models/tasks/annotate_models_tasks.rake &lt;br /&gt;vendor/plugins/annotate_models/tasks/annotate_models_tasks.rake&lt;br /&gt;guest095:yadb ajb$ &lt;br /&gt;&lt;br /&gt;adds schema info to the top of your model files&lt;br /&gt;&lt;br /&gt;guest095:yadb ajb$ rake annotate_models&lt;br /&gt;(in /Users/ajb/dev/vtes/yadb)&lt;br /&gt;Annotating Card&lt;br /&gt;Annotating CardDiscipline&lt;br /&gt;Annotating Clan&lt;br /&gt;Unable to annotate Clan: Could not find table 'clans'&lt;br /&gt;Annotating CostType&lt;br /&gt;Annotating Deck&lt;br /&gt;Annotating Discipline&lt;br /&gt;Annotating Minion&lt;br /&gt;&lt;br /&gt;card.rb:&lt;br /&gt;&lt;br /&gt;# == Schema Information&lt;br /&gt;# Schema version: 6&lt;br /&gt;#&lt;br /&gt;# Table name: cards&lt;br /&gt;#&lt;br /&gt;#  id            :integer         not null, primary key&lt;br /&gt;#  name          :string(255)     &lt;br /&gt;#  text          :string(255)     &lt;br /&gt;#  requirements  :string(255)     &lt;br /&gt;#  cost          :integer         &lt;br /&gt;#  cost_type_id  :integer         &lt;br /&gt;#  minion_id     :integer         &lt;br /&gt;#  deck_id       :integer         &lt;br /&gt;#  discipline_id :integer         &lt;br /&gt;#&lt;br /&gt;&lt;br /&gt;class Card &amp;lt; ActiveRecord::Base&lt;br /&gt;  belongs_to :deck&lt;br /&gt;  belongs_to :cost_type&lt;br /&gt;  belongs_to :minion&lt;br /&gt;  has_many :card_disciplines&lt;br /&gt;  has_many :disciplines, :through =&amp;gt; :card_disciplines&lt;br /&gt;  belongs_to :vampire, :class_name =&amp;gt; "Minion", :conditions =&amp;gt; "minion.name = 'vampire'"&lt;br /&gt;  belongs_to :werewolf, :class_name =&amp;gt; "Minion", :conditions =&amp;gt; "minion.name = 'werewolf'"&lt;br /&gt;&lt;br /&gt;  validates_presence_of   :name&lt;br /&gt;  validates_uniqueness_of :name&lt;br /&gt;  validates_presence_of   :deck_id&lt;br /&gt;  &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;writing a plugin&lt;br /&gt;&lt;br /&gt;guest095:yadb ajb$ script/generate plugin nice_error_fields&lt;br /&gt;      create  vendor/plugins/nice_error_fields/lib&lt;br /&gt;      create  vendor/plugins/nice_error_fields/tasks&lt;br /&gt;      create  vendor/plugins/nice_error_fields/test&lt;br /&gt;      create  vendor/plugins/nice_error_fields/README&lt;br /&gt;      create  vendor/plugins/nice_error_fields/MIT-LICENSE&lt;br /&gt;      create  vendor/plugins/nice_error_fields/Rakefile&lt;br /&gt;      create  vendor/plugins/nice_error_fields/init.rb&lt;br /&gt;      create  vendor/plugins/nice_error_fields/install.rb&lt;br /&gt;      create  vendor/plugins/nice_error_fields/uninstall.rb&lt;br /&gt;      create  vendor/plugins/nice_error_fields/lib/nice_error_fields.rb&lt;br /&gt;      create  vendor/plugins/nice_error_fields/tasks/nice_error_fields_tasks.rake&lt;br /&gt;      create  vendor/plugins/nice_error_fields/test/nice_error_fields_test.rb&lt;br /&gt;&lt;br /&gt;guest095:yadb ajb$ cd vendor/plugins/nice_error_fields/&lt;br /&gt;guest095:nice_error_fields ajb$ ls&lt;br /&gt;MIT-LICENSE Rakefile install.rb tasks  uninstall.rb&lt;br /&gt;README  init.rb  lib  test&lt;br /&gt;&lt;br /&gt;install.rb needs the require 'nice_error_fields.rb'&lt;br /&gt;and the lib directory is added to the path&lt;br /&gt;&lt;br /&gt;the init.rb files are all read (so no need to create an initializer) and all the lib paths are added&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7407349534612751298?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7407349534612751298/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7407349534612751298' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7407349534612751298'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7407349534612751298'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/advancing-with-rails-course-day-4-pt-2.html' title='Advancing with Rails course - Day 4 pt 2'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4330483221640602517</id><published>2008-04-03T03:10:00.000-07:00</published><updated>2008-04-03T03:14:37.542-07:00</updated><title type='text'>Advancing with Rails course - Day 4 pt 1</title><content type='html'>Forms first off, a bit of idioms created based on RESTful:&lt;br /&gt;&lt;br /&gt;forms if you where to put in def new to render edit, when rendered it knows to post to create rather than edit,&lt;br /&gt;and know is calling edit to put to update&lt;br /&gt;&lt;br /&gt;AciveRecord can query objects via the new_record? method (@thing.new_record?), which means that it can determine whether it is a new object and so RESTfully can determine whether to create or update&lt;br /&gt;&lt;br /&gt;David doesn't like this as it has the feeling that it is not very elegant. It is over economising. Use a partial if there is &amp;gt;serious overlap, and then have different templates, which can have extra bits.&lt;br /&gt;&lt;br /&gt;(This was originally a plugin simply_helpful, now core and only works with REST)&lt;br /&gt;&lt;br /&gt;Subclass&lt;br /&gt;&lt;br /&gt;subdirectories&lt;br /&gt;&lt;br /&gt;You can&lt;br /&gt;&lt;br /&gt;script/generate controller admin/things&lt;br /&gt;&lt;br /&gt;in routes&lt;br /&gt;&lt;br /&gt;map.namespace :admin do |n|&lt;br /&gt;  n.resources :things&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;this gives a set of restful routes like new_admin_thing&lt;br /&gt;with a&lt;br /&gt;parallel tree of views app/views/admin/things/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Then onto some more Ruby&lt;br /&gt;&lt;br /&gt;Procs and Callbacks&lt;br /&gt;&lt;br /&gt;Ruby: proc (function) objects&lt;br /&gt;&lt;br /&gt;anonymous functions which are themselves objects (and can bepassed around, etc)&lt;br /&gt;&lt;br /&gt;Proc can remember a local variable within itself, rather than it going out of scope and being garbage collected&lt;br /&gt;&lt;br /&gt;guest095:yadb ajb$ irb&lt;br /&gt;&amp;gt;&amp;gt; Proc.new {}&lt;br /&gt;=&amp;gt; #&amp;lt;Proc:0x00000000@(irb):1&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; lambda {}&lt;br /&gt;=&amp;gt; #&amp;lt;Proc:0x00000000@(irb):2&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; proc {}&lt;br /&gt;=&amp;gt; #&amp;lt;Proc:0x00000000@(irb):3&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;lambda and proc are synonyms, but different from Proc.new&lt;br /&gt;&lt;br /&gt;code blocks can be captured within a def method&lt;br /&gt;&lt;br /&gt;p = lambda {|x| puts x * 10}&lt;br /&gt;array = [1,2,3,4,5,]&lt;br /&gt;array.each(&amp;p)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def convert(n, &amp;block)  &amp;lt;-- special &amp; argument syntax captures code block as a Proc object&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;puts convert (10) { |x| x * 30 }&lt;br /&gt;&lt;br /&gt;alternative&lt;br /&gt;&lt;br /&gt;def convert(n)&lt;br /&gt; if block_given?&lt;br /&gt;   yield(n)&lt;br /&gt; else&lt;br /&gt;   n * 2&lt;br /&gt; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;good unless you need to objectify the method for some reason&lt;br /&gt;&lt;br /&gt;it is a closure on the variables that exist around where it is created&lt;br /&gt;&amp;gt;&amp;gt; y= 1; [1,2,3].each {|x| puts x * 10; puts y; y += 1 }&lt;br /&gt;10&lt;br /&gt;1&lt;br /&gt;20&lt;br /&gt;2&lt;br /&gt;30&lt;br /&gt;3&lt;br /&gt;=&amp;gt; [1, 2, 3]&lt;br /&gt;&amp;gt;&amp;gt; puts y&lt;br /&gt;4&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;br /&gt;but leaves alone ones created after its creation&lt;br /&gt;&lt;br /&gt;&amp;gt;&amp;gt; class Counter&lt;br /&gt;&amp;gt;&amp;gt; def self.create(n=0, inc=1)&lt;br /&gt;&amp;gt;&amp;gt; return Proc.new{ n += inc; n - inc }&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; c = Counter.create&lt;br /&gt;=&amp;gt; #&amp;lt;Proc:0x0059f330@(irb):3&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; puts c.call&lt;br /&gt;0&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; puts c.call&lt;br /&gt;1&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; n = 222&lt;br /&gt;=&amp;gt; 222&lt;br /&gt;&amp;gt;&amp;gt; c = Counter.create&lt;br /&gt;=&amp;gt; #&amp;lt;Proc:0x0059f330@(irb):3&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; puts c.call&lt;br /&gt;0&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; puts c.call&lt;br /&gt;1&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; c = Counter.create(5,5)&lt;br /&gt;=&amp;gt; #&amp;lt;Proc:0x0059f330@(irb):3&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; puts c.call&lt;br /&gt;5&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; puts c.call&lt;br /&gt;10&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; puts n&lt;br /&gt;222&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;you can write code blocks with {} or do - end, but they are not interchangeable&lt;br /&gt;&lt;br /&gt;&amp;gt;&amp;gt; puts [1,2,3].map {|x| x * 10 }&lt;br /&gt;10&lt;br /&gt;20&lt;br /&gt;30&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; puts [1,2,3].map do |x| x * 10 end&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;3&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;br /&gt;you can write a method to warn about the presence of a block (or not), but not every ruby method will do this&lt;br /&gt;&lt;br /&gt;&amp;gt;&amp;gt; def m; raise "A block!" if block_given?; end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; m &lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; m {}&lt;br /&gt;RuntimeError: A block!&lt;br /&gt; from (irb):21:in `m'&lt;br /&gt; from (irb):23&lt;br /&gt;&lt;br /&gt;&amp;gt;&amp;gt; m do |x| x*10 end&lt;br /&gt;RuntimeError: A block!&lt;br /&gt; from (irb):27:in `m'&lt;br /&gt; from (irb):28&lt;br /&gt;&lt;br /&gt;&amp;gt;&amp;gt; def m; raise "A block!" if !block_given?; end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; m {}&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; m&lt;br /&gt;RuntimeError: A block!&lt;br /&gt; from (irb):24:in `m'&lt;br /&gt; from (irb):26&lt;br /&gt;&lt;br /&gt;Built in callbacks&lt;br /&gt;method_missing&lt;br /&gt;Modules: - included&lt;br /&gt;Classes: - inherited&lt;br /&gt;&lt;br /&gt;&amp;gt;&amp;gt; module M&lt;br /&gt;&amp;gt;&amp;gt;  def talk; puts "Hi!"; end&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; M.new&lt;br /&gt;NoMethodError: undefined method `new' for M:Module&lt;br /&gt; from (irb):4&lt;br /&gt;&lt;br /&gt;you can't instantiate a module, but they are good for mixins&lt;br /&gt;&lt;br /&gt;you can include Classes in Modules&lt;br /&gt;&lt;br /&gt;&amp;gt;&amp;gt; module Violin&lt;br /&gt;&amp;gt;&amp;gt; class String; end&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;br /&gt;modules can give their methods to a class, via include&lt;br /&gt;&lt;br /&gt;?&amp;gt; class Person&lt;br /&gt;&amp;gt;&amp;gt;  include M&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;=&amp;gt; Person&lt;br /&gt;&amp;gt;&amp;gt; andy = Person.new&lt;br /&gt;=&amp;gt; #&amp;lt;Person:0x59b71c&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; andy.talk&lt;br /&gt;Hi!&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Classes - inherited&lt;br /&gt;&lt;br /&gt;&amp;gt;&amp;gt; class Furniture&lt;br /&gt;&amp;gt;&amp;gt; def self.inherited(c)&lt;br /&gt;&amp;gt;&amp;gt; puts "#{self} has been inherited by class #{c}"&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; &lt;br /&gt;?&amp;gt; class Chair &amp;lt; Furniture&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;Furniture has been inherited by class Chair&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;extend&lt;br /&gt;&lt;br /&gt;?&amp;gt; module N&lt;br /&gt;&amp;gt;&amp;gt; def walk; puts "I am walking!"; end&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; Person.ancestors&lt;br /&gt;=&amp;gt; [Person, M, Object, Kernel]&lt;br /&gt;&amp;gt;&amp;gt; andy.walk&lt;br /&gt;NoMethodError: undefined method `walk' for #&amp;lt;Person:0x59b71c&amp;gt;&lt;br /&gt; from (irb):29&lt;br /&gt;&amp;gt;&amp;gt; andy.extend(N)&lt;br /&gt;=&amp;gt; #&amp;lt;Person:0x59b71c&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; andy.walk&lt;br /&gt;I am walking!&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;br /&gt;use extend via a module if you want to replace core methods as this is a low impact way&lt;br /&gt;&lt;br /&gt;&amp;gt;&amp;gt; module M;def shout;puts "HI!!!!";end;end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; class C;end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; C.extend(M)&lt;br /&gt;=&amp;gt; C&lt;br /&gt;&amp;gt;&amp;gt; C.shout&lt;br /&gt;HI!!!!&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&lt;br /&gt;Classes are objects. It extends the singleton methods on the object&lt;br /&gt;&lt;br /&gt;equivalent to &lt;br /&gt;&lt;br /&gt;class &amp;lt;&amp;lt; C; include M; end&lt;br /&gt;&lt;br /&gt;extend gives class methods, include are instance methods, all though David wasn't really sure what that ultimately would mean&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-4330483221640602517?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4330483221640602517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4330483221640602517' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4330483221640602517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4330483221640602517'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/advancing-with-rails-course-day-4-pt-1.html' title='Advancing with Rails course - Day 4 pt 1'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-6972913363108535787</id><published>2008-04-02T09:23:00.001-07:00</published><updated>2008-04-02T09:24:02.415-07:00</updated><title type='text'>Advancing with Rails course - Day 3 - pt3</title><content type='html'>Final blog for day 3 (I'll probably end up summarising a lot of this is a presentation to foomongers, so I'll post the slides if I do. However, for now:&lt;br /&gt;&lt;br /&gt;We started to look at REST.&lt;br /&gt;&lt;br /&gt;Just a few notes- not in any structure. A good resource to read is Roy T. Fielding's dissertation (http://www.ics.uci.edu/~fielding/)&lt;br /&gt;&lt;br /&gt;Representational State Transfer&lt;br /&gt;&lt;br /&gt;request sequence (slide 198)&lt;br /&gt;&lt;br /&gt;by default&lt;br /&gt;&lt;br /&gt;link_to =&amp;gt; GET &lt;br /&gt;form_for =&amp;gt; POST&lt;br /&gt;&lt;br /&gt;link_to with movie_path =&amp;gt; GET &lt;br /&gt;link_to using edit_movie_path =&amp;gt; GET&lt;br /&gt;&lt;br /&gt;as browsers don't issue PUT and DELETE requests, rails cheats with:&lt;br /&gt;&lt;br /&gt;to get PUT:&lt;br /&gt;form_for with :url =&amp;gt; movie_path(@movie) and :html =&amp;gt; (:method =&amp;gt; "put") &lt;br /&gt;you get =&amp;gt; method="post", intput type="hidden" name="_method" value="put"&lt;br /&gt;&lt;br /&gt;to get DELETE&lt;br /&gt;link_to wih item_path(@item), :method =&amp;gt; "delete"&lt;br /&gt;you get =&amp;gt; DELETE (but wrapped in a form so that spiders don't follow it)&lt;br /&gt;&lt;br /&gt;REST and CRUD have no actual real connection, but when brought into rails, then they meet up,&lt;br /&gt;because it has been orchestrated&lt;br /&gt;&lt;br /&gt;And then finally during the day onto some Ajax stuff&lt;br /&gt;&lt;br /&gt;Ajax:&lt;br /&gt;&lt;br /&gt;Ajax request happens overall with in the cycle of a request&lt;br /&gt;&lt;br /&gt;link_to_remote "Click me", :url =&amp;gt; {....}&lt;br /&gt;-&amp;gt; goes to server, does stuff C/A -&amp;gt; sends back to client&lt;br /&gt;&lt;br /&gt;typically, not a whole cycle, as only sends back a snippet/fragment which you want to drop into your document&lt;br /&gt;&lt;br /&gt;link_to_remote "Click me", :url =&amp;gt; {:action =&amp;gt; click_me}, :update =&amp;gt; "mydiv"&lt;br /&gt;&lt;br /&gt;so drops it into a div entitled mydiv&lt;br /&gt;&lt;br /&gt;def click_me&lt;br /&gt;  render :partial =&amp;gt; "clicked"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;renders the partial and drops it in&lt;br /&gt;&lt;br /&gt;link_to_remote "Click me", :url =&amp;gt; {:action =&amp;gt; click_me}, :div =&amp;gt; "mydiv"&lt;br /&gt;&lt;br /&gt;as no update&lt;br /&gt;&lt;br /&gt;def click_me&lt;br /&gt;  @div = params[:div]&lt;br /&gt;  @del = Auction.delete(params[:id])&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;hands off to click_me.rjs&lt;br /&gt;&lt;br /&gt;if @del.zero?&lt;br /&gt;  page.alert("Destroy operation failed")&lt;br /&gt;else&lt;br /&gt;  page.visual_effect :shrink, @div, :duration =&amp;gt;1&lt;br /&gt;  page.delay(2) do&lt;br /&gt;    page.alert("That auction is history!")&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;page sends back the javascript to do what you want&lt;br /&gt;&lt;br /&gt;form_remote_for :item, :url =&amp;gt; {:action =&amp;gt; :update}, :update =&amp;gt; "mydiv"&lt;br /&gt;&lt;br /&gt;flash is normally waiting for the next request cycle&lt;br /&gt;&lt;br /&gt;flash.now[:notice] will have it come through to the ajx response there and then, rather than wait for the page refresh&lt;br /&gt;&lt;br /&gt;raise request.xhr? method which means is this an ajax request, which means that you can fork on the request.&lt;br /&gt;&lt;br /&gt;name.html.erb will take preference over name.rjs, so you either shouldn't have them with the same name,&lt;br /&gt;or render rjs directly in the controller&lt;br /&gt;&lt;br /&gt;render :update do |page|&lt;br /&gt;  page.alert("Hello")&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;which would bypass that issue&lt;br /&gt;&lt;br /&gt;you can put rjs directly in the view to get javascript directly in the page&lt;br /&gt;&lt;br /&gt;xhr requests are post by default, so if you are requesting a view only allowed by get, put or delete, then you need to explicity state this in the link_to_remote tag&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-6972913363108535787?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/6972913363108535787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=6972913363108535787' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6972913363108535787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/6972913363108535787'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/advancing-with-rails-course-day-3-pt3.html' title='Advancing with Rails course - Day 3 - pt3'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-3766650951762963339</id><published>2008-04-02T04:05:00.000-07:00</published><updated>2008-04-02T04:06:37.061-07:00</updated><title type='text'>Advancing with Rails course - Day 3 - pt2</title><content type='html'>Rails Routing&lt;br /&gt;&lt;br /&gt;routing system does 2 things&lt;br /&gt;&lt;br /&gt;1) recognises and inteprets URLs&lt;br /&gt;2) generating URLs and path strings in your views&lt;br /&gt;&lt;br /&gt;recognition&lt;br /&gt; - goals to determine controller and action&lt;br /&gt;            store other values in params hash&lt;br /&gt;&lt;br /&gt;generation&lt;br /&gt; - arguments to link_to, form_for, etc&lt;br /&gt; - for redirection in controllers&lt;br /&gt; anywhere you need a path or URL in your actual code&lt;br /&gt; &lt;br /&gt;top-level route&lt;br /&gt; &lt;br /&gt;  map.root :controller =&amp;gt; "cards"&lt;br /&gt;&lt;br /&gt;link_to "top", root_path&lt;br /&gt;&lt;br /&gt;map.connect ':controller/:action/:id'&lt;br /&gt;&lt;br /&gt;shows what you want your URL to look like&lt;br /&gt;&lt;br /&gt;routing system only makes sense to itself -&amp;gt; only the routing system can know what the URL means if it generated it.&lt;br /&gt;&lt;br /&gt;routing system doesn't know how to resolve for another application&lt;br /&gt;&lt;br /&gt;model doesn't know the controller which is manipulating it.&lt;br /&gt;&lt;br /&gt;to inject css into a link_to&lt;br /&gt;&lt;br /&gt;&amp;lt;%= link_to "log in", {:controller =&amp;gt; 'this', :action =&amp;gt; 'that'}, :class =&amp;gt; "blah" %&amp;gt;&lt;br /&gt;&lt;br /&gt;hard coding&lt;br /&gt;&lt;br /&gt;map.connect 'help',&lt;br /&gt;  :controller =&amp;gt; "leagues",&lt;br /&gt;  ;action =&amp;gt; "assist"&lt;br /&gt;&lt;br /&gt;this will resolve http://www.web.site.com/help to the specific path leagues/assist&lt;br /&gt;and will only resolve /help as that&lt;br /&gt;&lt;br /&gt;map.connect 'help/:controller', :action =&amp;gt; "assist"&lt;br /&gt;&lt;br /&gt;resolves controller in  http://www.web.site.com/help/wibble to the path wibble/assist&lt;br /&gt;&lt;br /&gt;extra wildcards can also be added and matched positionally, which are then stored in params&lt;br /&gt;:id is special case though, which allows it to be nil, whereas anything else needs to specified,&lt;br /&gt;or will find no route (unless you use globbing)&lt;br /&gt;&lt;br /&gt;you can constrain the wildcards with pattern matching, etc and have many of these, but orderis important&lt;br /&gt;(higher up the routes.rb file, will hit first)&lt;br /&gt;&lt;br /&gt;You may need to catch a routing error.&lt;br /&gt;&lt;br /&gt;current and default values&lt;br /&gt;&lt;br /&gt;missing components get defaults&lt;br /&gt;&lt;br /&gt;:controller and :action from current ones&lt;br /&gt;:id, :topic, etc from params hash&lt;br /&gt;&lt;br /&gt;default gets turned off after the first one you specify&lt;br /&gt;&lt;br /&gt;named routes:&lt;br /&gt;&lt;br /&gt;map.&amp;lt;name&amp;gt; "name",&lt;br /&gt;  :controller =&amp;gt; :x,&lt;br /&gt;  :action =&amp;gt; :y&lt;br /&gt;&lt;br /&gt;then allows&lt;br /&gt;redirect_to name_url&lt;br /&gt;link_to "text" name_path&lt;br /&gt;&lt;br /&gt;map.vampires 'vampire/:name/:capacity',&lt;br /&gt;  :controller =&amp;gt; :cards,&lt;br /&gt;  :action =&amp;gt; :vampires&lt;br /&gt;  &lt;br /&gt;&amp;lt;%= link_to "#{vampire} with capacity #{capacity}",&lt;br /&gt;  vampires_path :name =&amp;gt; vampire, :capacity =&amp;gt; capacity %&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;%= link_to "#{vampire} with capacity #{capacity}",&lt;br /&gt;  vampires_path (vampire, capacity) %&amp;gt;&lt;br /&gt;(walks through the list into the url space)&lt;br /&gt;&lt;br /&gt;Named routes and CRUD&lt;br /&gt;&lt;br /&gt;these can be expressive and encourage good action names&lt;br /&gt;&lt;br /&gt;cluster thinking around the operation (borrow book is create loan, renew is update, return is destroy/delete, view all loans is read)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-3766650951762963339?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/3766650951762963339/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=3766650951762963339' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3766650951762963339'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/3766650951762963339'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/advancing-with-rails-course-day-3-pt2.html' title='Advancing with Rails course - Day 3 - pt2'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1893586781597187635</id><published>2008-04-02T04:01:00.000-07:00</published><updated>2008-04-02T04:04:50.417-07:00</updated><title type='text'>Advancing with Rails course - Day 3 - pt1</title><content type='html'>protect_from_forgery -&amp;gt; ensures that posts are only acted on if from your application&lt;br /&gt;&lt;br /&gt;session hash is now stored as a cookie on the client side&lt;br /&gt;typical to put in a user_id&lt;br /&gt;&lt;br /&gt;before_filter s : anything is true unless false and will stop processing if it gets false,&lt;br /&gt;so explicitly return true&lt;br /&gt;&lt;br /&gt;attr_accessible and attr_protected&lt;br /&gt;&lt;br /&gt;in model&lt;br /&gt;&lt;br /&gt;attr_protected :admin, :hashed_password&lt;br /&gt;black list of stuff which can't be changed&lt;br /&gt;&lt;br /&gt;attr_accessible :price, :size&lt;br /&gt;white list of stuff which can be changed&lt;br /&gt;&lt;br /&gt;however, if not in attr_accessible and this is declared, can't be updated&lt;br /&gt;&lt;br /&gt;needs maintenance by hand, but pretty cheap&lt;br /&gt;&lt;br /&gt;untrusted_input &lt;br /&gt;&lt;br /&gt;item = Item.find(:first, :conditions =&amp;gt; ["material = ?", untrusted_input])&lt;br /&gt;&lt;br /&gt;Don't know how the database does boolean&lt;br /&gt;User.find(:first, :conditions =&amp;gt; ["admin = ?", true])&lt;br /&gt;&lt;br /&gt;for doing in&lt;br /&gt;User.find(:first, :conditions =&amp;gt; ["email in (?)", ["a@c", "b@d]])&lt;br /&gt;&lt;br /&gt;User.find(:first, :conditions =&amp;gt; ["created_at &amp;lt; ?", Time.now])&lt;br /&gt;&lt;br /&gt;method h&lt;br /&gt;&lt;br /&gt;&amp;lt;%=h item.name %&amp;gt;&lt;br /&gt;&lt;br /&gt;when there is the chance that it could be html which could get injected, which then escapes the html and so doesn't drag in a &amp;lt;script&amp;gt; or &amp;lt;img&amp;gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1893586781597187635?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1893586781597187635/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1893586781597187635' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1893586781597187635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1893586781597187635'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/advancing-with-rails-course-day-3-pt1.html' title='Advancing with Rails course - Day 3 - pt1'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7491606673867172096</id><published>2008-04-02T00:46:00.000-07:00</published><updated>2008-04-02T00:52:16.616-07:00</updated><title type='text'>Advancing with Rails course - Day 2</title><content type='html'>April 1st&lt;br /&gt;&lt;br /&gt;So, a lot to blog about from day 2. Don't feel you need to read it all, and some may hardly be a revelation to most, but part of the purpose of this blog is also for my own record of what occurred and what I learnt, so you'll have to bear with me.&lt;br /&gt;&lt;br /&gt;We did a lot of working within our own apps today, doing little bits of lecture, and then seeing if it can be applied.&lt;br /&gt;I seem to have picked a good little app to work on, as it does forms, and needs associations, which was some of what we worked on today. (Looking through my notes again, please note that some of these are my interpretations of the discussions an what notes I grabbed, so may not be the whole truth, or even everything David said)&lt;br /&gt;&lt;br /&gt;The first lecture bit started on the "Request Cycle".&lt;br /&gt;&lt;br /&gt;tThis was hardly a revelation to myself, but was nice to see it formalised. Basically, all rails apps work on the idea of a request cycle.&lt;br /&gt;&lt;br /&gt;A user clicks somewhere (to launch the application, a hyperlink, sends a form...)&lt;br /&gt;Mongrel sets off a dispatcher, which goes to routing system to load share (if your server is set up this way)&lt;br /&gt;then calls controller/action/params[x]&lt;br /&gt;&lt;br /&gt;i.e. items/show/1&lt;br /&gt; -&amp;gt;  contr/act/params[:id]&lt;br /&gt;&lt;br /&gt;It then takes items and adds up to make it items_controller.rb and looks for ItemsController class.&lt;br /&gt;&lt;br /&gt;This is now just all in the hands of the ruby interpreter, the server is just waiting for a response, which it then sends back to the user.&lt;br /&gt;&lt;br /&gt;The server then cares nothing about what happens until it receives another request from the user. Quite obvious, but it explains why new instances of the objects are created each time, whereas within a gui app, the program loads once, and then there are very few times that a new instance of an object would get created. However, the application expects further user input (even if it is just a kill command), whereas the Rails app doesn't. Even if you request a form, the rails app doesn't actually expect you to fill it in and send it back.&lt;br /&gt;&lt;br /&gt;So what is happening during the ruby interpreter phase.&lt;br /&gt;&lt;br /&gt;firstly it creates an instance or object of the controller&lt;br /&gt;&lt;br /&gt;controller = ItemsController.new&lt;br /&gt;&lt;br /&gt;and then looks for an action on that instance of the given name&lt;br /&gt;&lt;br /&gt;controller.show&lt;br /&gt;&lt;br /&gt;passing in the params[:id] if given&lt;br /&gt;&lt;br /&gt;now by default active record objects know that they should render a view with the same name, but you can use the render method to render another template. They also know how to render themselves as xml by default.&lt;br /&gt;&lt;br /&gt;render :xml =&amp;gt; @item&lt;br /&gt;&lt;br /&gt;or you could use a redirect, but this starts a new request cycle (sends a 302 to the user/client, which the goes back to mongrel), which therefore needs to reassign and item id (and any other params that might need to pass through)&lt;br /&gt;&lt;br /&gt;(update doesn't typically have an update.html.erb, as you usually want a redirect or to render the form again)&lt;br /&gt;&lt;br /&gt;render uses the changed values, whereas redirect sets a new instance, which then enables the wrong values to propagate through to the rendered template&lt;br /&gt;&lt;br /&gt;before_filter command&lt;br /&gt;&lt;br /&gt;before_filter :set_item,&lt;br /&gt;              :only =&amp;gt; ['edit', 'udpate', 'show']&lt;br /&gt;this would cause set_item action to run before the stated actions&lt;br /&gt;&lt;br /&gt;This is good, because it then enforces use of the correct variable use, and then, should you end up with a blank action (ie: def show;end) because the set_item does everything, then it will jump through and render the show.html.erb file, without even needing def show;end to be there at all&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now, instance variables are how the controller talks to the views, but in this case the instance variable does not belong purely to self, as&lt;br /&gt;&lt;br /&gt;self in controller is a controller object&lt;br /&gt;self in view is an ActionView::Base object&lt;br /&gt;&lt;br /&gt;when rails hands off a variable to the view, it walks through the array and sets its type to the ActionView::Base,&lt;br /&gt;so they don't technically share them.&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;&lt;br /&gt;Before lunch we looked at non-default associations. This got very interesting for my app YADB, we found a plugin which allowed nested has many through associations.&lt;br /&gt;&lt;br /&gt;belongs_to :auction (gives methods from Auction)&lt;br /&gt;belongs_to :bidder, :class_name =&amp;gt; "User" (enables User to be used as a bidder, but no bidder object)&lt;br /&gt;&lt;br /&gt;self.bidder = User.new(x)&lt;br /&gt;&lt;br /&gt;has_many :bids, :foreign_key =&amp;gt; "bidder_id" (sets the use of a different foreign key as otherwise would look for bids_id)&lt;br /&gt;has_many :auctions_held, :class_name =&amp;gt; "Auction", :foreign_key =&amp;gt; "seller_id"&lt;br /&gt;      (combination of the previous two)&lt;br /&gt;has_many :auctions_bid_on (no class), :through =&amp;gt; :bids,&lt;br /&gt;                        :source =&amp;gt; :auction, (however you got there, I want to know via bid.auction)&lt;br /&gt;                        :uniq =&amp;gt; true (creates many to many relationship, but with useful join table with it's own model)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;define singleton methods on the association&lt;br /&gt;&lt;br /&gt;has_many :bids do&lt;br /&gt;  def average_interval&lt;br /&gt;    i = 0.0&lt;br /&gt;    inject {|a,b| i += b.created_at = a.created_at; b }&lt;br /&gt;    i / (size-1)/ 1.day&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;A bit on inject:&lt;br /&gt;&lt;br /&gt;it is a method on enumerable&lt;br /&gt;&lt;br /&gt;[1,2,3,4].inject do |a,b|&lt;br /&gt;  a+b&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;loop&lt;br /&gt;&lt;br /&gt;1st time 1 is a, 2 is b&lt;br /&gt;next time a is result of code block, b is the next element&lt;br /&gt;&lt;br /&gt;1: a = 1, b = 2&lt;br /&gt;2: a = 3, b = 3&lt;br /&gt;3: a = 6, b = 4&lt;br /&gt;&lt;br /&gt;also used with hashes (both are equivalent)&lt;br /&gt;&lt;br /&gt;[1,2,3,4,5].inject({}) do |hash,e| hash[e] = e * 10; hash; end&lt;br /&gt;[1,2,3,4,5].inject({}) do |hash,e| hash.update(e =&amp;gt; e * 10); end&lt;br /&gt;&lt;br /&gt;I had a problem about trying to link through 2 tables, with the following idea&lt;br /&gt;&lt;br /&gt;class Disciplines &amp;lt; ActiveRecord::Base&lt;br /&gt;  has_many :card_disciplines&lt;br /&gt;  has_many :cards, :through =&amp;gt; :card_disciplines&lt;br /&gt;  has_many :vampires,&lt;br /&gt;           :through =&amp;gt; :cards,&lt;br /&gt;           :source =&amp;gt; :minion, :conditions =&amp;gt; "minions.name = 'vampire'"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;Now this isn't in 2.0.2, but there had been a lot of discussion about it. A patch has been submitted and the writers have created the following plugin &lt;br /&gt;&lt;br /&gt;nested_has_many_through.rb&lt;br /&gt;https://svn.colivre.coop.br/svn/noosfero/trunk/vendor/plugins/nested_has_many_through/&lt;br /&gt;&lt;br /&gt;which works. David mentioned that the discussion about this has reached a point where he thought it had been incorporated, and may be in edge rails, but clearly didn't make the cut to 2.0.2&lt;br /&gt;&lt;br /&gt;I hope that it makes it in, as when I first attempted YADB with 1.2, I was looking for some links like this. There is some discussion about how this hits the database, but it keeps the Rails pragma, instead of writing a SQL statement myself, and still only generated 1 single SQL statement. What more do you want?&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;&lt;br /&gt;We then looked at errors and validation.&lt;br /&gt;&lt;br /&gt;ActiveRecord examines the object to decide if it is valid. If it fails, then it never even attempts to save the information in the database.&lt;br /&gt;&lt;br /&gt;validates_size_of :name, minimum =&amp;gt; 5&lt;br /&gt;validates_size_of :name, maximum =&amp;gt; 50&lt;br /&gt;  - &amp;gt; must be on separate lines&lt;br /&gt;  &lt;br /&gt;You can create your own validations by using validate&lt;br /&gt;&lt;br /&gt;def validate&lt;br /&gt;  errors.add("name", "That's impossible") if name =~ /\d/&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;All ActiveRecord objects have and errors attribute. If the errors attribute is empty, then the object is valid&lt;br /&gt;&lt;br /&gt;You can trap the errors if the database has the constraints on it that disallow data to be be saved, but do this via the controller, as you are expecting a big error, rather than a protection of the data integrity&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;&lt;br /&gt;This then lead onto forms and processing, starting with a bit on the difference between each and map&lt;br /&gt;&lt;br /&gt;each vs map&lt;br /&gt;&lt;br /&gt;each loops over 1 element at a time, and return value is the object&lt;br /&gt;&lt;br /&gt;x = [1,2,3,4]&lt;br /&gt;y = x.each {|e| puts e*10}&lt;br /&gt;y.equal?(x) =&amp;gt; true&lt;br /&gt;&lt;br /&gt;map returns a mapping (an accumulator of the results of the block)&lt;br /&gt;&lt;br /&gt;newx = x.map {|e| e* 10 }&lt;br /&gt;newx =&amp;gt; [10,20,30,40]&lt;br /&gt;&lt;br /&gt;What goes on when you display and process forms&lt;br /&gt;&lt;br /&gt;&amp;lt;% form_tag :action =&amp;gt; 'update', :id =&amp;gt; @item.id do %&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt; name: &amp;lt;%= text_field 'item', 'name' %&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt; year: &amp;lt;%= text_field 'item', 'year' %&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;prepopulates these on form generation&lt;br /&gt;&lt;br /&gt;then, for each first argument&lt;br /&gt;&lt;br /&gt;params[:item]&lt;br /&gt;&lt;br /&gt;and then&lt;br /&gt;&lt;br /&gt;params[:item][:name]&lt;br /&gt;params[:item][:year]&lt;br /&gt;&lt;br /&gt;fields_for will allow you to override the default selected using form_for&lt;br /&gt;&lt;br /&gt;&amp;lt;% form_for 'auction' :url =&amp;gt; {:action =&amp;gt; create} do |f| %&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;p&amp;gt;Title: &amp;lt;%= f.text_field "title" %&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;% fields_for 'item' do |fi| %&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;Description: &amp;lt;%= fi.text_field 'description' %&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;br /&gt;&amp;lt;%= submit_tag %&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;br /&gt;&lt;br /&gt;gives&lt;br /&gt;&lt;br /&gt;params[:auction][:title]&lt;br /&gt;params[:item][:description]&lt;br /&gt;&lt;br /&gt;errors which occur get wrapped with a &amp;lt;div class="fieldWithErrors"&amp;gt;&amp;lt;/div&amp;gt; which causes the box to jump down beneath the title.&lt;br /&gt;&lt;br /&gt;This means you could style it&lt;br /&gt;&lt;br /&gt;You can change it.&lt;br /&gt;&lt;br /&gt;It calls a proc, which is a function and you can replace it.&lt;br /&gt;&lt;br /&gt;Lazy (in environment.rb, should be an initializer)&lt;br /&gt;&lt;br /&gt;ActionView::Base.field_error_proc = Proc.new {|a.b|&lt;br /&gt; "&amp;lt;p&amp;gt;Andy's placeholder&amp;lt;/p&amp;gt;"&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;This is an executable object that I can call again and again when I want it.&lt;br /&gt;&lt;br /&gt;Look on google with 'field_error_proc'&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;&lt;br /&gt;The final lecture session was our daily dose of ruby. We were looking at the singleton method. This was interesting to find out, as we looked the singleton class, and a bit on how an object looks to find if it has a method.&lt;br /&gt;&lt;br /&gt;The method lookup path&lt;br /&gt;&lt;br /&gt;1) The object's singleton class&lt;br /&gt; - modules mixed into singleton class&lt;br /&gt;2) The object's class&lt;br /&gt; - modules mixed into object's class&lt;br /&gt;3) The object's superclass&lt;br /&gt; - modules mixed into object's superclass&lt;br /&gt;&lt;br /&gt;repeat 3 as needed until&lt;br /&gt;-Object&lt;br /&gt;a) kernel&lt;br /&gt;&lt;br /&gt;To open the singleton class definition for self&lt;br /&gt;&lt;br /&gt;class &amp;lt;&amp;lt; self&lt;br /&gt;&lt;br /&gt;this is a very frequent idiom for class methods, and means if you have lots of&lt;br /&gt;&lt;br /&gt;def self.method&lt;br /&gt;  ...&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;then you can save the 'self.' by&lt;br /&gt;&lt;br /&gt;class &amp;lt;&amp;lt; self&lt;br /&gt;  def method&lt;br /&gt;  end&lt;br /&gt;  def another_singleton_method&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;To demonstrate this, I'll just dump here the irb that I did whilst trying this, and that will be all from day 2. Looking forward to day 3.&lt;br /&gt;&lt;br /&gt;guest095:~ ajb$ irb&lt;br /&gt;&amp;gt;&amp;gt; class Person; attr_accessor :name; end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; andy = Person.new&lt;br /&gt;=&amp;gt; #&amp;lt;Person:0x5a2abc&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; class &amp;lt;&amp;lt; andy&lt;br /&gt;&amp;gt;&amp;gt; def talk&lt;br /&gt;&amp;gt;&amp;gt; puts "Hi"&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;&amp;gt;&amp;gt; end&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; andy.talk&lt;br /&gt;Hi&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;&amp;gt;&amp;gt; aclass = Person&lt;br /&gt;=&amp;gt; Person&lt;br /&gt;&amp;gt;&amp;gt; aclass.methods&lt;br /&gt;=&amp;gt; ["to_yaml_style", "inspect", "private_class_method", "const_missing", "clone", "method", "public_methods", "public_instance_methods", "yaml_as", "instance_variable_defined?", "method_defined?", "superclass", "equal?", "freeze", "included_modules", "const_get", "to_yaml_properties", "methods", "respond_to?", "module_eval", "class_variables", "dup", "instance_variables", "protected_instance_methods", "to_yaml", "__id__", "public_method_defined?", "eql?", "object_id", "require", "const_set", "id", "send", "singleton_methods", "taguri", "class_eval", "taint", "require_gem", "instance_variable_get", "frozen?", "yaml_tag_class_name", "taguri=", "include?", "private_instance_methods", "instance_of?", "__send__", "private_method_defined?", "to_a", "name", "yaml_tag_read_class", "autoload", "type", "new", "&amp;lt;", "instance_eval", "gem", "protected_methods", "&amp;lt;=&amp;gt;", "display", "==", "&amp;gt;", "===", "instance_method", "instance_variable_set", "extend", "kind_of?", "protected_method_defined?", "const_defined?", "&amp;gt;=", "ancestors", "to_s", "&amp;lt;=", "public_class_method", "allocate", "class", "hash", "private_methods", "=~", "tainted?", "instance_methods", "class_variable_defined?", "untaint", "nil?", "constants", "is_a?", "yaml_tag_subclasses?", "autoload?"]&lt;br /&gt;&amp;gt;&amp;gt; dc = class &amp;lt;&amp;lt; andy; self; end&lt;br /&gt;=&amp;gt; #&amp;lt;Class:#&amp;lt;Person:0x5a2abc&amp;gt;&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; dc&lt;br /&gt;=&amp;gt; #&amp;lt;Class:#&amp;lt;Person:0x5a2abc&amp;gt;&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt; dc.instance_methods.sort&lt;br /&gt;=&amp;gt; ["==", "===", "=~", "__id__", "__send__", "class", "clone", "display", "dup", "eql?", "equal?", "extend", "freeze", "frozen?", "gem", "hash", "id", "inspect", "instance_eval", "instance_of?", "instance_variable_defined?", "instance_variable_get", "instance_variable_set", "instance_variables", "is_a?", "kind_of?", "method", "methods", "name", "name=", "nil?", "object_id", "private_methods", "protected_methods", "public_methods", "require", "require_gem", "respond_to?", "send", "singleton_methods", "taguri", "taguri=", "taint", "tainted?", "talk", "to_a", "to_s", "to_yaml", "to_yaml_properties", "to_yaml_style", "type", "untaint"]&lt;br /&gt;&amp;gt;&amp;gt; dc.instance_methods(false)&lt;br /&gt;=&amp;gt; ["name", "talk", "name="]&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7491606673867172096?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7491606673867172096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7491606673867172096' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7491606673867172096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7491606673867172096'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/advancing-with-rails-course-day-2.html' title='Advancing with Rails course - Day 2'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-2454735749685972874</id><published>2008-04-01T01:12:00.000-07:00</published><updated>2008-04-01T01:14:01.357-07:00</updated><title type='text'>Advancing with Rails course - Day 1</title><content type='html'>Mar 31 was the first of our 4 day Ruby on Rails course at work. It is being presented by David Black, author of Ruby for Rails, and Director of Ruby Power and Light LLC.&lt;br /&gt;&lt;br /&gt;We have essentially managed to catch him between the European Ruby conference and Scotland on Rails.&lt;br /&gt;&lt;br /&gt;The day was very much an introductory day, with a few little tidbits. We set up our own little Sandbox project to work within. I have opted to start with something I have been looking to do for sometime which is a VTES CCG deck-building program. (VTES is a vampire based collectable card game which is where the Vampire Software comes from. It will be something I'll get into sourceforge soon!)&lt;br /&gt;&lt;br /&gt;David also explained a bit more about Ruby objects and rails functional testing. The former was a good tidy up of what I already knew/had worked out. The latter was a good formalisation of what they are trying to achieve, as whilst I had done some functional testing in rails before, most was concentrated on the unit testing. (A note here. functional testing can mean different things in different frameworks. Some prefer to think of functional testing in Rails as just an extension of Unit testing specifically for controllers, as in other frameworks, functional tests are what Rails calls Integration tests).&lt;br /&gt;&lt;br /&gt;At this point, David also gave us a reason for NOT using scaffold generation to generate model backed controllers and views. Personally, I like this approach (I think that code which writes code is a very good thing) but he says that as you get further into rails, you end up removing more from scaffold generated code, than you would write that could have been auto-generated, and as such prefers to suggest that you should learn writing from scratch. With my limited experience, I'll take a rain-check on making a confirmed decision, but I will (for now), stick with auto-generation when it comes to a model backed controllers.&lt;br /&gt;&lt;br /&gt;One thing here though that I did discover is in Rails 2.0.2, if you have generated your model first, and then run script/generate scaffold &lt;model&gt;, it stops after it finds a migration with the same name as it wants to generate. You actually need to do the following&lt;br /&gt;&lt;br /&gt;1) script/generate scaffold &lt;model&gt; --skip-migration&lt;br /&gt;2) not do script/generate model &lt;model&gt; first&lt;br /&gt;3) rename the migration file and class within it before running&lt;br /&gt;&lt;br /&gt;This was annoying, as before when I did this, it used to ask if I wanted to replace it, and it generates everything up to the point where it barfs, but nothing after it. Oh well, I suppose it is something I can get used to. Just think I'll need to read up on the new stuff for 2.0.2 sooner than I expected. :)&lt;br /&gt;&lt;br /&gt;We also touched upon the new Initializers, which look very useful for keeping track of requirements. I hope we might look at those again.&lt;br /&gt;&lt;br /&gt;The final thing to talk about here was how to implement Composite Primary Keys. Firstly - why? ActiveRecord is never going to support them.&lt;br /&gt;&lt;br /&gt;Secondly - However, we have a big legacy database using them extensively, so David showed us how to implement Dr. Nic's composite_primary_keys gem (requiring it via an Initializer!). I won't explain it all here, but the scratchpad attempt I made of it worked well. (to install it though - sudo gem install composite_primary_keys)&lt;br /&gt;&lt;br /&gt;And finally, speaking of Legacy databases. In Rails 'type' is a reserved column type for when using Single Table Inheritance, and ActiveRecord expects to use this as a class name. We have fallen foul of this with our legacy database, but fall foul of it no more. In the model put:&lt;br /&gt;&lt;br /&gt;set_inheritance_column :nothing&lt;br /&gt;&lt;br /&gt;and hey presto! you can use the 'type' column in your legacy schema as planned.&lt;br /&gt;&lt;br /&gt;That will do for now. Let's see what the next day brings.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-2454735749685972874?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/2454735749685972874/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=2454735749685972874' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2454735749685972874'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/2454735749685972874'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/04/advancing-with-rails-course-day-1.html' title='Advancing with Rails course - Day 1'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5197365873351122677</id><published>2008-03-19T08:14:00.000-07:00</published><updated>2008-03-19T08:18:27.099-07:00</updated><title type='text'>Composite Primary Keys - Yuuurrghhh!</title><content type='html'>Well, nearly.&lt;br /&gt;&lt;br /&gt;You have seen me write how I think that Composite Primary Keys are above all else a BAD THING(tm).&lt;br /&gt;&lt;br /&gt;Well, I think they still are, as Primary Keys.&lt;br /&gt;&lt;br /&gt;However, there is no reason not to have a unique combination of keys in a table. This I have never had an issue with.&lt;br /&gt;&lt;br /&gt;So why?&lt;br /&gt;&lt;br /&gt;Well, look at our tagging example. We don't want to assign a tag to an entity_type twice in our frequency table, at this point we just want to increment the frequency count. Here we hit a snag.&lt;br /&gt;When we send info to our model, we don't know the id_tag_frequency, only the id_tag and the id_entity_type. This isn't a unique primary key.&lt;br /&gt;&lt;br /&gt;However, not to worry. We just need a constraint to ensure that the combination here is unique (tags will be assigned to multiple entity_types, and entity_types will have multiple tags).&lt;br /&gt;&lt;br /&gt;If we do this, then you can do a query lookup to return just one row.&lt;br /&gt;&lt;br /&gt;So why not make this the Primary Key?&lt;br /&gt;&lt;br /&gt;Well, if you do this, then you could lose out on simple coding like this&lt;br /&gt;&lt;br /&gt;if (unique a + b) and not primary key { fetch primary key from database or create new entry }&lt;br /&gt;&lt;br /&gt;but, more importantly, if later on you need to reference the table row in another table (which is the benefit of relational databases, isn't it?) you only need the single primary key put in the new table, instead of all the parts which make up the composite key. This maintains a DRY principle within the database, because, should you need to change part of the composite uniqueness in table 1, you don't need to do it in table 2 as well.&lt;br /&gt;&lt;br /&gt;So, never worry about needing a unique combination of fields, that is great, but always have a unique single field primary key on the row. This should help future proof your database when you need new tables, and will make it much easier to create new programs, as you have now given people the option of searching via the composite unique key, or the single primary key.&lt;br /&gt;&lt;br /&gt;An important aside to this. If you use an iscurrent boolean for your rows. It can be difficult to make this part of the unique composite key (because only 1 will be current), but do ensure that your code makes only 1 current for the entity it relates to. This will ensure that anyone querying the database will always be able to find the current one. I have had this problem working with a legacy database before, due to the fact that someone had put the constraint:&lt;br /&gt;&lt;br /&gt;entity + status + date = unique composite primary key&lt;br /&gt;&lt;br /&gt;because date (especially if stored as datetime) will generally give you a guaranteed unique combination, new rows were getting stored as iscurrent = 1, but all other rows for that entity and status were not getting set to iscurrent = 0. Whilst you could always use the following sql&lt;br /&gt;&lt;br /&gt;select entity, status, date from table order by date&lt;br /&gt;&lt;br /&gt;and select the one with the most recent date, you lose the benefit of just asking&lt;br /&gt;&lt;br /&gt;select entity, status, date from table where iscurrent = 1&lt;br /&gt;&lt;br /&gt;if that is all you want. And again, this future proofs development of new applications, as if the table is very large (and in bioinformatics data storage, they often get very exceptionally large) then developers will need to rely on the field names to determine how to use the table.&lt;br /&gt;&lt;br /&gt;So, if you have an iscurrent, keep it up to date, otherwise, DON'T USE IT.&lt;br /&gt;&lt;br /&gt;Rant over. Normal service resumes whenever we can determine what is normal anyway.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5197365873351122677?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5197365873351122677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5197365873351122677' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5197365873351122677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5197365873351122677'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/03/composite-primary-keys-yuuurrghhh.html' title='Composite Primary Keys - Yuuurrghhh!'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8988609941020148536</id><published>2008-03-18T05:28:00.000-07:00</published><updated>2008-03-18T05:37:44.509-07:00</updated><title type='text'>Define Existence</title><content type='html'>Not an existential question this one, but an interesting 'bug'.&lt;br /&gt;&lt;br /&gt;I have never used if (exists $var-&gt;{key}) {}&lt;br /&gt;&lt;br /&gt;I don't know why, but I just have never needed to. In fact, I can only just barely remember being told about it on a course.&lt;br /&gt;&lt;br /&gt;I have always used if (defined $var-&gt;{key}) {}&lt;br /&gt;&lt;br /&gt;What is the difference.&lt;br /&gt;&lt;br /&gt;To quote the Camel&lt;br /&gt;&lt;br /&gt;A variable can only be defined if it exists, however, the reverse is not necessarily true.&lt;br /&gt;&lt;br /&gt;So, if during an initialisation step, you do&lt;br /&gt;&lt;br /&gt;$model-&gt;{primary_key} = [result of some sql query for primary key]&lt;br /&gt;&lt;br /&gt;but there is no primary key result as the sql statement returned no results, then&lt;br /&gt;&lt;br /&gt;(exists $model-&gt;{primary_key}) == true&lt;br /&gt;&lt;br /&gt;however&lt;br /&gt;&lt;br /&gt;(defined $model-&gt;{primary_key}) == false&lt;br /&gt;&lt;br /&gt;as $model-&gt;{primary_key} == NULL&lt;br /&gt;&lt;br /&gt;Just something I found whilst trying to bug-fix why my save kept trying to go to update (and then croaking because of, you guessed it, no primary key) when it should have been going to create.&lt;br /&gt;&lt;br /&gt;I suppose I should have know this, but when I have always, in the past, used defined to tell me if it both exists and is defined, then it's an easy thing to miss.&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8988609941020148536?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8988609941020148536/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8988609941020148536' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8988609941020148536'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8988609941020148536'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/03/define-existence.html' title='Define Existence'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8481660219727325736</id><published>2008-03-17T07:28:00.000-07:00</published><updated>2008-03-17T07:29:24.672-07:00</updated><title type='text'>Are Clouds Real?</title><content type='html'>This is a bit of a summary of what we decided in the production of our database tables for tags.&lt;br /&gt;&lt;br /&gt;Very simply, we wanted to be able to tag different entities with tags. We also want to be able&lt;br /&gt;to track the tags used for an entity type (frequency, use for that entity type), so that we&lt;br /&gt;could lookup that, and offer suggestions based on the usage already of that tag - i.e. a cloud&lt;br /&gt;of tags for that entity_type (see scrumptious tagging for some application of that).&lt;br /&gt;&lt;br /&gt;So, the first is simply to have a table of tags&lt;br /&gt;&lt;br /&gt;id_tag   |   tag&lt;br /&gt;-------------------&lt;br /&gt;&lt;br /&gt;then we have a table of runs&lt;br /&gt;&lt;br /&gt;id_run   |   other info about run&lt;br /&gt;_________________________________&lt;br /&gt;&lt;br /&gt;and then a join table&lt;br /&gt;&lt;br /&gt;id_tag_run  |  id_run  |  id_tag&lt;br /&gt;_________________________________&lt;br /&gt;&lt;br /&gt;OK, so that is quite easy. But, we don't have any opportunity to look at any information on this,&lt;br /&gt;particularly our 'clouds' of tags.&lt;br /&gt;&lt;br /&gt;We looked at the possibility of a cloud table&lt;br /&gt;&lt;br /&gt;id_cloud | ids_of_tags_in_cloud &lt;br /&gt;&lt;br /&gt;But how do we know the entity (remember, we want to tag many different things)&lt;br /&gt;&lt;br /&gt;In this case, we have an entity_type table&lt;br /&gt;&lt;br /&gt;id_entity_type  |  description  |  iscurrent&lt;br /&gt;_______________________________________________&lt;br /&gt;&lt;br /&gt;From our models, we can get them to work out their entity_type, as our models are named&lt;br /&gt;xxx::model::&lt;entity_type&gt; where entity type is the name of the table. This is just a simple&lt;br /&gt;method. So, we make sure the description matches. This gives us an id_entity_type.&lt;br /&gt;&lt;br /&gt;id_cloud | ids_of_tags_in_cloud  |  id_entity_type&lt;br /&gt;____________________________________________________&lt;br /&gt;&lt;br /&gt;However, now we nothing of the frequencies the tags are used for an entity. We also need to&lt;br /&gt;keep appending the tag id's to a field, which could make it difficult to represent each&lt;br /&gt;individual tag in the model.&lt;br /&gt;&lt;br /&gt;At this point, our discussion came about to what clouds actually are. (at this point the&lt;br /&gt;science teacher in me said a collection of water vapour). We wondered about abstracting the&lt;br /&gt;cloud, and try to collect the frequencies of a tag against the entity type&lt;br /&gt;&lt;br /&gt;So, out with the cloud table (for now?) and in with the following&lt;br /&gt;&lt;br /&gt;id_tag_frequency | id_entity_type | id_tag | frequency&lt;br /&gt;_________________________________________________________&lt;br /&gt;&lt;br /&gt;This is looking good. We can store the frequencies that a tag has been used on an entity,&lt;br /&gt;and we also get an easy lookup for all tags that have been saved against an entity, which means&lt;br /&gt;that we can abstract our cloud with an sql statement like this&lt;br /&gt;&lt;br /&gt;cloud = select e.description as entity_type, t.tag as tag, frequency&lt;br /&gt;        from   entity_type e, tag_frequency tf, tag t&lt;br /&gt;        where  e.description = ?&lt;br /&gt;        and    e.id_entity_type = tf.id_entity_type&lt;br /&gt;        and    tf.id_tag = t.id_tag;&lt;br /&gt;&lt;br /&gt;So, do we need any more information. It is useful to know who first saved a tag for an entity, and a date.&lt;br /&gt;We can add this to our tag_&lt;entity_type&gt; join table&lt;br /&gt;&lt;br /&gt;id_tag_run  |  id_run  |  id_tag  |  id_user  | date&lt;br /&gt;_______________________________________________________&lt;br /&gt;&lt;br /&gt;So what we have &lt;br /&gt;&lt;br /&gt;tag table:&lt;br /&gt;id_tag | tag&lt;br /&gt;&lt;br /&gt;&lt;entity_type&gt;'s table:&lt;br /&gt;id_&lt;entity&gt; | info about entity&lt;br /&gt;&lt;br /&gt;tag_&lt;entity&gt; tables: (one for each entity)&lt;br /&gt;id_tag_&lt;entity&gt; | id_&lt;entity&gt; | id_tag | id_user | date&lt;br /&gt;&lt;br /&gt;entity_type dictionary table:&lt;br /&gt;id_entity_type | description | iscurrent(optional)&lt;br /&gt;&lt;br /&gt;tag_frequency:&lt;br /&gt;id_tag_frequency | id_entity_type | id_tag | frequency&lt;br /&gt;&lt;br /&gt;and clouds are purely abstract entities created from the data in the tables. We just need to use to code&lt;br /&gt;to (sql or within your program if another language) to calculate the frequency.&lt;br /&gt;&lt;br /&gt;So, in the Clearpress MVC framework (or in Rails) we create a model for each of tag* tables with the accessors&lt;br /&gt;for the table and hey presto, a setup for tagging any entities in our database. You could even tag a tag :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8481660219727325736?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8481660219727325736/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8481660219727325736' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8481660219727325736'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8481660219727325736'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/03/are-clouds-real.html' title='Are Clouds Real?'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-5414364241627861831</id><published>2008-03-13T09:10:00.000-07:00</published><updated>2008-03-13T09:11:38.574-07:00</updated><title type='text'>How many tests does it take to change a lightbulb? - the film</title><content type='html'>The film of my talk is now available - you can see it &lt;a href="http://www.foomongers.org.uk/videos/andybrown-testing.html"&gt;here:&lt;br /&gt;&lt;br /&gt;http://www.foomongers.org.uk/videos/andybrown-testing.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Cheers&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-5414364241627861831?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/5414364241627861831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=5414364241627861831' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5414364241627861831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/5414364241627861831'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/03/how-many-tests-does-it-take-to-change_13.html' title='How many tests does it take to change a lightbulb? - the film'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1266242944010417186</id><published>2008-03-12T09:33:00.001-07:00</published><updated>2008-03-12T09:37:16.889-07:00</updated><title type='text'>How many tests does it take to change a lightbulb?</title><content type='html'>&lt;div&gt;I gave this talk at lunchtime today during our Wednesday FooMongers meeting to probably the largest number of people who have attended. It was well received.&lt;br /&gt;&lt;br /&gt;It focused on why we should test, and how to go about testing, mostly test-driven development wise.&lt;br /&gt;&lt;br /&gt;Rob is putting a video up on YouTube tonight of the talk, so I'll post the link tomorrow.&lt;br /&gt;&lt;br /&gt;Andy&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;      From: &lt;a href="http://www.slideshare.net/setitesuk/"&gt;setitesuk&lt;/a&gt;, 8 minutes ago&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;      &lt;div style="width: 425px; text-align: left;" id="__ss_303611"&gt;&lt;object style="margin: 0px;" height="355" width="425"&gt;&lt;param name="movie" value="http://static.slideshare.net/swf/ssplayer2.swf?doc=test-presentation-1205336023530855-4"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=test-presentation-1205336023530855-4" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="355" width="425"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size: 11px; font-family: tahoma,arial; height: 26px; padding-top: 2px;"&gt;&lt;a href="http://www.slideshare.net/?src=embed"&gt;&lt;img src="http://static.slideshare.net/swf/logo_embd.png" style="border: 0px none ; margin-bottom: -5px;" alt="SlideShare" /&gt;&lt;/a&gt; | &lt;a href="http://www.slideshare.net/setitesuk/test-presentation-303611?src=embed" title="View 'Test Presentation' on SlideShare"&gt;View&lt;/a&gt; | &lt;a href="http://www.slideshare.net/upload?src=embed"&gt;Upload your own&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;      Slides for a presentation on testing given to foomongers at the EBI/Sanger Insts 2008/03/12&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;      &lt;a href="http://www.slideshare.net/setitesuk/test-presentation-303611"&gt;SlideShare Link&lt;/a&gt;&lt;br /&gt;     &lt;/div&gt;&lt;br /&gt;    &lt;img style="visibility: hidden; width: 0px; height: 0px;" src="http://counters.gigya.com/wildfire/CIMP/JnB*PTEyMDUzMzk2Mzc1MTcmcD*xMDE5MSZkPSZuPWJsb2dnZXI=.jpg" border="0" height="0" width="0" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1266242944010417186?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1266242944010417186/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1266242944010417186' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1266242944010417186'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1266242944010417186'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/03/how-many-tests-does-it-take-to-change.html' title='How many tests does it take to change a lightbulb?'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-186605237217511704</id><published>2008-03-11T03:42:00.000-07:00</published><updated>2008-03-11T03:44:28.918-07:00</updated><title type='text'>Scrumptious Tagging</title><content type='html'>So, we needed to look at, as well as writing full annotations for runs, the ability to tag runs (and other entities) with keywords. Being as we are writing a web-based application, this is very web2.0.&lt;br /&gt;&lt;br /&gt;For examples of tagging, look at de.licio.us, facebook or flickr.&lt;br /&gt;&lt;br /&gt;We wanted a style very similar to de.licio.us. However, with the range of autocomplete/autofill javascript out there in the open source community, I couldn't find anything that mimics the way de.licio.us works.&lt;br /&gt;&lt;br /&gt;In a nutshell:&lt;br /&gt;&lt;br /&gt;Display tags already given to a run (I'll use this, but replace with entity).&lt;br /&gt;Click to Add tags&lt;br /&gt;Change display to an entry field which has the tags in it, a cloud of all tags which have already been assigned to runs, which you can click to add or remove from the entry field, and they highlight if already in the entry field,&lt;br /&gt;and the clever bit - if the user starts typing into the field, it comes up with suggestions in another revealed field of tags from the cloud, sorted by the frequency the tags have been used, showing the ten most common for the current suggestions, changing as the user types more letters.&lt;br /&gt;&lt;br /&gt;(You may have noticed here that we are trying to get the users to essentially try to use the same tags again and again, rather than stick in a hyphen, etc)&lt;br /&gt;&lt;br /&gt;My boss already had a two function javascript to toggle the tags in and out from the cloud, and highlight them (with some css) if they are already in the text field, but I needed to do the rest. This I managed to some up in 4 more functions.&lt;br /&gt;&lt;br /&gt;With my bosses permission, I have included his 2 functions (crediting him of course) and I have put the javascript on sourceforge (http://sourceforge.net/projects/scrumptious/). You can just checkout the current svn trunk to obtain the code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-186605237217511704?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/186605237217511704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=186605237217511704' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/186605237217511704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/186605237217511704'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/03/scrumptious-tagging.html' title='Scrumptious Tagging'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-4235839291871031691</id><published>2008-02-25T05:52:00.000-08:00</published><updated>2008-02-25T06:05:47.232-08:00</updated><title type='text'>Mocking out to retrieve an email</title><content type='html'>So, we are using MIME::Lite to generate emails from our scripts/modules. The problem I came across in&lt;br /&gt;testing was:&lt;br /&gt;&lt;br /&gt;How do I test the $msg-&gt;send() to see that the email I have generated is what was expected?&lt;br /&gt;&lt;br /&gt;In Ruby on Rails, the test framework automatically allows for email to be appended to an array, which&lt;br /&gt;you can then test against.&lt;br /&gt;&lt;br /&gt;I want something like this in PERL!&lt;br /&gt;&lt;br /&gt;Well, it turns out that I can.&lt;br /&gt;&lt;br /&gt;After the module using MIME::Lite has been loaded, and my object created, do the following before running a method calling $msg-&gt;send(); ($model is an object created from xx.pm containing the method you want to call $msg-&gt;send() from)&lt;br /&gt;&lt;br /&gt;my $sub = sub {&lt;br /&gt;  my $msg = shift;&lt;br /&gt;  push @{$model-&gt;{emails}}, $msg-&gt;as_string;&lt;br /&gt;  return;&lt;br /&gt;};&lt;br /&gt;MIME::Lite-&gt;send('sub', $sub);&lt;br /&gt;&lt;br /&gt;Now, if you run the method containing $msg-&gt;send(), you can then access an array, where each element contain the MIME::Lite output of the email generated (and no emails would have been sent, thus not spamming people at random:).&lt;br /&gt;&lt;br /&gt;If you model method only creates a MIME::Lite object, and you are testing what a script might do with it, then an alternative $sub is&lt;br /&gt;&lt;br /&gt;my $sub = sub {&lt;br /&gt;  my $msg = shift;&lt;br /&gt;  return $msg-&gt;as_string();&lt;br /&gt;};&lt;br /&gt;MIME::Lite-&gt;send('sub', $sub);&lt;br /&gt;&lt;br /&gt;Then yous can happily do:&lt;br /&gt;&lt;br /&gt;my $email = $msg-&gt;send();&lt;br /&gt;&lt;br /&gt;and test $email to your hearts content, including sending it through MIME::Parser to obtain the headers, attachments, body etc.&lt;br /&gt;&lt;br /&gt;I hope this may be of help for someone.&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-4235839291871031691?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/4235839291871031691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=4235839291871031691' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4235839291871031691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/4235839291871031691'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/02/mocking-out-to-retrieve-email.html' title='Mocking out to retrieve an email'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-7652082298715254880</id><published>2008-02-08T04:39:00.001-08:00</published><updated>2008-02-08T05:06:05.795-08:00</updated><title type='text'>Contacting Authors and creating SQL</title><content type='html'>So yesterday I was having a problem. I wanted to send an email using MIME::Lite from a PERL&lt;br /&gt;script,&lt;br /&gt;but such that auto responders wouldn't send back.&lt;br /&gt;&lt;br /&gt;I trawled through the RFC to see what things they suggested. It is a lot of boring documentation, but I found 3 useful things&lt;br /&gt;&lt;br /&gt;1) Subjects should have autoreply in them&lt;br /&gt;2) The auto-submitted header should be set&lt;br /&gt;3) Auto responders should ignore anything that comes with a Precendence: list header&lt;br /&gt;&lt;br /&gt;The first two I could programmatically cope with (if (x) { ignore in some way }).&lt;br /&gt;&lt;br /&gt;The third needed me to change my outgoing posts. So, off to the documentation for MIME::Lite to find how to set the Precendence header.&lt;br /&gt;&lt;br /&gt;Unfortunately, I couldn't find this, and just using MIME::Lite-&gt;new(Precendence =&gt; 'list'); wasn't working. Give up I did not! I mailed the very nice man who wrote MIME::Lite as to whether a) it did it, or b) if it would be put in a future release.&lt;br /&gt;&lt;br /&gt;Bingo, he mailed me back. Simply do MIME::Lite-&gt;new('Precendence:' =&gt; 'list'); Hey presto,it worked - brilliant. (My biggest thanks to eryq for his quick informative response!)&lt;br /&gt;&lt;br /&gt;In fact, he mentioned you can set any of the non-standard MIME::Lite setting headers in this way. A very useful thing to know indeed.&lt;br /&gt;&lt;br /&gt;So, one quick change and a test later, and we have this in svn!&lt;br /&gt;&lt;br /&gt;On to the next issue - generating advanced queries in SQL programmatically.&lt;br /&gt;&lt;br /&gt;So, I have been set for this sprint to write an advanced query page. I have taken this slowly and steadily, as I want to get it right! The first task is just to get it self generating some multi-table queries, that also use join tables, performing a simple one statement select.&lt;br /&gt;&lt;br /&gt;Easy or hard? That is the question.&lt;br /&gt;&lt;br /&gt;The way I have looked at it is that I, the wonderful software guru, know the database tables. After all, why wouldn't I? In a search model, I create 4 new methods.&lt;br /&gt;&lt;br /&gt;1) advanced_search - this is to generate the SQL, perform it and return the results&lt;br /&gt;2) search_for - this returns a hash of all the required fields and corresponding tables based on what the user has requested.&lt;br /&gt;3) search_conditions - this returns a hash of what fields are being selected on, and corresponding tables for those. Here, I can write some additional extra WHERE statements to group together more than one field lookup if needed (which for loader was)&lt;br /&gt;4) table_links - this returns a hash of all tables that have foreign keys, and the table for that foreign key (all keys have the format "id_&lt;table_name&gt;").&lt;br /&gt;&lt;br /&gt;With this and some looped code, I have managed to generate some rather complex single SELECT statements in the format of&lt;br /&gt;&lt;br /&gt;SELECT DISTINCT tablea.id_tablea, tablea.name, tableb.id_tableb, tablec.comments&lt;br /&gt;FROM tablea, tableb, tablec, tabled&lt;br /&gt;WHERE tabela.position IN (x,y,z)&lt;br /&gt;AND  tablea.id_tabled = tabled.id_tabled&lt;br /&gt;AND tabled.id_tablec = tablec.id_tablec&lt;br /&gt;AND tablec.comment LIKE '%good%'&lt;br /&gt;AND tablec.id_tableb = tableb.id_tableb&lt;br /&gt;&lt;br /&gt;I have chosen to put DISTINCT in as a default, as sometimes, when x,y,z are all chosen, it can return multiple rows that are the same.&lt;br /&gt;&lt;br /&gt;This seems to be going quite fast, however my ever faithful friend Test::Perl::Critic says that method advanced_search has a high complexity score, so it definitely needs a bit of refactoring.&lt;br /&gt;&lt;br /&gt;However, it seems to be going fairly well. So, refactoring can wait until next week.&lt;br /&gt;&lt;br /&gt;By for now&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-7652082298715254880?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/7652082298715254880/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=7652082298715254880' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7652082298715254880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/7652082298715254880'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/02/contacting-authors-and-creating-sql.html' title='Contacting Authors and creating SQL'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-8691803703046068170</id><published>2008-02-05T08:39:00.000-08:00</published><updated>2008-02-05T08:56:37.579-08:00</updated><title type='text'>It's been a (rather busy) while</title><content type='html'>So, a little while since I last posted. However, much has been done.&lt;br /&gt;&lt;br /&gt;We had another release last week, some of which has needed some tweaking since for the next release. GD doesn't help make pages go quickly! However, a bit of ajaxing later and we have a page which loads quickly, but allows the graphs to be opened on the page afterwards.&lt;br /&gt;&lt;br /&gt;Nice!&lt;br /&gt;&lt;br /&gt;Also, I have been experimenting with creating a script ot parse a MIME format email, to then store the body as an annotation and get the id from the subject.&lt;br /&gt;&lt;br /&gt;Fairly easy. I am using MIME::Lite and MIME::Parser to do the hard work, and the api for the application to contact the main models, which in turn handle saving the annotation. You got to love well constructed and documented APIs.&lt;br /&gt;&lt;br /&gt;I have written extensive tests over the code for the main part. I wrote most within a module, so that this could be tested easily and reused, with a small script to handle receiving the email, and if anything croaks, doing something with the croak. (An email back, unless it is an Out of Office reply - why do people need to use them? I think we should all start immediately spamming anyone who uses them so an end such that perhaps they won't in future*).&lt;br /&gt;&lt;br /&gt;Anyway, I've done a fair bit over the past couple of weeks towards new features, or bettering existing ones. The learning curve keeps going up. I am now also looking at trying to dynamically create SQL in order to be able to do advanced searching. The problem, however, is how to store the foreign key pattern from across the database. I need to look into if the models can provide the information they possess, or if I need to store it within a hash (as current).&lt;br /&gt;&lt;br /&gt;Must dash for now. More soon.&lt;br /&gt;&lt;br /&gt;*This is not a serious suggestion. I do not condone spamming in any way. A polite request should be used instead.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-8691803703046068170?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/8691803703046068170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=8691803703046068170' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8691803703046068170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/8691803703046068170'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/02/its-been-rather-busy-while.html' title='It&apos;s been a (rather busy) while'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1158653486009791669.post-1596525303517472321</id><published>2008-01-23T00:58:00.000-08:00</published><updated>2008-01-23T01:19:21.636-08:00</updated><title type='text'>Adding a graph</title><content type='html'>So, Hands up all those that have 'use(d) GD;'?&lt;br /&gt;&lt;br /&gt;Now that includes me!&lt;br /&gt;&lt;br /&gt;So, I needed to show a table of numbers in a bar chart format. Quite simply, I had an arrayref of hash refs, each hash ref using keys date and percentage.&lt;br /&gt;&lt;br /&gt;First off, the GD plot needs it as an arrayref containing 2 arraysrefs, the first of the x-axis (in this case the date) and the second of the percentages. ($cmap is an object of in-house stuff to work out appropriate colours to use).&lt;br /&gt;&lt;br /&gt;Easy&lt;br /&gt;&lt;br /&gt;  my $plot = [[],[]];&lt;br /&gt;  foreach my $href (@{$arrayref}) {&lt;br /&gt;    push $plot-&gt;[0], $href-&gt;{date};&lt;br /&gt;    push $plot-&gt;[1], $href-&gt;{percentage};&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  my $graph                = GD::Graph::bars-&gt;new(1000,400);&lt;br /&gt;  $graph-&gt;set(&lt;br /&gt;              'dclrs'        =&gt; [&lt;br /&gt;                                 map {&lt;br /&gt;                                   GD::Graph::colour::add_colour(q(#).$cmap-&gt;hex_by_name($_));&lt;br /&gt;                                 } (qw(red purple orange blue green yellow magenta cyan))],&lt;br /&gt;              'fgclr'        =&gt; 'black',&lt;br /&gt;              'boxclr'       =&gt; 'white',&lt;br /&gt;              'accentclr'    =&gt; 'black',&lt;br /&gt;              'shadowclr'    =&gt; 'black',&lt;br /&gt;              'y_long_ticks' =&gt; 1,&lt;br /&gt;              'x_label'      =&gt; 'Date',&lt;br /&gt;              'y_label'      =&gt; 'Percentage Activity'&lt;br /&gt;             );&lt;br /&gt;  return $graph-&gt;plot($plot)-&gt;png();&lt;br /&gt;&lt;br /&gt;See, I said it was easy. :)&lt;br /&gt;&lt;br /&gt;So, of course now I have said that, everyone wants their data in a graph format. Luckily, GD provides lots of graph formats. I just wish that we could submit to them as array of hrefs, rather than array of arrays, which I personlly think is&lt;br /&gt;&lt;br /&gt;1) messier&lt;br /&gt;2) relies on the end user to know the order the arrays should be in&lt;br /&gt;3) Sends no additional information for legends (should this be needed), whereas the key can be the legend.&lt;br /&gt;&lt;br /&gt;Oh well. I shouldn't complain really. I didn't have to write it. Don't you just love the CPAN!&lt;br /&gt;&lt;br /&gt;(GD and it's suite of components by Lincoln D. Stein and searchable on CPAN. There are lots of additional stuff to link into Template::Toolkit, etc written by others. Have a look!)&lt;br /&gt;&lt;br /&gt;FooMongers today. Don't yet know what we'll discuss.&lt;br /&gt;&lt;br /&gt;Andy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1158653486009791669-1596525303517472321?l=vampiresoftware.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://vampiresoftware.blogspot.com/feeds/1596525303517472321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1158653486009791669&amp;postID=1596525303517472321' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1596525303517472321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1158653486009791669/posts/default/1596525303517472321'/><link rel='alternate' type='text/html' href='http://vampiresoftware.blogspot.com/2008/01/adding-graph.html' title='Adding a graph'/><author><name>Andy Brown - SetitesUK</name><uri>http://www.blogger.com/profile/05926250964970803769</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='14' height='32' src='http://3.bp.blogspot.com/_ZSX52mUFcNs/SJ6WQkeIfyI/AAAAAAAAAAU/hS9X4z5othI/s1600-R/badge.png'/></author><thr:total>0</thr:total></entry></feed>
