<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>Effectif Development - Home</title>
  <id>tag:effectif.com,2009:mephisto/</id>
  <generator uri="http://mephistoblog.com" version="0.8.0">Mephisto Drax</generator>
  <link href="http://effectif.com/feed/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://effectif.com/" rel="alternate" type="text/html"/>
  <updated>2008-11-23T10:38:37Z</updated>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-11-23:157</id>
    <published>2008-11-23T01:34:00Z</published>
    <updated>2008-11-23T10:38:37Z</updated>
    <category term="ruby"/>
    <link href="http://effectif.com/2008/11/23/notes-from-the-ruby-manor-part-2" rel="alternate" type="text/html"/>
    <title>Notes from the Ruby Manor (part 2)</title>
<summary type="html">&lt;p&gt;So this is Ruby Manor part 2. After the &lt;a href=&quot;/2008/11/22/notes-from-the-ruby-manor&quot;&gt;Ruby Manor morning session&lt;/a&gt; we had a tasty pub lunch, rocked up a little late, and settled down to an interesting afternoon of Ruby talks&#8230;&lt;/p&gt;

&lt;p&gt;Again, the errors and omissions are mine.&lt;/p&gt;</summary><content type="html">&lt;p&gt;So this is Ruby Manor part 2. After the &lt;a href=&quot;/2008/11/22/notes-from-the-ruby-manor&quot;&gt;Ruby Manor morning session&lt;/a&gt; we had a tasty pub lunch, rocked up a little late, and settled down to an interesting afternoon of Ruby talks&#8230;&lt;/p&gt;

&lt;p&gt;Again, the errors and omissions are mine.&lt;/p&gt;

&lt;h2&gt;Jonathan Conway &#8211; Neo4J&lt;/h2&gt;

&lt;p&gt;In the beginning we had flat files. They worked well. Then we got relational databases. Great for ad-hoc queries and they allow you to define relational constraints. Brilliant. However, there are problems&#8230;&lt;/p&gt;

