<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	>

<channel>
	<title>Oliver Searle-Barnes</title>
	<atom:link href="http://www.opsb.co.uk/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.opsb.co.uk</link>
	<description>Agile web craftsman</description>
	<pubDate>Mon, 26 Apr 2010 06:36:34 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Introducing Butler-IO</title>
		<link>http://www.opsb.co.uk/?p=176</link>
		<comments>http://www.opsb.co.uk/?p=176#comments</comments>
		<pubDate>Mon, 01 Feb 2010 12:52:54 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=176</guid>
		<description><![CDATA[Some of those IO tasks can simply be the height of tedium. Why not ask the butler to take care of it? He&#8217;s rather clever don&#8217;t you know, he even understands a thing or two about that apache VFS.
Butler will fetch

byte []
String
String in utf8
InputStream
File

from

VFS locations - allows loading from any of; local files, http, https, [...]]]></description>
			<content:encoded><![CDATA[<p>Some of those IO tasks can simply be the height of tedium. Why not ask the butler to take care of it? He&#8217;s rather clever don&#8217;t you know, he even understands a thing or two about that <a href="http://commons.apache.org/vfs/" title="apache VFS">apache VFS</a>.</p>
<p>Butler will fetch</p>
<ul>
<li>byte []</li>
<li>String</li>
<li>String in utf8</li>
<li>InputStream</li>
<li>File</li>
</ul>
<p>from</p>
<ul>
<li>VFS locations - allows loading from any of; local files, http, https, ftp, sftp, temporary files, zip, jar, tar, gzip, bzip2, res, ram, mime. Take a look at <a href="http://commons.apache.org/vfs/filesystems.html" title="VFS examples">some examples</a>.</li>
<li>InputStreams</li>
<li>Files</li>
<li>File in same package as a Class</li>
</ul>
<h2>Installation</h2>
<p>Just update your maven settings with</p>
<pre>
&lt;repository&gt;
    &lt;id&gt;opsbreleases&lt;/id&gt;
    &lt;name&gt;opsb-releases&lt;/name&gt;
    &lt;url&gt;http://opsb.co.uk/nexus/content/repositories/releases/&lt;/url&gt;
&lt;/repository&gt;
</pre>
<p>and</p>
<pre>
&lt;dependency&gt;
   &lt;groupId&gt;uk.co.opsb&lt;/groupId&gt;
   &lt;artifactId&gt;butler-io&lt;/artifactId&gt;
   &lt;version&gt;0.3&lt;/version&gt;
&lt;/dependency&gt;
</pre>
<h2>Usage</h2>
<p>First let&#8217;s call for the butler:</p>
<pre>import static uk.co.opsb.butler.ButlerIO.*;</pre>
<p>Now let&#8217;s put him to task</p>
<h3>Fetching text</h3>
<pre>String fromClasspath          = textFrom( "res:articles/steve_jobs.txt" );
String fromUtf8File           = utf8From( "file:///path/to/steve_jobs.txt" );
String fromUtf8FileOnWindows  = utf8From( "file:///c:/path/to/steve_jobs.txt" );
String fromInputStream        = textFrom( inputStream );
String overHttpsUsingVfs      = textFrom( "https://username:password@domain_name.com/article.txt" );
String fromFtpZipUsingVfs     = textFrom( "zip:ftp://username:password@domain_name.com/file.txt.zip" );
String fromFileNextToClass    = textFrom( "name_of_file_in_same_package_as", YourClass.class );
</pre>
<h3>Fetching bytes</h3>
<pre>byte [] fromClasspath          = bytesFrom( "res:articles/steve_jobs.txt" );
byte [] fromUtf8File           = bytesFrom( "file:///path/to/steve_jobs.txt" );
byte [] fromUtf8FileOnWindows  = bytesFrom( "file:///c:/path/to/steve_jobs.txt" );
byte [] fromInputStream        = bytesFrom( inputStream );
byte [] overHttpUsingVfs       = bytesFrom( "https://domain_name.com/article.txt" );
byte [] fromSftpGzipUsingVfs   = bytesFrom( "gz:sftp://username:password@domain_name.com/file.txt.gz" );
byte [] fromFileNextToClass    = bytesFrom( "name_of_file_in_same_package_as", YourClass.class );
</pre>
<h3>Fetching properties</h3>
<pre>Properties fromClasspath          = propertiesFrom( "res:articles/steve_jobs.txt" );
Properties fromUtf8File           = propertiesFrom( "file:///path/to/steve_jobs.txt" );
Properties fromUtf8FileOnWindows  = propertiesFrom( "file:///c:/path/to/steve_jobs.txt" );
Properties fromInputStream        = propertiesFrom( inputStream );
Properties overHttpsUsingVfs      = propertiesFrom( "https://username:password@domain_name.com/article.txt" );
Properties fromJarUsingVfs        = propertiesFrom( "jar://username:password@domain_name.com/outer.jar!inner/file.txt" );
Properties fromFileNextToClass    = propertiesFrom( "name_of_file_in_same_package_as", YourClass.class );
</pre>
<h3>Opening an InputStream</h3>
<pre>InputStream fromClasspath          = inputStreamFrom( "res:articles/steve_jobs.txt" );
InputStream fromUtf8File           = inputStreamFrom( "file:///path/to/steve_jobs.txt" );
InputStream fromUtf8FileOnWindows  = inputStreamFrom( "file:///c:/path/to/steve_jobs.txt" );
InputStream overHttpsUsingVfs      = inputStreamFrom( "https://username:password@domain_name.com/article.txt" );
InputStream fromFtpZipUsingVfs     = inputStreamFrom( "zip:ftp://username:password@domain_name.com/file.txt.zip" );
InputStream fromFileNextToClass    = inputStreamFrom( "name_of_file_in_same_package_as", YourClass.class );
</pre>
<h3>Getting a reference to a File</h3>
<pre>File fromClasspath          = fileFrom( "res:path/to/file" );
File fromFileNextToClass    = fileFrom( "file_name", YourClass.class );
</pre>
<h2>Aliases</h2>
<p>I often need to fetch articles and reports from the same places. I don&#8217;t know about you but I rather like my butler to show a little initiative.</p>
<pre>#Inside a file at {classpath}/butler_aliases.properties
articles\:=res://path/to/articles    # remember to escape any colons you use before the equals
reports\:=res://path/to/reports
</pre>
<p>Now when I ask for articles and reports he&#8217;ll know just what to do</p>
<pre>String article = textFrom( "articles:steve_jobs.txt" ); // =&gt; res:path/to/articles/steve_jobs.txt
String report  = textFrom( "reports:q4_figures.txt" ); // =&gt; res:path/to/reports/q4_figures.txt
</pre>
<p>Marvellous. He can do better than that though, how about we use a convention</p>
<pre>^(\\w*)\:=res:uk/co/opsb/%s/

String article = textFrom( "articles:steve_jobs.txt" ); // =&gt; res:uk/co/opsb/articles/steve_jobs.txt
String report  = textFrom( "reports:q4_figures.txt" ); // =&gt; res:uk/co/opsb/reports/q4_figures.txt
</pre>
<p>What a clever chap. He&#8217;s used the regex to capture articles/reports and then String.format to merge them in.</p>
<p>Fancy a tinker? Fork it at http://github.com/opsb/butler-io</p>
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=176</wfw:commentRss>
		</item>
		<item>
		<title>Follow table links in cucumber</title>
		<link>http://www.opsb.co.uk/?p=165</link>
		<comments>http://www.opsb.co.uk/?p=165#comments</comments>
		<pubDate>Thu, 03 Dec 2009 23:55:32 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[cucumber]]></category>

		<category><![CDATA[rails]]></category>

		<category><![CDATA[ruby]]></category>

		<category><![CDATA[selectors]]></category>

		<category><![CDATA[table links]]></category>

		<category><![CDATA[webrat]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=165</guid>
		<description><![CDATA[On a page you&#8217;ll quite often have a table like the following


Book
Author
&#160;



Harry Potter and half blood prince
J.K. Rowling
Delete


The Cuckoo&#8217;s Egg: Tracking a Spy Through the Maze of Computer Espionage
Cliff Stoll
Delete



In your cucumber steps you want to say

Given I am on the books page
When I follow the "Delete" link for "Harry Potter and half blood prince"
Then [...]]]></description>
			<content:encoded><![CDATA[<p>On a page you&#8217;ll quite often have a table like the following</p>
<table>
<thead>
<th>Book</th>
<th>Author</th>
<th>&nbsp;</th>
</thead>
<tbody>
<tr>
<td>Harry Potter and half blood prince</td>
<td>J.K. Rowling</td>
<td><a href="#">Delete</a></td>
</tr>
<tr>
<td>The Cuckoo&#8217;s Egg: Tracking a Spy Through the Maze of Computer Espionage</td>
<td>Cliff Stoll</td>
<td><a href="#">Delete</a></td>
</tr>
</tbody>
</table>
<p>In your cucumber steps you want to say</p>
<pre>
Given I am on the books page
When I follow the "Delete" link for "Harry Potter and half blood prince"
Then ...
</pre>
<p>Out of the box webrat doesn&#8217;t have a step that will allow you to do this. Let&#8217;s create one that will do the job</p>
<pre>
When /^I follow the "([^\"]*)" link for "([^\"]*)"$/ do |link, cell_value|
  within "//*[.//text()='#{cell_value}' and .//a[text()='#{link}']]" do |scope|
   scope.click_link link
 end
end
</pre>
<p>This works great, it even works outside tables, the step will work so long as the text and link have a common parent in the dom. Just one problem, for this step to work webrat needs to understand xpath selectors. Here&#8217;s a little monkey patch that will get it working in webrat 0.6.0.</p>
<pre>
#lib/webrat_extensions.rb
module Webrat
  class Scope
    protected
      def scoped_dom
        begin
          @scope.dom.css(@selector).first
        rescue Nokogiri::CSS::SyntaxError, Nokogiri::XML::XPath::SyntaxError =&gt; e
          begin
            @scope.dom.xpath(@selector).first
          rescue Nokogiri::XML::XPath::SyntaxError
            raise e
          end
        end
      end
  end
end
</pre>
<p>Once you&#8217;ve added the patch our new webrat step will work. As a bonus you also get to use xpath selectors anywhere you use css selectors.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=165</wfw:commentRss>
		</item>
		<item>
		<title>rcov for cucumber and shoulda</title>
		<link>http://www.opsb.co.uk/?p=163</link>
		<comments>http://www.opsb.co.uk/?p=163#comments</comments>
		<pubDate>Thu, 03 Dec 2009 09:11:58 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=163</guid>
		<description><![CDATA[There seems to be a lot of bad info out there about this. It&#8217;s really quite simple, you just have to make use of the built in tasks provided by cucumber and rcov. The following task definitions will generate coverage reports for cucumber features and rails tests in coverage.features and converage.tests respectively. You&#8217;ll also get [...]]]></description>
			<content:encoded><![CDATA[<p>There seems to be a lot of bad info out there about this. It&#8217;s really quite simple, you just have to make use of the built in tasks provided by cucumber and rcov. The following task definitions will generate coverage reports for cucumber features and rails tests in coverage.features and converage.tests respectively. You&#8217;ll also get the overviews for the same reports at the command line when you run the tasks.</p>
<pre>
require 'cucumber/rake/task'
require 'rcov/rcovtask'

namespace :rcov do

  rcov_opts = ['-T','--exclude /Library/Ruby/Site/*,.rip/*,gems/*,rcov*,features/step_definitions/webrat_steps.rb']

  desc 'Measures cucumber coverage'
  Cucumber::Rake::Task.new(:features) do |t|
    t.rcov = true
    t.rcov_opts = rcov_opts
    t.rcov_opts &lt;&lt; '-o coverage.features'
  end

  desc 'Measures shoulda coverage'
  Rcov::RcovTask.new(:tests) do |t|
    t.libs &lt;&lt; 'test'
    t.test_files = FileList['test/unit/*_test.rb','test/functional/*_test.rb','test/unit/helpers/*_test.rb']
    t.rcov_opts = rcov_opts
    t.output_dir = "coverage.tests"
  end

  desc 'Measures all coverage'
  task :all do
    ["features", "tests"].each{ |task| Rake::Task["rcov:#{task}"].invoke }
  end
end
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=163</wfw:commentRss>
		</item>
		<item>
		<title>Validates uniqueness of multiple columns</title>
		<link>http://www.opsb.co.uk/?p=149</link>
		<comments>http://www.opsb.co.uk/?p=149#comments</comments>
		<pubDate>Fri, 27 Nov 2009 18:30:21 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[active_record]]></category>

		<category><![CDATA[rails]]></category>

		<category><![CDATA[ruby]]></category>

		<category><![CDATA[validation]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=149</guid>
		<description><![CDATA[We came across a case where we wanted to validate that the combination of first and last names for a new person was unique. Add the following snippet

#config/initializers/validators.rb
ActiveRecord::Base.class_eval do
  def self.validates_uniqueness_of_combined(*attr_names)
    options = attr_names.extract_options!.symbolize_keys
    attr_names = attr_names.flatten

    send(validation_method(options[:on] &#124;&#124; :save), options) do &#124;record&#124;
   [...]]]></description>
			<content:encoded><![CDATA[<p>We came across a case where we wanted to validate that the combination of first and last names for a new person was unique. Add the following snippet</p>
<pre>
#config/initializers/validators.rb
ActiveRecord::Base.class_eval do
  def self.validates_uniqueness_of_combined(*attr_names)
    options = attr_names.extract_options!.symbolize_keys
    attr_names = attr_names.flatten

    send(validation_method(options[:on] || :save), options) do |record|
      sql             = attr_names.map{ |attr_name| "UPPER(#{attr_name}) = ?"}.join(" AND ")
      values       = attr_names.map{ |a| record.send(a) }.map{ |v| v &amp;&amp; v.upcase }
      conditions = [sql, *values]

      db_record = record.class.find(:first, :conditions =&gt; conditions)
      if db_record &amp;&amp; db_record != record
        default_message = "#{attr_names.map{ |attr_name| record.send attr_name }.join(' ')} has already been added"
        record.errors.add_to_base(options["message"] || default_message)
      end
    end
  end
end
</pre>
<p>to your initializers then you can do</p>
<pre>
class Person &lt; ActiveRecord::Base
  validates_uniqueness_of_combined :first_name, :last_name
end
</pre>
<p>Note that this does a case insensitive match on the column names. If you want case sensitive you should replace</p>
<pre>
      sql             = attr_names.map{ |attr_name| "UPPER(#{attr_name}) = ?"}.join(" AND ")
      values        = attr_names.map{ |a| record.send(a) }.map{ |v| v &amp;&amp; v.upcase }
</pre>
<p>with</p>
<pre>
      sql             = attr_names.map{ |attr_name| "#{attr_name} = ?"}.join(" AND ")
      values        = attr_names.map{ |a| record.send(a) }
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=149</wfw:commentRss>
		</item>
		<item>
		<title>Put your team back together with some promiscuous pairing</title>
		<link>http://www.opsb.co.uk/?p=122</link>
		<comments>http://www.opsb.co.uk/?p=122#comments</comments>
		<pubDate>Sun, 22 Nov 2009 19:23:27 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=122</guid>
		<description><![CDATA[It&#8217;s happened so many times. The big release is coming up, we realise there isn&#8217;t enough time to get all the necessary features in, the business demands that we drop quality to hit the deadline. We reluctantly comply and&#8230; we hit the deadline. The business and more importantly our customers are delighted. Some big ticket [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s happened so many times. The big release is coming up, we realise there isn&#8217;t enough time to get all the necessary features in, the business demands that we drop quality to hit the deadline. We reluctantly comply and&#8230; we hit the deadline. The business and more importantly our customers are delighted. Some big ticket customers that were going to cancel contracts with us don&#8217;t. Kinda difficult to argue with that.</p>
<h2>Problems</h2>
<p>Of course there was a cost and now we, the development team were feeling it, bad. The project is full of broken windows, there&#8217;s buildings on fire and crack dealers on the corner. </p>
<ul>
<li>inconsistent and poor quality</li>
<li>conflicting views</li>
<li>ownership - people were protective of code</li>
<li>unhappy developers</li>
<li>poor team mentality</li>
<li>low velocity</li>
</ul>
<h2>The Solution</h2>
<p>One night I come across a paper that I&#8217;ve read once before, <a href="http://mitchlacey.com/docs/XR4PromiscuousPairingandBeginnersMind.pdf">promiscous pairing and beginners mind</a>. While I found it interesting the first time I didn&#8217;t persue it. As I reread I started to wonder if perhaps it might provide us with a way to put the team back together and to bring the project back to a state of pride. So how&#8217;s it work?</p>
<p>Promiscuous pairing - pair up, after an hour and a half, the developers in the driving seat moves on to the next story. Back seat developers move into the driving seat and are joined by a new developer, etc.</p>
<h2>Result</h2>
<p>The effect was noticeable the first day. After a week we had a team with</p>
<ul>
<li>Higher energy</li>
<li>Higher velocity</li>
<li>Higher quality</li>
<li>Happy developers</li>
<li>More fun</li>
</ul>
<h2>Why</h2>
<ul>
<li>Every time a switch takes place the developer staying on a story must explain the story to the new developer. This is what creates the &#8220;beginners mind&#8221;, it ensures that focus is always on the requirements of the story.</li>
<li>Juniors received continuous guidance allowing them to produce same quality of code as seniors.</li>
<li>Don&#8217;t get stuck in rabbit holes, those oh so clever little ideas that turn into sprawling epics get nipped in the bud</li>
<li>All design decisions are discussed and agreed upon leading to consensus across the team</li>
<li>Discussing all problems in english encourages ubiquitous language and semantic match with business.</li>
<li>Remain focused, never get stuck because other person always has ideas, get completely stuck? - every hour and a half you get a fresh pair of eyes on the problem.</li>
<li>Each team member has different strengths, rotation means all strengths are applied to all tasks</li>
<li>Knowledge spread - every key shortcut, refactoring trick, test technique, domain modelling principle shared between all team members.</li>
<li>Risk reduction - all members of team are up to speed with all technologies and features in project so don&#8217;t need to worry about holidays/illness/turnover.</li>
<li>Stress reduction - no one is responsible for delivering a feature, focus is on how best to move story/bug along in next hour and a half.</li>
<li>No resentment about features being implemented poorly</li>
<li>Team mentality - everyone did everything</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=122</wfw:commentRss>
		</item>
		<item>
		<title>Getting started with ruby DBI and mysql</title>
		<link>http://www.opsb.co.uk/?p=88</link>
		<comments>http://www.opsb.co.uk/?p=88#comments</comments>
		<pubDate>Fri, 25 Sep 2009 14:30:03 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=88</guid>
		<description><![CDATA[DBI is a database api based on Perls&#8217; DBI. It&#8217;s great for those occasions where you want to interact with a database in a script, or perhaps when you have a really lightweight app that doesn&#8217;t need an ORM framework. 
Install
Ok, it took quite a bit of searching around but this is the magic recipe [...]]]></description>
			<content:encoded><![CDATA[<p>DBI is a database api based on Perls&#8217; DBI. It&#8217;s great for those occasions where you want to interact with a database in a script, or perhaps when you have a really lightweight app that doesn&#8217;t need an ORM framework. </p>
<h2>Install</h2>
<p>Ok, it took quite a bit of searching around but this is the magic recipe that will get everything you need installed on ubuntu.</p>
<h3>Mysql on ubuntu</h3>
<pre>
sudo apt-get install mysql-client
sudo apt-get install libmysqlclient15-dev
</pre>
<h3>Gems</h3>
<pre>
sudo gem install dbi
sudo gem install mysql
sudo gem install dbd-mysql
</pre>
<h2>Usage</h2>
<p>First of all let&#8217;s load up all the dependencies</p>
<pre>
require 'dbi'
require 'mysql'
require 'dbd-mysql'
</pre>
<p>Now let&#8217;s make a connection to a db, obviously replacing the schema, hostname etc. with your own.</p>
<pre>
dbh = DBI.connect('DBI:Mysql:schema:hostname', 'username', 'password')
</pre>
<p>For the examples let&#8217;s assume that we have a table, people, that contains</p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>jim</td>
</tr>
<tr>
<td>2</td>
<td>paul</td>
</tr>
</tbody>
</table>
<h3>Select</h3>
<p>DBI provides two select methods, select_all and select_one. Each will return rows or a row that contain values that can be indexed using the name of a column or the index of the column. DBI will map the values in your columns to ruby classes automatically.</p>
<pre>
row = dbh.select_one("SELECT * FROM people;")
puts row[:id]                 # 1
puts row[:id].class         # Fixnum
puts row[0]                   # 1
puts row[0].class           # Fixnum
puts row[:name]            # jim
puts row[:name].class    # String

rows = dbh.select_all(statement)
puts rows[0][:id]       # 1
puts rows[0][:name]  # jim
puts rows[1][:id]       # 2
puts rows[2][:name]  # paul
</pre>
<h3>Insert</h3>
<p>For operations that update the db you can use the method &#8216;do&#8217;. Note that we&#8217;ve used the ? to indicate where our values will go in the query and then supplied them as arguments at the end, this is to avoid <a href="http://en.wikipedia.org/wiki/SQL_injection">SQL injection attacks</a>.</p>
<pre>
dbh.do("INSERT INTO people (id, name) VALUES (?,?)", nil, 'bob')
</pre>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>jim</td>
</tr>
<tr>
<td>2</td>
<td>paul</td>
</tr>
<tr>
<td>3</td>
<td>bob</td>
</tr>
</tbody>
</table>
<h3>Update</h3>
<p>We can use &#8216;do&#8217; for updates as well</p>
<pre>
dbh.do("UPDATE people SET name=? WHERE id=?", "mark", 3)
</pre>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>jim</td>
</tr>
<tr>
<td>2</td>
<td>paul</td>
</tr>
<tr>
<td>3</td>
<td>mark</td>
</tr>
</tbody>
</table>
<h3>Delete</h3>
<p>You get the idea&#8230;</p>
<p>So there you go, a whirl wind tour to get you up and running. For more in depth instructions I recommend this <a href="http://www.kitebird.com/articles/ruby-dbi.html">tutorial</a>. The <a href="http://ruby-dbi.rubyforge.org">ruby DBI homepage</a> has extra information including the <a href="http://ruby-dbi.rubyforge.org/rdoc/index.html">rdocs</a>. Finally, this <a href="http://github.com/erikh/ruby-dbi/">github version</a> of the project also has some good information in its&#8217; readme, particularly regarding different db drivers.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=88</wfw:commentRss>
		</item>
		<item>
		<title>Installing mysql gem on ubuntu</title>
		<link>http://www.opsb.co.uk/?p=77</link>
		<comments>http://www.opsb.co.uk/?p=77#comments</comments>
		<pubDate>Fri, 25 Sep 2009 11:25:29 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=77</guid>
		<description><![CDATA[Install mysql

apt-cache search mysql-client

Choose one and install (sudo apt-get install xxx)
Install dev headers

apt-cache search libmysql

Choose one that ends in dev and install
Install gem

sudo gem install mysql

]]></description>
			<content:encoded><![CDATA[<h3>Install mysql</h3>
<pre>
apt-cache search mysql-client
</pre>
<p>Choose one and install (sudo apt-get install xxx)</p>
<h3>Install dev headers</h3>
<pre>
apt-cache search libmysql
</pre>
<p>Choose one that ends in dev and install</p>
<h3>Install gem</h3>
<pre>
sudo gem install mysql
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=77</wfw:commentRss>
		</item>
		<item>
		<title>#clapforwhy</title>
		<link>http://www.opsb.co.uk/?p=76</link>
		<comments>http://www.opsb.co.uk/?p=76#comments</comments>
		<pubDate>Thu, 20 Aug 2009 21:47:51 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=76</guid>
		<description><![CDATA[
]]></description>
			<content:encoded><![CDATA[
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=76</wfw:commentRss>
		</item>
		<item>
		<title>Useful regular expressions with egrep</title>
		<link>http://www.opsb.co.uk/?p=75</link>
		<comments>http://www.opsb.co.uk/?p=75#comments</comments>
		<pubDate>Mon, 17 Aug 2009 10:08:28 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[regex bash]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=75</guid>
		<description><![CDATA[First word: egrep -o &#8220;^\w*\b&#8221;
]]></description>
			<content:encoded><![CDATA[<p>First word: egrep -o &#8220;^\w*\b&#8221;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=75</wfw:commentRss>
		</item>
		<item>
		<title>Pimp my git</title>
		<link>http://www.opsb.co.uk/?p=56</link>
		<comments>http://www.opsb.co.uk/?p=56#comments</comments>
		<pubDate>Wed, 24 Jun 2009 23:12:44 +0000</pubDate>
		<dc:creator>opsb</dc:creator>
		
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://www.opsb.co.uk/?p=56</guid>
		<description><![CDATA[Now that I&#8217;m starting to use git more regularly I&#8217;ve started looking for ways to make git even better. It turns out that git&#8217;s really easy to customise.
Aliases
Having recently heard that &#8220;git stage&#8221; is going to be added as an alias for &#8220;git add&#8221; another git fan mentioned that you can add your own git [...]]]></description>
			<content:encoded><![CDATA[<p>Now that I&#8217;m starting to use git more regularly I&#8217;ve started looking for ways to make git even better. It turns out that git&#8217;s really easy to customise.</p>
<h2>Aliases</h2>
<p>Having recently heard that &#8220;git stage&#8221; is going to be added as an alias for &#8220;git add&#8221; another git fan mentioned that you can add your own git aliases to the .git/config file. I particularly like this idea as some of the commands can be a little bit esoteric. To get you started try adding this entry to your .git/config file.</p>
<pre>
[alias]
  stage = add
  unstage = reset HEAD
</pre>
<p>Now you can add content to the staging area using </p>
<pre>
  git stage new_file
</pre>
<p>and then remove it again using</p>
<pre>
  git unstage new_file
</pre>
<h2>Pretty log</h2>
<p>This one&#8217;s an alias, just add it in the same way as stage/unstage</p>
<pre>
plog = log --pretty=tformat:'%h %Cblue%cr%Creset %cn %Cgreen%s%Creset'
</pre>
<p>I find this format much easier to read, colour coded information and you can fit lot&#8217;s more commits on screen. This makes the graph version of log awesome, take a look</p>
<pre>
git plog --graph
</pre>
<p>Win!</p>
<h2>Colour coded status</h2>
<p>Add the following to .git/config and staged files will be shown in green, unstaged in red.</p>
<pre>
[color]
  ui = auto
</pre>
<h2>Command line prompt</h2>
<p>The <a href="http://volnitsky.com/project/git-prompt/">git-prompt project</a> will let you customise your prompt to include all sorts of git information, the defaults are a bit full on but you can tame it to your taste easily enough </p>
<p>I&#8217;m sure I&#8217;ll keep adding to this list, let me know any great git tricks you&#8217;ve got.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.opsb.co.uk/?feed=rss2&amp;p=56</wfw:commentRss>
		</item>
	</channel>
</rss>
