Sass filters without style tags

A recent backwards incompatible change in sass means that when using a SASS filter in HAML it generates <style> tags for you. This behaviour is not great for inline stylesheets where you are using a media filter.

To restore the original behaviour, drop this into your ruby script:

module Haml::Filters
  remove_const(:Sass)

  module Sass
    include Haml::Filters::Base

    def render(text)
      ::Sass::Engine.new(text).render
    end
  end
end

Programatically get the version of an egg

The documentation for pkg_resources does not make it 100% obvious how to get the current version of a Python egg. To do so:
pkg_resources.get_distribution(egg_name).version

Tee and exec at the same time

Print a dot for every four lines of output, also logging to output.log.
LOGFILE="output.log"
LOGPIPE="/tmp/$$.tmp"
trap "rm -f $LOGPIPE" EXIT
mknod "$LOGPIPE" p
sudo tee <"$LOGPIPE" -a "$LOGFILE" | awk '{ if (NR % 4 == 0) printf "."; system("") }' &
exec 1>>"$LOGPIPE" 2>&1

Ruby lambda in a different scope

When you create a ruby lambda it is bound the the scope it was defined in. Therefore, if you create it in the class scope, you cannot access instance variables or protected instance methods.

To execute the lambda in your instance’s scope, use the instance_eval method:

Git Post Commit Hook to Play “Hallelujah” After a Commit

Inspired by Brandon Keepers, what follows is a self contained script that will play a single Hallelujah after you commit.

To make it work, place it in your repository’s .git/hooks directory as post-commit and chmod it executable.

NSThread a selector with multiple arguments

One of the curious absences in the Mac SDK is the ability to invoke a thread that targets a method with multiple arguments.

If you want to use [+ NSThread detachNewThreadSelector:toTarget:withObject:] you either have to refactor your function to a) take a collection like an NSArray or NSDictionary or b) call an intermediary function which then makes the call on that thread. You would half-expect a method like [+ NSThread detachNewThreadSelector:toTarget:withObjects:] to exist.

In order to get round this, I created the SEThreadInvocation object which blends a NSThread and a NSInvocation together.

You can then call your method like so:

Duplicate NSManagedObject with NSFetchedResultsController

While working on WineLedger for the iPhone I spent a lot of time trying to work out why duplicate ‘ghost’ entries would sometimes appear when merging two NSManagedObjectContexts:

As I inserted a new entry, two copies would appear: One would remain stuck, while the second would correctly update when you edited it. As soon as the app was restarted the ghost entry would miraculously vanish. To add to the confusion, this behaviour would only occur when I used the camera to take a photo via UIImagePickerController.

After a lot of trial and error I finally discovered that the main view was being unloaded from memory as soon as you took a picture. Using the camera causes a huge memory spike as the phone stores the high resolution image.

This shouldn’t be a problem, but as the view loads back into memory the NSFetchedResultsController does a fetch. This initial fetch loads the full set of results (including the freshly inserted object), and then the delegate methods proceed to perform a nice slide animation when the two contexts are merged, inserting the object into the UITableView again.

The fix for this problem is to insert a guard to check to see if the NSFetchedResultsController already has some managed objects before it does a fetch on viewDidLoad.

if (self.fetchedResultsController.fetchedObjects == nil)
{
    NSError * error = nil;
	
    if (![self.fetchedResultsController performFetch:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }
}

Running Resque / Rake Tasks with Monit

script/consumer:
#!/usr/bin/env ruby

def pidfile
  @pidfile ||= File.expand_path(File.join('..', '..', 'tmp', 'pids', 'consumer.pid'),  __FILE__)
end

case ARGV.first
  when 'start' then
    require 'rubygems'
    require 'rake'
    
    ENV['QUEUE'] ||= '*'
    ENV['RAILS_ENV'] ||= 'production'

    load(File.expand_path(File.join('..', '..', 'Rakefile'),  __FILE__))

    Process.detach(consumer = Process.fork do
      begin
        Rake::Task['environment'].invoke
        Rake::Task['resque:work'].invoke
      rescue Exception => e
        $stdout.puts([e, *e.backtrace].join($/))
      end
    end)

    %x(echo "#{consumer}" > #{pidfile})

  when 'stop' then
    %x(kill `cat #{pidfile}`)
    %x(rm #{pidfile})
    
  else
    
    puts 'usage: script/consumer start|stop'
end

Reinstalling git on Snow Leopard

If you are reinstalling git for Snow Leopard, don’t forget to nuke MacPorts. Leaving an old version on your machine will cause the git compile process to spit out various incorrect architecture errors:

ld: warning: in /opt/local/lib/libz.dylib, file is not of required architecture
ld: warning: in /opt/local/lib/libiconv.dylib, file is not of required architecture

Remove MacPorts with the terrifyingly brutal:

sudo rm -rf \
    /opt/local \
    /etc/manpaths.d/macports \
    /etc/paths.d/macports \
    /Applications/DarwinPorts \
    /Applications/MacPorts \
    /Library/LaunchDaemons/org.macports.* \
    /Library/Receipts/DarwinPorts*.pkg \
    /Library/Receipts/MacPorts*.pkg \
    /Library/StartupItems/DarwinPortsStartup \
    /Library/Tcl/darwinports1.0 \
    /Library/Tcl/macports1.0

Once you have removed MacPorts, the compile process should be as easy as:

make configure 
./configure --prefix=/usr/local
make prefix=/usr/local all
sudo make prefix=/usr/local install

git config --global user.name "[user.name]" 
git config --global user.email "[user.email]"

And when you are done, don’t forget this little gem for adding pretty colours to your git output:

git config --global color.ui "auto"

Reinstalling All Native Gems on Snow Leopard

After installing Snow Leopard all of my native code gems broke horribly, complaining that they were now on the wrong architecture. To re-install every gem automatically, I used:

sudo gem list | awk '{print $1}' | xargs sudo gem install