&lt;p&gt;When you try and map an object into a relational table you can run into issues. Jonathan pointed out other issues too, but I think I missed them. My mistake (I&#8217;d just had lunch and was slightly snoozy)&#8230;&lt;/p&gt;

&lt;p&gt;There are plenty of ORMs to choose from, such as &lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveRecord/Base.html&quot; title=&quot;Class: ActiveRecord::Base&quot;&gt;ActiveRecord&lt;/a&gt;, &lt;a href=&quot;http://datamapper.org/&quot; title=&quot;start [DataMapper]&quot;&gt;Datamapper&lt;/a&gt;, &lt;a href=&quot;http://sequel.rubyforge.org/&quot;&gt;Sequel&lt;/a&gt; and &lt;a href=&quot;http://www.nitroproject.org&quot; title=&quot;Nitro and Og&quot;&gt;Og&lt;/a&gt;. When we use an ORM we move the constraints out of the database and into the model. It&#8217;s a nice way to work, but you&#8217;re no longer using the relational database in the way that it was designed to be used. You end up with semi-structured data (data that has very few mandatory fields but many optional fields) and sparse tables. You then find that you need to partition your tables sooner than you would do if you had properly structured data.&lt;/p&gt;

&lt;p&gt;Maybe the relational database isn&#8217;t a one size fits all solution. There are other possibilites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Column oriented databases (e.g. lucid).&lt;/li&gt;
&lt;li&gt;Document oriented databases (such as &lt;a href=&quot;http://couchdb.org&quot;&gt;CouchDB&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Graph oriented databases (e.g. &lt;a href=&quot;http://neo4j.org&quot;&gt;Neo4J&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neo4J uses a graph as the underlying data structure. It&#8217;s based on Java, but has a small memory footprint. It&#8217;s also very fast, a benefit of being embedded in the JVM.&lt;/p&gt;

&lt;p&gt;There&#8217;s a Ruby wrapper &#8211; Neo4J.rb. It&#8217;s &lt;a href=&quot;http://github.com/andreasronge/neo4j/tree/master&quot;&gt;available on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Queries are written using Lucene. All nodes in the graph &#8211; and the relationships between them &#8211; can have properties defined on them. They&#8217;re exposed as simple hashes in Ruby.&lt;/p&gt;

&lt;p&gt;Jonathan doesn&#8217;t think that Neo4J.rb is production ready yet, but Neo4J itself is. Also see &lt;a href=&quot;http://neotechnology.com&quot;&gt;http://neotechnology.com&lt;/a&gt;. It sounds pretty interesting &#8211; well worth a look if you&#8217;re interested in database technology.&lt;/p&gt;

&lt;h2&gt;Martin Loughran &#8211; APICache&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://mloughran.com/&quot;&gt;Martin&lt;/a&gt; got up and did a lightning talk on his &lt;a href=&quot;http://blog.new-bamboo.co.uk/2008/6/6/api_cache&quot;&gt;APICache&lt;/a&gt; project; a useful little library for improving the resilience of sites that rely on (sometimes flaky) third party APIs.&lt;/p&gt;

&lt;h2&gt;Alex McCaw &#8211; Acts as Recommendable&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://eribium.org/blog/&quot;&gt;Alex&lt;/a&gt; works at &lt;a href=&quot;http://www.madebymany.co.uk&quot;&gt;Made By Many&lt;/a&gt; where he developed the Acts as Recommendable plugin. It allows you to add a recommendations system to your Rails app, and was largely developed using O&#8217;Reilly&#8217;s &lt;a href=&quot;http://oreilly.com/catalog/9780596529321/&quot;&gt;Programming Collective Intelligence&lt;/a&gt;. There are three basic approaches to recommendations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content based (e.g. bayesian filtering).&lt;/li&gt;
&lt;li&gt;User based.&lt;/li&gt;
&lt;li&gt;Item based.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Acts as Recommendable uses item based recommendations.&lt;/p&gt;

&lt;p&gt;To use the plugin you need to define a &lt;code&gt;has_many :through&lt;/code&gt; relationship between your models, then call &lt;code&gt;acts_as_recommendable&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User &amp;lt; ActiveRecord::Base
  has_many :user_books
  has_many :books, :through =&amp;gt; :user_books
  acts_as_recommendable :books, :through =&amp;gt; :user_books
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You will then have these extra methods on your models:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;User#similar_users
User#recommended_books
Book#similar_books
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this example the second two methods are the really useful ones.&lt;/p&gt;

&lt;p&gt;You can obviously store things on the join table, such as a score (if you&#8217;re interested in making a rating system you can add a rating column to your join table).&lt;/p&gt;

&lt;p&gt;You can choose from the following algorightms: Manhattan distance, Euclidean distance, Cosine, Pearson correlation coefficient, Jaccard, Levenshtein.&lt;/p&gt;

&lt;p&gt;Alex described some of the issues that they ran into whilst developing Acts as Recommendable. They had to work around scaling issues (such as queries that pulled every row from their tables), caching issues, problems with Ruby (it couldn&#8217;t cope with data sets so large), and performance problems with the algorithm. They solved all the issues (though I didn&#8217;t quite grasp the full details of how they went about it), split the cache up, and re-implemented slow code (the Pearson algorithm) in C.&lt;/p&gt;

&lt;p&gt;Alex&#8217;s advice when implementing &lt;code&gt;acts_as_recommendable&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don&#8217;t want to have too many relationships.&lt;/li&gt;
&lt;li&gt;You don&#8217;t want to have too many items (but it&#8217;s been tested with 100,000).&lt;/li&gt;
&lt;li&gt;Setup the similarity relationships for items, not users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To scale it further you can use &lt;a href=&quot;http://en.wikipedia.org/wiki/K-means_algorithm&quot; title=&quot;k-means algorithm - Wikipedia, the free encyclopedia&quot;&gt;K Means clustering&lt;/a&gt; or split your clusters by category (which is the way Amazon does it). You need to start splitting your clusters once you get to approximately 100,000 items. The plugin can probably cope with up to 200,000 items, but you can always partition your data and use Nanite or map reduce to scale it further.&lt;/p&gt;

&lt;p&gt;This is clearly pretty clever stuff. I&#8217;ve missed many of the details from Alex&#8217;s slides; hopefully he&#8217;ll upload his slides to &lt;a href=&quot;http://eribium.org/blog/&quot;&gt;his blog&lt;/a&gt;. The &lt;a href=&quot;http://github.com/maccman/acts_as_recommendable/tree/master&quot;&gt;code is in github&lt;/a&gt; (for a change). There&#8217;s also a &lt;a href=&quot;http://goruco2008.confreaks.com/05_dix.html&quot;&gt;Collective Intelligence video&lt;/a&gt; from Goruco 2008 that&#8217;s worth watching.&lt;/p&gt;

&lt;h2&gt;Sean O&#8217;Halpin &#8211; Unobtrusive Metaprogramming&lt;/h2&gt;

&lt;p&gt;What does unobtrusive mean? In this context it&#8217;s &#8220;polite, considerate&#8221;. About being nice. And metaprogramming? &#8220;Programs that write programs are the happiest programs in the world.&#8221; (Andrew Hume)&lt;/p&gt;

&lt;p&gt;Before you load ActiveSupport you have 75 methods on a Ruby object. After you load ActiveSupport you have 173. Sean&#8217;s point is that Rails messes with stuff. By defining methods on &lt;code&gt;Object&lt;/code&gt; rails can affect what you&#8217;re doing in your own namespace. For example, Rails defines an &lt;code&gt;inspect&lt;/code&gt; on class that hits the database. Would you expect that? More to the point, people who think that&#8217;s okay frighten the crap out of me&#8230;&lt;/p&gt;

&lt;p&gt;So (assuming you&#8217;re not also slightly scared by these people) what&#8217;s the problem?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Namespace pollution: You can&#8217;t use the names you want, your code is not safe, and you get clashes that can be hard to debug (we&#8217;ve all been there).&lt;/li&gt;
&lt;li&gt;Documentation pollution: &lt;code&gt;rubydoc&lt;/code&gt; documents all methods on your object, so all this extra cruft ends up in your docs.&lt;/li&gt;
&lt;li&gt;Cognitive pollution: What does an object do? You can&#8217;t tell by looking at its capabilities.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So it&#8217;s about pollution then. Stinky.&lt;/p&gt;

&lt;p&gt;Why does this happen? The obvious answer is that it&#8217;s easy. But it&#8217;s one thing to monkey patch classes/objects in your own application. It&#8217;s another thing entirely to do it in a reusable library; you affect everybody.&lt;/p&gt;

&lt;p&gt;How should we solve these problems?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use functions that accept an object and return a modified version (i.e. you don&#8217;t need to monkey patch another method onto an object &#8211; functions are okay).&lt;/li&gt;
&lt;li&gt;Use namespaces (i.e. modules).&lt;/li&gt;
&lt;li&gt;Hide your mess.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sean then showed us some rather clever code for &#8220;hiding your mess&#8221;. Hopefully he&#8217;ll post the code online. If he does, check out the &lt;code&gt;KittySafe&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;And as Sean pointed out, we each paid 23p (8 minutes out of 8 hours) to hear his talk. Bargain. It would have been even better value if somebody hadn&#8217;t been heckling halfway through (cheers for your input Piers, but have you considered being slightly more unobtrusive yourself by saving your quibbles until the end of the presentation?).&lt;/p&gt;

&lt;h2&gt;James Darling &#8211; Government Hack Day&lt;/h2&gt;

&lt;p&gt;James is organising a government hack day. He&#8217;s realised that it&#8217;s difficult to persuade the powers that be to open up information behind freely available APIs. Most people just don&#8217;t appreciate the benefit of mashups (release the data and goodness is bound to follow) until you show them. &lt;/p&gt;

&lt;p&gt;The plan is to get some data together (he&#8217;ll be launching a wiki to collaborate on data gathering), get some interested coders together, then we can all spend 24 hours producing some incredible goodness. The results will (at the very least) help to demonstrate to those who hold the keys just how much could be done if the government adopted the same approach.&lt;/p&gt;

&lt;p&gt;Interested parties can sign up on the &lt;a href=&quot;http://hackday.coupde.com/&quot; title=&quot;The UK Government Hack Day thingy bob&quot;&gt;UK Government Hack Day site&lt;/a&gt;. So sign up!&lt;/p&gt;

&lt;h2&gt;David Black &#8211; Ruby 1.9&lt;/h2&gt;

&lt;p&gt;This is a talk about the changes in Ruby 1.9. Ruby 1.8.7 has a bunch of features that were backported from 1.9, but &lt;a href=&quot;http://www.rubypal.com/&quot; title=&quot;Ruby and Rails Training and Consulting&quot;&gt;David&lt;/a&gt; is looking at the changes between 1.8.6 and 1.9.&lt;/p&gt;

&lt;h3&gt;Method and block argument semantics&lt;/h3&gt;

&lt;p&gt;Required arguments can now come after optional arguments. If you call the method below with two arguments they&#8217;ll get bound to &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;c&lt;/code&gt; (i.e. required arguments get serviced first):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def m(a, b = 1, c)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Required args can now come after wildcard aguments:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def m(a, *b, c)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Arguments can be grouped with parentheses for dealing with arguments passed as arrays. If you call the method below with &lt;code&gt;[1, 2], 3&lt;/code&gt; then &lt;code&gt;a&lt;/code&gt; will be 1, &lt;code&gt;b&lt;/code&gt; 2 and &lt;code&gt;c&lt;/code&gt; 3. If you call it with &lt;code&gt;1, 2&lt;/code&gt; then &lt;code&gt;a&lt;/code&gt; will be 1, &lt;code&gt;b&lt;/code&gt; will be &lt;code&gt;nil&lt;/code&gt; and &lt;code&gt;c&lt;/code&gt; will be 2.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def m((a, b), c)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Block and method argument semantics have now effectively been merged, though they were different in previous versions of Ruby. This means you can pass optional arguments to blocks, use the parentheses above, etc.&lt;/p&gt;

&lt;h3&gt;Block variable scoping rules&lt;/h3&gt;

&lt;p&gt;In Ruby 1.8:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;a = 1
Proc.new { |a| }.call(30)
a #=&amp;gt; 30
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In Ruby 1.9:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;a = 1
Proc.new { |a| }.call(30)
a #=&amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However if the variable you update isn&#8217;t the block argument then it&#8217;ll still get changed in Ruby 1.9:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;a = 1; { a = 2 }  # a is now 2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Unless you specify it after a semicolon, which declares the variable to be a block local variable:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;a = 1; { |x; a| a = 2 }  # a is still 1
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Enumerators&lt;/h3&gt;

&lt;p&gt;Enumerator objects have all the methods you get in the Enumerable module, but they don&#8217;t know how to find their next item. So you have to tell them. You can teach the object how to do it by passing a block when you instantiate it, or you can get it to borrow &lt;code&gt;each&lt;/code&gt; from somewhere else.&lt;/p&gt;

&lt;p&gt;Here&#8217;s an example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;enum = Enumerator.new do |y|
  y &amp;lt;&amp;lt; [&quot;New York&quot;, &quot;NY&quot;]
  y &amp;lt;&amp;lt; [&quot;Florida&quot;, &quot;FL&quot;]
  y &amp;lt;&amp;lt; [&quot;Texas&quot;, &quot;TX&quot;]
end

enum.each do |state, abbr|
  puts &quot;#{abbr} is short for #{state}&quot;
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once your object knows how to handle &lt;code&gt;each&lt;/code&gt; you can happily call all the other &lt;code&gt;Enumerable&lt;/code&gt; methods such as &lt;code&gt;map&lt;/code&gt;. Nice.&lt;/p&gt;

&lt;p&gt;David then showed us a neat bit of code for XOR&#8217;ing a string (that I think he wrote earlier today in response to Paul pointing out how much easier it is to do in Perl) that took advantage of these new enum capabilities. Sorry, I didn&#8217;t capture the details&#8230;&lt;/p&gt;

&lt;p&gt;Good talk. He&#8217;s an entertaining chap.&lt;/p&gt;

&lt;h2&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;It&#8217;s been a great day. Excellent work James and Murray. Why aren&#8217;t more conferences like this? I think I may actually have made money out of turning up. No really.&lt;/p&gt;

&lt;p&gt;You see, in order to break even (having paid for the room, audio equipment, projector, etc.) the conference only needed 66 attendees. It was fully booked with 130 people. All the spare cash was put behind the bar downstairs. I don&#8217;t know about you, but I&#8217;ve never been to a conference that cost 12 quid (quid = British pounds) and came with five free pints of beer. We didn&#8217;t even manage to drink the bar dry (a poor show, but in our defense it was a subsidised University bar), so it stretched to half a curry for those of us that made it.&lt;/p&gt;

&lt;p&gt;Genius, really.&lt;/p&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-11-22:154</id>
    <published>2008-11-22T13:28:00Z</published>
    <updated>2008-11-24T10:14:00Z</updated>
    <category term="ruby"/>
    <link href="http://effectif.com/2008/11/22/notes-from-the-ruby-manor" rel="alternate" type="text/html"/>
    <title>Notes from the Ruby Manor</title>
<summary type="html">&lt;p&gt;I&#8217;m lucky enough to be at &lt;a href=&quot;http://rubymanor.org&quot;&gt;RubyManor&lt;/a&gt; today; a Ruby conference organised by Ruby users, for Ruby users, costing the grand total of twelve of your British pounds. A bargain, as you&#8217;ll see if you check the &lt;a href=&quot;http://rubymanor.org/schedule&quot;&gt;lineup of talks and speakers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I started making notes, but it seems to have evolved into some kind of blog post. Apologies for typos, crap grammar and glaring errors.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I&#8217;m lucky enough to be at &lt;a href=&quot;http://rubymanor.org&quot;&gt;RubyManor&lt;/a&gt; today; a Ruby conference organised by Ruby users, for Ruby users, costing the grand total of twelve of your British pounds. A bargain, as you&#8217;ll see if you check the &lt;a href=&quot;http://rubymanor.org/schedule&quot;&gt;lineup of talks and speakers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I started making notes, but it seems to have evolved into some kind of blog post. Apologies for typos, crap grammar and glaring errors.&lt;/p&gt;

&lt;h2&gt;Paul Battley &#8211; I&#8217;m an Evil iPlayer hacker&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://po-ru.com&quot;&gt;Paul Battley&lt;/a&gt; is a hacker. BBC News said so, so it must be true. Paul started by running through the history of iPlayer&#8217;s development. It launched in July 2007 in open beta, but required Windows XP and a custom client with built-in DRM. In December 2007 a flash streaming version was released, which worked on Windows, Mac and Linux. However you could only watch content for seven days.&lt;/p&gt;

&lt;p&gt;In March 2008 an iPhone version of the iPlayer was released. The iPhone doesn&#8217;t support flash, so the iPhone version streams MPEG4 over HTTP. When first released it worked by sniffing the user agent, and checking for the string &#8220;iphone&#8221;! Within hours people had released scripted solutions for downloading the videos, but one week later the BBC had plugged the hole.&lt;/p&gt;

&lt;p&gt;Wasting no time, Paul discovered that the video is pulled with a different user agent to the page, and an extra HTTP header was required. All was well with the world, but in June the downloaded files stopped working. Comparing old files to the new ones, they discovered that the BBC had XOR&#8217;d some data in the middle of the MPEG files. That was easy to work around.&lt;/p&gt;

&lt;p&gt;Later in June version 2 of the iPlayer came out with new URLs, but no new counter measures. In September the iPlayer started to serve radio, which can also be downloaded with &lt;a href=&quot;http://po-ru.com/projects/iplayer-downloader&quot;&gt;Paul&#8217;s scripts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So the basic technique for downloading content from the iPlayer is to configure your client to pretend to be an iPhone.&lt;/p&gt;

&lt;p&gt;Paul then talked about how to get a programme&#8217;s ID from some JavaScript served up with the programme&#8217;s web page. This was one of the hardest parts of the job, requiring him to write a JavaScript tokenzer.&lt;/p&gt;

&lt;p&gt;The flash version of the iPlayer uses the RTMP protocol, to make it a bit more difficult for people to &#8220;steal&#8221; the content. The next thing that Paul decided to try and reverse engineer RTMP and write a Ruby library to process RTMP streams. He&#8217;s not finished yet, but it looks as though he&#8217;s got quite a long way through the job.&lt;/p&gt;

&lt;p&gt;Lessons learnt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Releasing Ruby software to users is hard&lt;/li&gt;
&lt;li&gt;Writing GUIs is tedious (but check out the iPlayer grabber)&lt;/li&gt;
&lt;li&gt;Reverse engineering is fun and enlightening&lt;/li&gt;
&lt;li&gt;Obfuscation doesn&#8217;t work&lt;/li&gt;
&lt;li&gt;You need a feedback channel (or people will make one)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was a cracking presentation. More information is available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://po-ru.com/projects/iplayer-downloader&quot;&gt;iPlayer Downloader&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.google.com/p/paulbattley&quot;&gt;http://code.google.com/p/paulbattley&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a graphical app for the Mac, &lt;a href=&quot;http://fader.co.uk/post/iPlayer-Grabber-the-iPlayer-downloader-for-Mac-OS-X.aspx&quot; title=&quot;Fader | iPlayer Grabber, the iPlayer downloader for Mac OS X&quot;&gt;iPlayer grabber&lt;/a&gt; looks great.&lt;/p&gt;

&lt;h2&gt;Dan Webb &#8211; 8 Minutes on Rack&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://danwebb.net&quot;&gt;Dan Webb&lt;/a&gt;&#8217;s main message was that &lt;a href=&quot;http://rack.rubyforge.org&quot;&gt;rack&lt;/a&gt; is worth everybody&#8217;s time, not just that of people who write web frameworks. But what is it? It&#8217;s not a framework. It&#8217;s not a library. It&#8217;s just a convention.&lt;/p&gt;

&lt;p&gt;The convention is this. You have a &lt;code&gt;call&lt;/code&gt; method on a ruby object that returns an array of three values; the HTTP status code, a hash of headers to send to the client, and the response body. Here&#8217;s an example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;app = Proc.new do |env|
  [200, { &quot;Content-Type&quot; =&amp;gt; &quot;text/plain&quot; }, &quot;Hello World&quot;]
end
Rack::Handler::Thin.run(app, :Port =&amp;gt; 4000)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The response body can be any object that responds to the each method, such as an open file object.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;file = File.new(&quot;myfile.xml&quot;)
[200, { &quot;Content-Type&quot; =&amp;gt; &quot;application/xml&quot; }, file]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For binary files you can write your own class to stream anything you like. Here&#8217;s an example of an &lt;code&gt;each&lt;/code&gt; method that you could use to stream a binary file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def each
  File.open(@file, &quot;rb&quot;) do file
    while part = file.read(8192)
      yield part
    end
    File.delete(@file)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Why should you care? Passenger, Mongrel, CGI, SCGI, FastCGI, Thin, Webrick, Fuzed (the list goes on).&lt;/p&gt;

&lt;p&gt;Other things that Dan thinks are worth looking into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Rack Gem&lt;/li&gt;
&lt;li&gt;Middleware (very simple and clver, well worth a look)&lt;/li&gt;
&lt;li&gt;Rack and Passenger&lt;/li&gt;
&lt;li&gt;rackup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check http://rack.rubyforge.org and read the (slightly impenetrable) documentation. Dan will be writing some Rack stuff up on his &lt;a href=&quot;http://danwebb.net&quot;&gt;blog&lt;/a&gt;, and his slides are &lt;a href=&quot;http://www.slideshare.net/danwrong/8-minutes-on-rack-presentation&quot;&gt;available on slideshire&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;David Black popped his hand up at the end and pointed out that strings in Ruby 1.9 don&#8217;t have an &lt;code&gt;each&lt;/code&gt; method. Ooops.&lt;/p&gt;

&lt;h2&gt;George Palmer &#8211; Nanite&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.rowtheboat.com/&quot;&gt;George Palmer&lt;/a&gt; gave an introduction to [Nanite](. I missed the very beginning because I was chatting in the canteen. Sorry.&lt;/p&gt;

&lt;p&gt;There are currently three different broad brush approaches available to running background tasks in Ruby web frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fork into background of rails process (e.g. spawn, run_later).&lt;/li&gt;
&lt;li&gt;Record a job to a file or database, for handling by a background deamon.&lt;/li&gt;
&lt;li&gt;Fork into some kind of queue (e.g. backgroundRB, beanstalk, starling, etc.).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nanite is great for heavy lifting, and is designed for running jobs on other servers. It&#8217;s based on RabbitMQ; a queuing system written in Erlang. As an aside, when installing Erlang on a Mac you need to avoid using the R12B5 of Erlang; this means that you don&#8217;t want to use &lt;a href=&quot;http://www.macports.org&quot;&gt;MacPorts&lt;/a&gt;, and are better off just downloading the source form the &lt;a href=&quot;http://www.erlang.org&quot;&gt;Erlang site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amqp.org&quot;&gt;AMQP&lt;/a&gt; stands for Advanced Messaging Queueing Protocol. It&#8217;s an enterprise quality protocol, used by (for example) big financial corporations. It&#8217;s also language neutral. &lt;a href=&quot;http://www.rabbitmq.com/&quot;&gt;RabbitMQ&lt;/a&gt; implements AMQP 0.8.&lt;/p&gt;

&lt;p&gt;To install AMQP for Ruby you need the eventmachine gem, then grab the source from github and install the gem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo gem install eventmachine
$ git clone git://github.com/tmm1/amqp.git
$ cd amqp &amp;amp;&amp;amp; rake gem &amp;amp;&amp;amp; sudo gem install amqp-&amp;lt;version&amp;gt;.gem
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nanite mappers control and track the work. You can run as many mappers as you like; they get updates from the mapper exchange. Mappers are run either inside your Rails/Merb app (on Thin) or via the command line.&lt;/p&gt;

&lt;p&gt;Mappers assign work to agents. Agents announce their presence when they start up so that the mappers know that they&#8217;re available. Actors know how to do the work. Here&#8217;s an example actor:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Manor &amp;lt; Nanite::Actor
  expose :name

  def name(vars)
    # Do something interesting
    :result =&amp;gt; &quot;RubyManor&quot;
  end
end

Nanite:Dispatcher.register(Manor.name)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There were a lot more code examples that I didn&#8217;t manage to catch, but George will be uploading the slides to his blog, and I recommend taking a look.&lt;/p&gt;

&lt;p&gt;This is what I did catch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nanite has no concept of security, but relies on the security in RabbitMQ.&lt;/li&gt;
&lt;li&gt;George recommends monitoring your agents with a program like monit or god.&lt;/li&gt;
&lt;li&gt;Once a nanite request has been sent to an agent and processed by an actor you can&#8217;t check it for status; use the database for that.&lt;/li&gt;
&lt;li&gt;You can send files to your agents (e.g. an image that needs resizing).&lt;/li&gt;
&lt;li&gt;Nanite allocates work by looking at the currently available agents and sending the job to (by default) the least loaded server. If you prefer you can choose to send a request to all agents, or a randomly selected agent.&lt;/li&gt;
&lt;li&gt;You can also define your own metrics (using Ruby) for an agent&#8217;s &#8220;load&#8221;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, George ran a quick demo. Members of the audience fired up a nanite agent (an example agent that is distributed with the nanite source code) that announced itself to a server running on his laptop, and he used the server to list the gems on everybody&#8217;s laptops. A neat demo.&lt;/p&gt;

&lt;p&gt;For more information, try these links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.rabbitmq.com&quot;&gt;http://www.rabbitmq.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://hopper.squarespace.com/blog/2008/7/22/simple-amqp-library-for-ruby.html&quot;&gt;http://hopper.squarespace.com/blog/2008/7/22/simple-amqp-library-for-ruby.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://github.com/ezmobius/nanite&quot;&gt;http://github.com/ezmobius/nanite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Nanite docs aren&#8217;t currently that great, but the code is very readable.&lt;/p&gt;

&lt;h2&gt;Rob McKinnon &#8211; Rugalytics&lt;/h2&gt;

&lt;p&gt;Rob wondered if there was a Ruby API for Google Analytics. jnunemaker had written &lt;a href=&quot;http://github.com/jnunemaker/statwhore/tree/master&quot;&gt;statwhore&lt;/a&gt; that gets you some XML, and wrapped it with helper methods such as &lt;code&gt;pageviews&lt;/code&gt; and &lt;code&gt;visits&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So that was cool, but Rob wanted more. He wanted to emerge class definitions at runtime from the data. So he forked statwhore, then hacked on it so much that he renamed it to &lt;a href=&quot;http://github.com/robmckinnon/rugalytics/tree/master&quot;&gt;Rugalytics&lt;/a&gt;. Then he converted the XML to CSV to make it easier to work with.&lt;/p&gt;

&lt;p&gt;Rob is now using Rugalytics to put a list of popular pages on his site, pulling the data direct from Google Analytics. Fantastic stuff.&lt;/p&gt;

&lt;p&gt;To get an Analytics report you pass the report name when calling analytics. You can find the available reports by calling the brand new &lt;code&gt;profile.report_names&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Google have a tendency to tweak the format of the CSV files, and this has been known to break Rugalytics. There are also some language issues &#8211; if you&#8217;ve got your Google account setup to US english then you end up with a method (dynamically created from the CSV) called &lt;code&gt;pageviews&lt;/code&gt;. If you&#8217;re Google account is configured for UK english you used to get a method called &lt;code&gt;page_views&lt;/code&gt; (but Rob has now hardcoded it to &lt;code&gt;pageviews&lt;/code&gt;). In Italian you get &lt;code&gt;pagine_visualizzate&lt;/code&gt;. Nice! The README file now advises that you choose US/UK english.&lt;/p&gt;

&lt;p&gt;Rob also has a local web server (which will be included in the gem) &#8211; similar to a Rack example that Dan talked about earlier &#8211; that serves JavaScript to Rob&#8217;s &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/748&quot;&gt;Greasemonkey&lt;/a&gt; enabled browser. Greasemonkey then annotates the pages on his site (for him, and only for him) showing which keywords people have used to find the page that he&#8217;s currently looking at. Pretty impressive.&lt;/p&gt;

&lt;p&gt;See &lt;a href=&quot;http://github.com/robmckinnon&quot;&gt;Rob&#8217;s account on github&lt;/a&gt; to get the code. Rugalytics is also available as a gem.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo gem install rugalytics
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Murray Steele and Martin Saddler &#8211; GUI Manor Born&lt;/h2&gt;

&lt;p&gt;Apparrently Ben Griffiths suggested all talks should be named around the phrase &#8220;To The Manor Born&#8221;. As Murray put it, &#8220;at least we tried&#8221;.&lt;/p&gt;

&lt;p&gt;So Murray and &lt;a href=&quot;http://www.beyondthetype.com&quot;&gt;Martin&lt;/a&gt; are talking about how to build GUI apps with Ruby. There are plenty of GUI toolkits available to Ruby, such as wxRuby, fxRuby, etc. However for their example app (a Twitter client)   Martin used &lt;a href=&quot;http://monkeybars.rubyforge.org/&quot;&gt;Monkeybars&lt;/a&gt; and Murray used &lt;a href=&quot;http://shoooes.net/&quot; title=&quot;Shoes &amp;amp;bull; Colorful programs for Mac OS X, Linux and Windows&quot;&gt;Shoes&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Monkeybars&lt;/h3&gt;

&lt;p&gt;Monkeybars is built using jRuby and produces a swing front end. What you really need (to follow along with Martin&#8217;s demo) is JRuby, Netbeans and Ruby gems. To start a new Monkeybars project you run a command that makes you a blank project (much as Rails does):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ monkeybars myapp
$ cd myapp
$ rawr install  # choose option 2
$ jruby -S rake generate ALL='src/login'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At this point Martin demonstrated (in the style of Blue Peter &#8211; &#8220;here&#8217;s one I made earlier&#8221;) how to build his Twitter client using Netbeans. There&#8217;s a GUI builder that allows you to drag and drop GUI controls into the user interface. You can then bind the widgets to the code using Netbeans&#8217; normal GUI builder tools.&lt;/p&gt;

&lt;p&gt;Monkeybars uses a form of the &lt;a href=&quot;http://en.wikipedia.org/wiki/Model-view-controller&quot;&gt;MVC design pattern&lt;/a&gt;. The view binds the widgets defined in the GUI builder to Ruby code that you can access in the controller. The controller and view are pretty much standard stuff.&lt;/p&gt;

&lt;p&gt;You can easily create callback methods in your controller for widgets in your UI by taking the name of the widget (e.g. &lt;code&gt;login_button&lt;/code&gt;) and creating a method called &lt;code&gt;login_button_action_performed&lt;/code&gt;. Seems prety straightforward.&lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;rawr&lt;/code&gt; program to package your app.&lt;/p&gt;

&lt;p&gt;The downsides? Not everything works in JRuby at the moment (for example Martin couldn&#8217;t use the twitter4r gem). Build paths can get mucked up. Assumes knowledge of Java, there&#8217;s a learning curve, etc. But these problems are being worked on.&lt;/p&gt;

&lt;p&gt;The good bits? GUI Builder. Interface to mature Java libs. It&#8217;s all Ruby. Cross platform distribution. No platform dependency on Ruby.&lt;/p&gt;

&lt;p&gt;See &lt;a href=&quot;http://tweetobix.com&quot;&gt;tweetobix.com&lt;/a&gt; to follow through with Martin&#8217;s example.&lt;/p&gt;

&lt;h3&gt;Shoes&lt;/h3&gt;

&lt;p&gt;Shoes is a GUI framework written by &lt;a href=&quot;http://whytheluckystiff.net/&quot; title=&quot;.c(  whytheluckystiff  )o.&quot;&gt;whytheluckystiff&lt;/a&gt;. It was designed as a replacement for an HTML rendering engine for use when teaching people to code, so it&#8217;s pretty familiar to people who know a fair bit of HTML.&lt;/p&gt;

&lt;p&gt;To install it you don&#8217;t install a gem. Shoes is it&#8217;s own version of Ruby. &lt;a href=&quot;http://shoooes.net/downloads/&quot;&gt;Download it&lt;/a&gt; and install it.&lt;/p&gt;

&lt;p&gt;The code is pretty simple. Murray&#8217;s app is called Talon. You start with a block, which makes a window. Then you can do stuff to the window. Here&#8217;s the main window (with no widgets in it):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Shoes.app :title =&amp;gt; &quot;Talon&quot; do
  background &quot;#fff&quot;
  background &quot;talon.jpg&quot;,
             :bottom =&amp;gt; 0,
             :right =&amp;gt; -20
  flow :width =&amp;gt; &quot;100%&quot; do
    background &quot;#df9&quot;, :curve =&amp;gt; 12
    title &quot;Talon&quot;,
          :stroke =&amp;gt; &quot;#691FFF&quot;,
          :align =&amp;gt; &quot;center&quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The styling is a lot like CSS. Backgrounds can be layered.&lt;/p&gt;

&lt;p&gt;To layout widgets you don&#8217;t worry about layout managers; you use stacks and flows. Stacks allow you to lay things out vertically (a bit like a vbox in GTK). Flows are similar, but run horizontally (a GTK hbox).&lt;/p&gt;

&lt;p&gt;To load a gem you must reference it in the &lt;code&gt;setup&lt;/code&gt; block. This will ensure that when somebody runs your app that all required gems will be installed, rather than throwing a backtrace.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Shoes.setup do
  gem &quot;twitter&quot;
end

require &quot;twitter&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Murray showed a lot of exmaple code for laying out and animating widgets. It seems very clear and succinct.&lt;/p&gt;

&lt;p&gt;You can ask Shoes to package your app with one click, and it builds a package for Windows, Mac, etc.&lt;/p&gt;

&lt;p&gt;For more info you can access the manual:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ shoes --manual
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should also check out &lt;a href=&quot;http://hackety.org/press&quot;&gt;Nobody Knows Shoes&lt;/a&gt;, which &#8211; Murray assures us &#8211; is fantastic.&lt;/p&gt;

&lt;p&gt;The code for Murray&#8217;s example (Talon) is &lt;a href=&quot;http://github.com/hlame/talon&quot;&gt;available on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cracking stuff.&lt;/p&gt;

&lt;h2&gt;The Afternoon Session&lt;/h2&gt;

&lt;p&gt;At this point we went off to the pub for lunch, but when we got back I took notes in the afternoon session too. See &lt;a href=&quot;/2008/11/23/notes-from-the-ruby-manor-part-2&quot;&gt;Notes from the Ruby Manor (part 2)&lt;/a&gt;.&lt;/p&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-11-17:153</id>
    <published>2008-11-17T23:36:00Z</published>
    <updated>2008-12-12T18:19:29Z</updated>
    <category term="automation"/>
    <link href="http://effectif.com/2008/11/17/optimising-png-images-for-the-web" rel="alternate" type="text/html"/>
    <title>Optimising PNG images for the web</title>
<content type="html">&lt;p&gt;Stoyan Stefanov has recently written an interesting series of articles on optimising image files for the web. In his &lt;a href=&quot;http://yuiblog.com/blog/2008/11/14/imageopt-3/&quot;&gt;latest article&lt;/a&gt; he shows how to use &lt;a href=&quot;http://en.wikipedia.org/wiki/Pngcrush&quot;&gt;pngcrush&lt;/a&gt; to safely shrink your files, one file at a time:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ pngcrush -rem alla -brute -reduce src.png dest.png
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This evening I found myself wanting to repeat that command for all the PNG files in my current directory, and then (having reviewed the new images) overwrite the originals. Rather than do it by hand I did what all pragmatic programmers do these days, and wrote a script. Here it is in action:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ pngcrushall 
Crushing test4012.png ... ok
Crushing test9200a.png ... ok
Crushing test9200b.png ... ok
Overwrite original files? (y/N) y
Overwriting test4012.png ... ok
Overwriting test9200a.png ... ok
Overwriting test9200b.png ... ok
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can get it from github &#8211; &lt;a href=&quot;http://github.com/gma/gma-utils/tree/master/web/pngcrushall&quot;&gt;download pngcrushall&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Obviously you&#8217;ll need to install &lt;a href=&quot;http://en.wikipedia.org/wiki/Pngcrush&quot;&gt;pngcrush&lt;/a&gt; too. On a Mac this is pretty easy with &lt;a href=&quot;http://www.macports.org&quot;&gt;MacPorts&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo port install pngcrush
&lt;/code&gt;&lt;/pre&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-11-11:151</id>
    <published>2008-11-11T23:20:00Z</published>
    <updated>2008-11-11T23:23:06Z</updated>
    <category term="automation"/>
    <category term="merb"/>
    <category term="rails"/>
    <link href="http://effectif.com/2008/11/11/integration-testing-for-multiple-web-applications" rel="alternate" type="text/html"/>
    <title>Integration Testing for Multiple Web Applications &#8211; Introducing Integral</title>
<content type="html">&lt;p&gt;I&#8217;ve recently been developing a Rails app that relies on a database that is maintained by another team as part of a different application. As you can imagine, unexpected changes to the database schema can bring our Rails app to it&#8217;s knees if they&#8217;re deployed to the live server before our application is updated.&lt;/p&gt;

&lt;p&gt;So far we&#8217;ve been lucky, but the issue has been at the back of our minds for a while. So this week I sat down and cooked up a solution, and we&#8217;ve called it &lt;a href=&quot;http://github.com/gma/integral/wikis&quot;&gt;Integral&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Integral consists of some &lt;a href=&quot;http://yehudakatz.com/2008/05/12/by-thors-hammer/&quot;&gt;Thor&lt;/a&gt; tasks that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run tests on your test server.&lt;/li&gt;
&lt;li&gt;Keep track of which versions of your applications work well together.&lt;/li&gt;
&lt;li&gt;Can be integrated into your deployment recipes to ensure that deploying an app should never break your live server again…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the Integral code is &lt;a href=&quot;http://github.com/gma/integral/tree/master&quot;&gt;on github&lt;/a&gt;, and I&#8217;ve written some fairly extensive &lt;a href=&quot;http://github.com/gma/integral/wikis&quot;&gt;documentation on the Wiki&lt;/a&gt;. Please try it out, give us some feedback, fork it, burn it, give us some feedback, etc. etc.&lt;/p&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-10-05:145</id>
    <published>2008-10-05T08:36:00Z</published>
    <updated>2008-10-22T08:51:52Z</updated>
    <category term="merb"/>
    <link href="http://effectif.com/2008/10/5/installing-merb-with-thor" rel="alternate" type="text/html"/>
    <title>Installing merb with thor</title>
<content type="html">&lt;p&gt;&lt;strong&gt;Update (22 October):&lt;/strong&gt; Things move &lt;strong&gt;very&lt;/strong&gt; fast around here &#8211; the &lt;code&gt;merb.thor&lt;/code&gt; file has been overhauled and now all you have to do to get the latest edge is download a single .thor file and run one command.&lt;/p&gt;

&lt;p&gt;Matt Aimonetti wrote a neat post on &lt;a href=&quot;http://merbist.com/2008/10/04/get-on-merb-edge-pre-10/&quot;&gt;installing merb edge with thor&lt;/a&gt;. &lt;a href=&quot;http://yehudakatz.com/2008/05/12/by-thors-hammer/&quot;&gt;Thor&lt;/a&gt; seems to have become the defacto technique for installing merb edge these days, and I&#8217;m going to give it a go in preference to &lt;a href=&quot;/2008/7/14/merb-and-datamapper-on-the-edge&quot;&gt;installing merb with sake&lt;/a&gt;, as I had been doing.&lt;/p&gt;

&lt;p&gt;Since Matt wrote his article the &lt;code&gt;merb.thor&lt;/code&gt; file underwent a serious overhaul during the sprint at MerbCamp &#8211; what you need to do these days is quite a bit simpler:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;curl -L http://merbivore.com/merb.thor &amp;gt; merb.thor
sudo thor merb:stack:install --edge
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The source code for all the Merb gems will be checked out into a directory called &lt;code&gt;./src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To get the full low down on how to use Thor with Merb, head over to the &lt;a href=&quot;http://wiki.merbivore.com/howto/installation/thor&quot;&gt;Thor Howto&lt;/a&gt; on the excellent new &lt;a href=&quot;http://wiki.merbivore.com/&quot;&gt;Merb wiki&lt;/a&gt;.&lt;/p&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-10-02:143</id>
    <published>2008-10-02T22:24:00Z</published>
    <updated>2008-10-02T22:26:57Z</updated>
    <category term="mac"/>
    <category term="merb"/>
    <link href="http://effectif.com/2008/10/2/building-do_mysql-on-a-64-bit-mac" rel="alternate" type="text/html"/>
    <title>Building do_mysql on a 64-bit Mac</title>
<summary type="html">&lt;p&gt;Whilst &lt;a href=&quot;/2008/7/14/merb-and-datamapper-on-the-edge&quot;&gt;installing merb with edgy&lt;/a&gt; I&#8217;ve had run into some trouble building the &lt;code&gt;do_mysql&lt;/code&gt; gem on my Macbook Pro. It&#8217;s a 64-bit machine, so I installed the 64-bit version of MySQL, and that was my first mistake…&lt;/p&gt;</summary><content type="html">&lt;p&gt;Whilst &lt;a href=&quot;/2008/7/14/merb-and-datamapper-on-the-edge&quot;&gt;installing merb with edgy&lt;/a&gt; I&#8217;ve had run into some trouble building the &lt;code&gt;do_mysql&lt;/code&gt; gem on my Macbook Pro. It&#8217;s a 64-bit machine, so I installed the 64-bit version of MySQL, and that was my first mistake…&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;do_mysql&lt;/code&gt; gem installed fine. I didn&#8217;t realise there was a problem until I tried to run some migrations in a blank database:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ merb -i
 ~ Loaded DEVELOPMENT Environment...
 ~ loading gem 'dm-validations' ...
 ~ loading gem 'merb_datamapper' ...
 ~ loading gem 'merb-haml' ...
 ~ loading gem 'dm-core' ...
 ~ Merb::Orms::DataMapper::Connect block.
 ~ Connecting to database...
 ~ Checking if we need to use DataMapper sessions
 ~ Merb::Orms::DataMapper::Connect complete
 ~ Parent pid: 9932
 ~ Compiling routes...
&amp;gt;&amp;gt; Post.auto_migrate!
dyld: lazy symbol binding failed: Symbol not found: _mysql_init
  Referenced from: /Library/Ruby/Gems/1.8/gems/do_mysql-0.9.6/lib/do_mysql_ext.bundle
  Expected in: dynamic lookup

dyld: Symbol not found: _mysql_init
  Referenced from: /Library/Ruby/Gems/1.8/gems/do_mysql-0.9.6/lib/do_mysql_ext.bundle
  Expected in: dynamic lookup
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Oh dear. I did some Googling, and found a few articles from people having similar problems (e.g. Ben Novakovic, who had &lt;a href=&quot;http://blog.bmn.name/2008/02/issues-with-mysql-27-gem/&quot;&gt;problems with the mysql gem&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;It turns out that the gems cannot be compiled properly against a 64-bit version of MySQL, as they don&#8217;t take proper notice of the output of &lt;code&gt;mysql_config&lt;/code&gt;. Chris Cruft &lt;a href=&quot;http://cho.hapgoods.com/wordpress/?p=158&quot;&gt;investigated the problem&lt;/a&gt; and ended up working around it by using the 32-bit version.&lt;/p&gt;

&lt;p&gt;Setting the &lt;code&gt;ARCHFLAGS&lt;/code&gt; environment variable (as suggested in Chris&#8217;s article) to the output of &lt;code&gt;mysql_config --cflags&lt;/code&gt; didn&#8217;t work for me either, so I&#8217;ve gone for the same solution as Chris and installed the &lt;a href=&quot;http://dev.mysql.com/downloads/mysql/5.0.html#macosx-dmg&quot;&gt;i386 build of MySQL&lt;/a&gt;. The &lt;code&gt;do_mysql&lt;/code&gt; gem now works fine.&lt;/p&gt;

&lt;p&gt;If you check the comments on &lt;a href=&quot;http://cho.hapgoods.com/wordpress/?p=158&quot;&gt;Chris&#8217;s post&lt;/a&gt; you&#8217;ll see that some people have managed to get the &lt;code&gt;mysql&lt;/code&gt; gem to build properly against 64-bit MySQL. I&#8217;ve opted not to bother trying to shovel lots of command line switches at the problem because I&#8217;m &lt;a href=&quot;/2008/7/14/merb-and-datamapper-on-the-edge&quot;&gt;installing merb with edgy&lt;/a&gt;, and am getting by fine with the 32-bit version for now…&lt;/p&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-10-02:142</id>
    <published>2008-10-02T19:59:00Z</published>
    <updated>2008-10-02T20:27:04Z</updated>
    <category term="javascript"/>
    <category term="mac"/>
    <category term="web"/>
    <link href="http://effectif.com/2008/10/2/debugging-javascript-in-webkit" rel="alternate" type="text/html"/>
    <title>Debugging JavaScript in WebKit</title>
<summary type="html">&lt;p&gt;If you do much web development you&#8217;re probably a big fan of the &lt;a href=&quot;http://getfirebug.com&quot;&gt;Firebug&lt;/a&gt; plugin for Firefox. It really is a rare gem in the web developer&#8217;s arsenal. When I&#8217;m not developing I prefer to use &lt;a href=&quot;http://www.apple.com/safari/&quot;&gt;Safari&lt;/a&gt; &#8211; it seems faster and leaner, so I often find that I&#8217;ve got both browsers open at once.&lt;/p&gt;

&lt;p&gt;A couple of days ago something rather fantastic happened &#8211; Safari got a makeover&#8230;&lt;/p&gt;</summary><content type="html">&lt;p&gt;If you do much web development you&#8217;re probably a big fan of the &lt;a href=&quot;http://getfirebug.com&quot;&gt;Firebug&lt;/a&gt; plugin for Firefox. It really is a rare gem in the web developer&#8217;s arsenal. When I&#8217;m not developing I prefer to use &lt;a href=&quot;http://www.apple.com/safari/&quot;&gt;Safari&lt;/a&gt; &#8211; it seems faster and leaner, so I often find that I&#8217;ve got both browsers open at once.&lt;/p&gt;

&lt;p&gt;A couple of days ago something rather fantastic happened &#8211; Safari got a makeover.&lt;/p&gt;

&lt;p&gt;More specifically, the &lt;a href=&quot;http://webkit.org/blog/197/web-inspector-redesign/&quot;&gt;WebKit Web Inspector got a makeover&lt;/a&gt;, and now seems to be a viable competitor to Firebug. Here&#8217;s a quick screenshot of the debugger in action:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/webkit-debugger.png&quot; alt=&quot;Screenshot of the WebKit JavaScript debugger&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I&#8217;m also a big fan of the JavaScript code completion in the console. Here&#8217;s an example, with the code completion reminding me of the name of Prototype&#8217;s &lt;code&gt;_getElementsByXPath()&lt;/code&gt; function (which I rarely manage to type correctly in Firebug, but find very useful for debugging my XPath statements):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/webkit-code-completion.png&quot; alt=&quot;Screenshot of the WebKit's JavaScript code completion&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can see from the position of the cursor that I&#8217;ve typed &lt;code&gt;document._get&lt;/code&gt;, and that Web Inspector is suggesting &#8220;ElementsByXPath&#8221;. Pressing tab cycles between matching names, and the right cursor key completes the function, moving the cursor to the end of the line. It&#8217;s the little things…&lt;/p&gt;

&lt;p&gt;You can open the console at any time by pressing Esc (or clicking the second icon from the left at the bottom of the Web Inspector window).&lt;/p&gt;

&lt;p&gt;There are loads more features. Check out the &lt;a href=&quot;http://webkit.org/blog/197/web-inspector-redesign/&quot;&gt;WebKit blog post that covers the redesign&lt;/a&gt;, or get yourself over to the &lt;a href=&quot;http://nightly.webkit.org/&quot;&gt;WebKit nightly build page&lt;/a&gt; and download a copy (it doesn&#8217;t affect your existing installation of Safari, but can function as a drop in replacement, so there&#8217;s no risk and you can flip between them at will).&lt;/p&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-09-13:131</id>
    <published>2008-09-13T06:22:00Z</published>
    <updated>2008-09-13T12:37:45Z</updated>
    <category term="rails"/>
    <category term="sysadmin"/>
    <link href="http://effectif.com/2008/9/13/configure-the-expires-header-on-your-rails-site-with-nginx" rel="alternate" type="text/html"/>
    <title>Configure the Expires header for Rails under nginx</title>
<summary type="html">&lt;p&gt;The images, CSS and JavaScript files served up by your &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Rails&lt;/a&gt; application can usually be cached by the web browser, rather than being downloaded (or checked) every time the browser loads a page on your site. For a properly configured site the net effect is faster page load times and a vastly improved user experience. Rails gives you some help here, but it doesn&#8217;t happen automatically &#8211; you need to configure your web server to set the HTTP Expires header. This article explains how to configure the expires header for a Rails application running behind the &lt;a href=&quot;http://wiki.codemongers.com/&quot;&gt;nginx HTTP proxy server&lt;/a&gt;.&lt;/p&gt;</summary><content type="html">&lt;p&gt;The images, CSS and JavaScript files served up by your &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Rails&lt;/a&gt; application can usually be cached by the web browser, rather than being downloaded (or checked) every time the browser loads a page on your site. For a properly configured site the net effect is faster page load times and a vastly improved user experience. Rails gives you some help here, but it doesn&#8217;t happen automatically &#8211; you need to configure your web server to set the HTTP Expires header. This article explains how to configure the expires header for a Rails application running behind the &lt;a href=&quot;http://www.codemongers.com/&quot;&gt;nginx HTTP proxy server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nginx is popular amongst Rails developers for fronting a cluster of mongrel web servers. I&#8217;ll not say much more about it, on the assumption that this article probably won&#8217;t be of much interest to you if you&#8217;re not already using it.&lt;/p&gt;

&lt;h2&gt;So how do we speed your Rails site up?&lt;/h2&gt;

&lt;p&gt;When a web browser downloads a web page for the first time it also retrieves copies of the images, CSS and JavaScript files that are required to display it. On subsequent requests it will typically make a new connection to the web server to verify whether each of these files has been updated. When you know that the files won&#8217;t have changed you can gain a significant performance increase by telling the browser when it first downloads the file that it doesn&#8217;t need to check it again. More information on the Expires header (and other great performance enhancing tips) can be found amongst Yahoo&#8217;s &lt;a href=&quot;http://developer.yahoo.com/performance/rules.html#expires&quot;&gt;Best Practices for Speeding Up Your Web Site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Files that don&#8217;t change between requests are known in the Rails community as &#8220;static assets&#8221;. When running in production mode your Rails application will append all URLs for static assets with the file&#8217;s timestamp (in seconds since the 1 January 1970). Here&#8217;s an example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;http://effectif.com/stylesheets/blog.css?1221178271
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If we were to edit the file and redeploy the application the timestamp would be updated, and the browser would realise that it needed to download a new copy of the file.&lt;/p&gt;

&lt;p&gt;The Rails &lt;a href=&quot;http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html&quot;&gt;documentation for AssetTagHelper&lt;/a&gt; covers how to configure a far future expires header under Apache (scroll down to the section entitled &#8220;Using asset timestamps&#8221;).&lt;/p&gt;

&lt;p&gt;The equivalent nginx configuration looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if ($request_uri ~* &quot;.(ico|css|js|gif|jpe?g|png)\?[0-9]+$&quot;) {
    expires max;
    break;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that we&#8217;re matching suitable file types, but only setting the expires header for files that have a timestamp appended.&lt;/p&gt;

&lt;p&gt;Here&#8217;s the configuration again, in the context of a &lt;code&gt;location&lt;/code&gt; directive:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;location / {
    root /var/apps/myapp/current/public;
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect false;

    if ($request_uri ~* &quot;.(ico|css|js|gif|jpe?g|png)\?[0-9]+$&quot;) {
        expires max;
        break;
    }
    if (-f $request_filename) {
        break;
    }
    if (-f $request_filename/index.html) {
        rewrite (.*) $1/index.html break;
    }
    if (-f $request_filename.html) {
        rewrite (.*) $1.html break;
    }

    proxy_pass http://mymongrels;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Testing your configuration&lt;/h2&gt;

&lt;p&gt;It&#8217;s all well and good sticking the config in nginx and restarting it, but it&#8217;s nice to know that the stuff works, right? If you&#8217;re not already using it, get a copy of &lt;a href=&quot;http://www.getfirefox.com/&quot;&gt;Firefox&lt;/a&gt; and install &lt;a href=&quot;http://getfirebug.com/&quot;&gt;Firebug&lt;/a&gt;. Then install Yahoo&#8217;s &lt;a href=&quot;http://developer.yahoo.com/yslow/&quot;&gt;YSlow&lt;/a&gt; plugin for Firebug.&lt;/p&gt;

&lt;p&gt;I recommend that you read Yahoo&#8217;s &lt;a href=&quot;http://developer.yahoo.com/yslow/&quot;&gt;introduction to YSlow&lt;/a&gt; to learn how to use it. You can check whether the expires header is set for all the assets required by your web page using the Components view, a screenshot of which I&#8217;ve cheekily purloined here:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://us.i1.yimg.com/us.yimg.com/i/rt/yslow/compsview.png&quot; alt=&quot;YSlow copmonents view, showing the value of the expires header&quot; /&gt;&lt;/p&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-06-18:67</id>
    <published>2008-06-18T22:15:00Z</published>
    <updated>2008-06-19T06:38:18Z</updated>
    <category term="erlang"/>
    <link href="http://effectif.com/2008/6/18/learning-erlang-screencasts" rel="alternate" type="text/html"/>
    <title>Learning Erlang with the Pragmatic Programmer Screencasts</title>
<summary type="html">&lt;p&gt;I&#8217;ve been meaning to get around to playing with &lt;a href=&quot;http://www.erlang.org/&quot; title=&quot;Erlang&quot;&gt;Erlang&lt;/a&gt; for a while now. Tonight I set aside 30 minutes to watch the first of the Pragmatic Programmer&#8217;s Erlang Screencasts by Kevin Smith: &lt;a href=&quot;http://pragprog.com/screencasts/v-kserl/erlang-by-example&quot; title=&quot;The Pragmatic Bookshelf | Erlang By Example&quot;&gt;Erlang By Example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each screencast is very reasonably priced at $5 a piece. I bought the first one, in which you learn how to build a simple chat system. I downloaded it immediately, excited to think that in only 30 minutes time I&#8217;d know lots more about Erlang than I did when I woke up this morning.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I&#8217;ve been meaning to get around to playing with &lt;a href=&quot;http://www.erlang.org/&quot; title=&quot;Erlang&quot;&gt;Erlang&lt;/a&gt; for a while now. Tonight I set aside 30 minutes to watch the first of the Pragmatic Programmer&#8217;s Erlang Screencasts by Kevin Smith: &lt;a href=&quot;http://pragprog.com/screencasts/v-kserl/erlang-by-example&quot; title=&quot;The Pragmatic Bookshelf | Erlang By Example&quot;&gt;Erlang By Example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each screencast is very reasonably priced at $5 a piece. I bought the first one, in which you learn how to build a simple online chat system (see &lt;a href=&quot;http://screencasts.pragprog.com/v-kserl-intro.mov&quot;&gt;the introductory video&lt;/a&gt; for an overview of what&#8217;s covered in the entire series). I downloaded it immediately, excited to think that in only 30 minutes time I&#8217;d know lots more about Erlang than I did when I woke up this morning.&lt;/p&gt;

&lt;h2&gt;Installing Erlang&lt;/h2&gt;

&lt;p&gt;While the video was downloading I kicked off the installation of Erlang on my Mac with &lt;a href=&quot;http://www.macports.org/&quot; title=&quot;The MacPorts Project -- Home&quot;&gt;MacPorts&lt;/a&gt;&#8230;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo port install -d erlang
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and grabbed a copy of the Erlang TextMate bundle:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cd ~/Library/Application\ Support/TextMate/Bundles
$ svn co https://macromates.com/svn/Bundles/trunk/Bundles/Erlang.tmbundle
$ osascript -e 'tell app &quot;TextMate&quot; to reload bundles'
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Following Along&lt;/h2&gt;

&lt;p&gt;If you&#8217;ve never done any Erlang at all (and to all intents and purposes, I hadn&#8217;t) then you&#8217;ll see quite a bit of new syntax during this screencast. It won&#8217;t all be explained to you straight away, but don&#8217;t worry about that as it doesn&#8217;t detract from what&#8217;s going on.&lt;/p&gt;

&lt;p&gt;Kevin edits his code in Emacs and compiles it with some magic incantation using an Erlang interpreter that is running in an Emacs buffer. It seems to be assumed that when you start watching the screencast you already know how to compile a code module. Worry not; if you&#8217;re trying to run the examples in a terminal, you should be able to get by with something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ erl
Erlang (BEAM) emulator version 5.6.2 [source] [async-threads:0] [kernel-poll:false]

Eshell V5.6.2  (abort with ^G)
1&amp;gt; c('message_router').
{ok,message_router}
2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#8220;c&#8221; means &#8220;compile&#8221;. Note the &#8220;.&#8221; character, which is used as a terminator in Erlang (it&#8217;s strange that this should seem noteworthy, given that this is exactly how we use it in English!). Incidentally, if you&#8217;re an Emacs fan then Kevin has written up his &lt;a href=&quot;http://weblog.hypotheticalabs.com/?p=265&quot;&gt;Emacs configuration&lt;/a&gt; on his blog.&lt;/p&gt;

&lt;p&gt;Kevin does a good job of giving you a the flavour of concurrent programming in Erlang. Concentrating on the syntax would have distracted from the overall message, without contributing much to your understanding (and if you really must know the syntax, there&#8217;s plenty of &lt;a href=&quot;http://www.erlang.org/doc/&quot;&gt;Erlang documentation&lt;/a&gt; that will fill in the gaps).&lt;/p&gt;

&lt;p&gt;Once the simple chat system was put together the code was refactored. While recompiling my edits and fixing my typos I picked up some useful tips on using the interactive interpreter.&lt;/p&gt;

&lt;p&gt;So in summary, I enjoyed it. I&#8217;m a long way from being able to claim that I know Erlang, but I know hell of a lot more about it than I did half an hour ago, and you can&#8217;t say fairer than that! For $5 a throw, the screencasts are a bargain.&lt;/p&gt;

&lt;h2&gt;Useful Links&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;http://pragprog.com/screencasts/v-kserl/erlang-by-example&quot; title=&quot;The Pragmatic Bookshelf | Erlang By Example&quot;&gt;Erlang By Example Screencasts&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.erlang.org/starting.html&quot; title=&quot;Getting started&quot;&gt;Getting Started with Erlang&lt;/a&gt; (also available as a &lt;a href=&quot;http://www.erlang.org/doc/pdf/getting_started.pdf&quot; title=&quot;Getting Started with Erlang&quot;&gt;PDF&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://weblog.hypotheticalabs.com/&quot; title=&quot;Hypothetical Labs&quot;&gt;Kevin Smith&#8217;s blog&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-05-24:49</id>
    <published>2008-05-24T11:05:00Z</published>
    <updated>2008-05-27T09:13:03Z</updated>
    <category term="rails"/>
    <link href="http://effectif.com/2008/5/24/database-yml-should-be-checked-in" rel="alternate" type="text/html"/>
    <title>database.yml should be checked in</title>
<summary type="html">&lt;p&gt;It&#8217;s generally acknowledged to be good practice to keep your production &lt;code&gt;database.yml&lt;/code&gt; file away from your version control system. Why? So that people snooping around your version control system don&#8217;t find your database password. There are plenty of examples out there that tell you how to achieve this with &lt;a href=&quot;http://capify.org&quot;&gt;Capistrano&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I&#8217;m talking about the &lt;code&gt;database.yml&lt;/code&gt; file that you use during development.&lt;/p&gt;</summary><content type="html">&lt;p&gt;It&#8217;s generally acknowledged to be good practice to keep your production &lt;code&gt;database.yml&lt;/code&gt; file away from your version control system. Why? So that people snooping around your version control system don&#8217;t find your database password. There are plenty of examples out there that tell you how to achieve this with &lt;a href=&quot;http://capify.org&quot;&gt;Capistrano&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I say that &lt;code&gt;database.yml&lt;/code&gt; should be checked in, I&#8217;m talking the version that you use during development.&lt;/p&gt;

&lt;p&gt;Teams often resist sharing database config files so they can keep their individual database passwords private and specify operating system specific settings (such as the location of MySQL&#8217;s socket file).&lt;/p&gt;

&lt;p&gt;Can you share configuration between different developers? A couple of approaches spring to mind.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don&#8217;t store &lt;code&gt;database.yml&lt;/code&gt; in version control, but create &lt;code&gt;database.yml.example&lt;/code&gt; instead. Force each developer to edit it themselves when they check out the code.&lt;/li&gt;
&lt;li&gt;Persuade your team mates to use the same password and check that in.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither is satisfactory. When I check my code out I expect it to just work, and I&#8217;m sure as hell not going to give anybody who can access the source a free run at my laptop&#8217;s database.&lt;/p&gt;

&lt;p&gt;Read on for my solution.&lt;/p&gt;

&lt;p&gt;So how do you check in &lt;code&gt;database.yml&lt;/code&gt; without storing your password in version control? I&#8217;m using MySQL at the moment. As we&#8217;ve &lt;a href=&quot;/2008/3/29/opening-ruby-gems-in-textmate&quot;&gt;already established&lt;/a&gt;, I&#8217;m a lazy &lt;a href=&quot;/tags/git&quot;&gt;git&lt;/a&gt;, so I hate having to specify a password when I connect to MySQL. I&#8217;ve set my local database password in the &lt;code&gt;~/.my.cnf&lt;/code&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cat ~/.my.cnf
[mysql]
user = root
password = gumo4Gum
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It&#8217;s a good idea to set the permissions on that file so that only you can read it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ chmod 600 ~/.my.cnf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now I can just connect without any trouble. Neat.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mysql myapp_development
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.0.45-Debian_1ubuntu3.3-log Debian etch distribution

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once you&#8217;ve gone to the trouble to make it easy to login to MySQL, why not let Rails connect using the same settings when it starts up?&lt;/p&gt;

&lt;p&gt;This is how I&#8217;ve been doing it (&#8220;Oh my God! He&#8217;s used Python!&#8221;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%
def get_mysql_password
  script =&amp;lt;&amp;lt;EOF
import ConfigParser
parser = ConfigParser.SafeConfigParser()
parser.read(&quot;#{ENV['HOME']}/.my.cnf&quot;)
print parser.get(&quot;mysql&quot;, &quot;password&quot;)
EOF
  %x(python -c '#{script}')
end
%&amp;gt;

mysql: &amp;amp;mysql
  adapter: mysql
  username: root
  password: &amp;lt;%= get_mysql_password %&amp;gt;
  host: localhost
  socket: &amp;lt;%= [
    '/var/lib/mysql/mysql.sock',
    '/var/run/mysqld/mysqld.sock',
    '/tmp/mysqld.sock',
    '/tmp/mysql.sock',
  ].detect { |socket| File.exist?(socket) } %&amp;gt;

development:
  database: myapp_development
  &amp;lt;&amp;lt;: *mysql

test: &amp;amp;test
  database: myapp_test
  &amp;lt;&amp;lt;: *mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Before I explain the Python, note the sneaky socket trick that allows Rails to try lots of different locations for MySQL&#8217;s Unix socket&#8212;it allows you to develop the same app on a Mac, or various flavours of Linux/BSD, without editing &lt;code&gt;database.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So then. This Python code. I suspect there&#8217;s a rather good way of doing it in Ruby, but when I first started developing with Rails I&#8217;d just spent five years doing Python full time, and I knew that the &lt;a href=&quot;http://docs.python.org/lib/module-ConfigParser.html&quot; title=&quot;9.2 ConfigParser -- Configuration file parser&quot;&gt;ConfigParser module&lt;/a&gt; was designed to read files like &lt;code&gt;~/.my.cnf&lt;/code&gt;. So I used it, and it stuck.&lt;/p&gt;

&lt;h3&gt;Send me improvements!&lt;/h3&gt;

&lt;p&gt;How would you do it?&lt;/p&gt;

&lt;p&gt;Can you write a neat snippet of Ruby that achieves the same thing as the Python code?&lt;/p&gt;

&lt;p&gt;If anybody knows a way to make this trick work with &lt;a href=&quot;http://www.merbivore.com/&quot; title=&quot;Merb | Looking for a hacker's framework?&quot;&gt;Merb&lt;/a&gt; I&#8217;d love to hear it. Last time I tried, Merb wasn&#8217;t processing the database config file with &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/&quot; title=&quot;erb: Ruby Standard Library Documentation&quot;&gt;erb&lt;/a&gt;&#8230;&lt;/p&gt;</content>  </entry>
  <entry xml:base="http://effectif.com/">
    <author>
      <name>graham</name>
    </author>
    <id>tag:effectif.com,2008-04-24:27</id>
    <published>2008-04-24T23:13:00Z</published>
    <updated>2008-05-24T11:06:46Z</updated>
    <category term="automation"/>
    <category term="git"/>
    <category term="rails"/>
    <link href="http://effectif.com/2008/4/24/easy-git-svn-for-rails" rel="alternate" type="text/html"/>
    <title>Easy git-svn for Rails (or "git-me-up")</title>
<summary type="html">&lt;p&gt;I&#8217;ve been trying to persuade &lt;code&gt;git-svn&lt;/code&gt; to work properly with Rails plugins that have been installed via &lt;code&gt;svn:externals&lt;/code&gt;. Whilst working out how to do it I stumbled across several great articles, but I couldn&#8217;t get any of the solutions presented to work perfectly. &lt;a href=&quot;http://blog.alieniloquent.com/2008/03/08/git-svn-with-svnexternals/&quot; title=&quot;Alieniloquent: Blog&quot;&gt;Samuel Tesla&#8217;s article&lt;/a&gt; is especially informative, but for a long time I couldn&#8217;t stop &lt;code&gt;git-svn&lt;/code&gt; from trying to commit Git metadata back into my Subversion repository&#8230;&lt;/p&gt;

&lt;p&gt;After a bit of head scratching I got it sorted, and wrapped the process up in a little utility called &lt;a href=&quot;/2008/4/24/easy-git-svn-for-rails&quot; title=&quot;Easy git-svn for Rails (or git-me-up)&quot;&gt;git-me-up&lt;/a&gt;.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I&#8217;ve been trying to persuade &lt;code&gt;git-svn&lt;/code&gt; to work properly with Rails plugins that have been installed via &lt;code&gt;svn:externals&lt;/code&gt;. Whilst working out how to do it I stumbled across several great articles, but I couldn&#8217;t get any of the solutions presented to work perfectly. &lt;a href=&quot;http://blog.alieniloquent.com/2008/03/08/git-svn-with-svnexternals/&quot; title=&quot;Alieniloquent: Blog&quot;&gt;Samuel Tesla&#8217;s article&lt;/a&gt; is especially informative, but for a long time I couldn&#8217;t stop &lt;code&gt;git-svn&lt;/code&gt; from trying to commit Git metadata back into my Subversion repository&#8230;&lt;/p&gt;

&lt;p&gt;After a bit of head scratching I got it sorted, and wrapped the process up in a little utility called &lt;a href=&quot;/2008/4/24/easy-git-svn-for-rails&quot; title=&quot;Easy git-svn for Rails (or git-me-up)&quot;&gt;git-me-up&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;What does git-me-up do?&lt;/h2&gt;

&lt;p&gt;In a nutshell, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Clones the latest version of the source in your Subversion repository into a new local Git repository. It doesn&#8217;t import any history for performance reasons.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Recreates empty directories that &lt;code&gt;git-svn&lt;/code&gt; ignored (but that your app might need).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Converts any &lt;code&gt;svn:ignore&lt;/code&gt; Subversion properties into entries in the &lt;code&gt;.git/info/exclude&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checks for Rails plugins installed via &lt;code&gt;svn:externals&lt;/code&gt; and clones them into their own git repositories. Each external plugin is then symlinked into &lt;code&gt;vendor/plugins&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creates a local working branch.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In other words, it takes all the hard work out of creating a local Git repository that points at your Subversion repo.&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;You need &lt;code&gt;git-svn&lt;/code&gt; installed first. On a Mac, install &lt;a href=&quot;http://www.macports.org/&quot; title=&quot;The MacPorts Project -- Home&quot;&gt;MacPorts&lt;/a&gt; and then type:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo port install -u git-core +svn +bash_completion
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When the package has finished building run these commands to benefit from bash completion:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cp /opt/local/etc/bash_completion.d/git ~/.git-bash-completion.sh
$ echo '[ -f ~/.git-bash-completion.sh ] &amp;amp;&amp;amp; . ~/.git-bash-completion.sh' \
    &amp;gt;&amp;gt; ~/.bashrc
$ . ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On Ubuntu Linux:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo apt-get install git-svn
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Installing git-me-up&lt;/h3&gt;

&lt;p&gt;The source is hosted on &lt;a href=&quot;http://github.com/gma/git-me-up/tree&quot; title=&quot;gma's git-me-up at master &amp;amp;mdash; GitHub&quot;&gt;GitHub&lt;/a&gt;. You can either &lt;a href=&quot;http://github.com/gma/git-me-up/tarball/master&quot;&gt;download the tar ball&lt;/a&gt; or pull the source with &lt;code&gt;git&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git clone git://github.com/gma/git-me-up.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;Makefile&lt;/code&gt; will install it into &lt;code&gt;/usr/local/bin&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cd git-me-up
$ sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Using git-me-up&lt;/h2&gt;

&lt;p&gt;This is pretty easy. Let&#8217;s start with an empty directory so you can see what gets created:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ pwd
/home/graham/test
$ git-me-up https://myhost.com/path/to/myproject/trunk myproject
git-me-up: finding latest revision of https://myhost.com/path/to/myproject/trunk
git-me-up: creating git repository in /home/graham/test/myproject
git-me-up: making empty directory: app/views/comments
git-me-up: making empty directory: app/views/pages
git-me-up: making empty directory: components
git-me-up: making empty directory: log
git-me-up: making empty directory: test/mocks/development
git-me-up: making empty directory: test/mocks/test
git-me-up: making empty directory: tmp/cache
git-me-up: making empty directory: tmp/pids
git-me-up: making empty directory: tmp/sessions
git-me-up: making empty directory: tmp/sockets
git-me-up: setting ignored files from subversion (this can take a while)
git-me-up: checking for plugins installed with svn:externals
git-me-up: finding latest revision of https://myhost.com/path/to/plugin/trunk
git-me-up: creating git repository in /home/graham/test/myproject/../plugins/site_theme
git-me-up: setting ignored files from subversion (this can take a while)
git-me-up: symlinking site_theme into myproject/vendor/plugins
git-me-up: creating 'work' branch on /home/graham/test/myproject
Switched to a new branch &quot;work&quot;
$ ls
myproject/  plugins/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you&#8217;re running on a Mac you may see this error message part way through the output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;No such file or directory: PROPFIND request failed on '/app': Could not open the requested SVN filesystem at /opt/local/bin/git-svn line 1872
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It appears as though the &lt;code&gt;git svn show-ignore&lt;/code&gt; command (using the current version of git from MacPorts&#8211;version 1.5.4.4) doesn&#8217;t work on my Mac. If you see this too don&#8217;t worry about it; it will only mean that Git doesn&#8217;t know which files you&#8217;ve chosen to ignore in your Subversion repo.&lt;/p&gt;

&lt;p&gt;Now we&#8217;re ready to use our new Git repo. Note that we&#8217;ve got a work branch by default:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cd myproject
$ git branch
  master
* work
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you want to call the branch something else, set the &lt;code&gt;BRANCH&lt;/code&gt; environment variable before you run &lt;code&gt;git-me-up&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Samuel &lt;a href=&quot;http://blog.alieniloquent.com/2008/03/08/git-svn-with-svnexternals/&quot; title=&quot;Alieniloquent: Blog&quot;&gt;recommends a good work flow&lt;/a&gt; for working with Subversion (skip down to &#8220;Step Five: Using this thing&#8221;) . When you want to update your working branch from subversion, try this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git checkout master  # switches to local master branch
$ git svn rebase       # pulls recent changes from subversion
$ git checkout work    # switches to local work branch
$ git rebase master    # pulls recent changes to master into work
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&#8217;s a good description of &lt;a href=&quot;http://jbowes.dangerouslyinc.com/2007/01/26/git-rebase-keeping-your-branches-current/&quot; title=&quot;James Bowes &amp;amp;rsaquo; git rebase: keeping your branches current&quot;&gt;why rebase is so great&lt;/a&gt; on James Bowes&#8217;s blog.&lt;/p&gt;

&lt;p&gt;When you want to commit changes back into subversion:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git checkout work    # you were probably there already
... run your tests ...
$ git commit -a -m &quot;Blah blah blah...&quot;
$ git checkout master
$ git merge work
$ git svn rebase       # checks for fresh changes in subversion
... run tests again if rebase pulled any changes ...
$ git svn dcommit
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make sure that you use &lt;code&gt;git svn rebase&lt;/code&gt; to pull upstream changes into your &lt;code&gt;master&lt;/code&gt; branch before you check in.&lt;/p&gt;

&lt;h3&gt;What git-me-up does with svn:externals plugins&lt;/h3&gt;

&lt;p&gt;The Rails app that I cloned above has a plugin that is installed via &lt;code&gt;svn:externals&lt;/code&gt; called &lt;code&gt;site_theme&lt;/code&gt;. I usually use &lt;a href=&quot;http://piston.rubyforge.org/&quot; title=&quot;Piston ~ Easy Vendor Branch Management&quot;&gt;piston&lt;/a&gt; or &lt;a href=&quot;http://evil.che.lu/projects/braid&quot; title=&quot;evil.che.lu - projects - braid&quot;&gt;braid&lt;/a&gt; to install Rails plugins, but when the plugin is stored on my own servers I&#8217;ve always preferred to pull in the source directly by setting the &lt;code&gt;svn:externals&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;I initially tried to convert &lt;code&gt;svn:externals&lt;/code&gt; into Git submodules, but it didn&#8217;t work well. No matter what I tried, I couldn&#8217;t persuade &lt;code&gt;git-svn&lt;/code&gt; to ignore the submodule when committing back to Subversion. It turns out that it&#8217;s easy to achieve with a symbolic link.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git-me-up&lt;/code&gt; cloned the &lt;code&gt;site-theme&lt;/code&gt; plugin into a Git repository, then made it available with a symbolic link:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ls
myproject/  plugins/
$ ls plugins/
site_theme/
$ ls -l myproject/vendor/plugins/ | grep site_theme
lrwxrwxrwx  1 graham development   32 Apr 24 18:41 site_theme -&amp;gt; ../../../plugins/site_theme/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To avoid checking the symlink into Subversion, &lt;code&gt;git-me-up&lt;/code&gt; told Git to ignore it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ grep site_theme myproject/.git/info/exclude
/vendor/plugins/site_theme
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The result is a trouble free alternative to submodules that allows you to integrate Git with &lt;code&gt;svn:externals&lt;/code&gt; in your Subversion repo.&lt;/p&gt;</content>  </entry>
</feed>
