Jay Caines-Gooby

Back in 5 mins

Archived Posts

Displaying posts 1 - 10 of 10

Max was writing some rake tasks today and it reminded me to finish off this post which has sat unfinished for months.

Bob’s been porting Charanga’s music-teaching desktop software from PC to Macs. The port is based on the work we’ve done in the past couple of years for our online products and means that we can now have an online offering as well as PC and Mac desktop products, all built from the same codebase.

We’ve initially released three products with a further eight to follow.

Each of these 11 products will come as either a hybrid DVD or CDROM, with both the PC and Mac version on, but only visible to relevant platform. Lots of CD burning products for Macs out there make it easy to burn these kind of ROMs, but the big problem is that they all need to be made manually. And with 11 different products, that’s 11 different manual processes, any one mistake of which could ruin the master that we’re sending off to the publisher.

It occurred to me that we deploy our web apps with a single invocation:

	cap deploy STAGE=production

So why not do the same with the burning of the CDROMs? Entirely automate the process so there’s no room for manual error…

Rake

Rake – Ruby Make – operates on a rakefile which defines lists of tasks, with optional requisite tasks that must first be completed. Given that building and burning the ROMs consists of a bunch of identical steps, differentiated only by the files that need to go on the relevant product’s CDROM or DVD, it sounds like an ideal tool, so let’s go ahead and build a skeleton rakefile…

There are a bunch of files that are common to all the products, plus product specific files. These get pulled out of subversion (yes, yes, we’re only just migrating to git), copied into the product filestructure, the PC content gets added, the hybrid ISO image gets created and then we use this to physically burn the ROM.

If we make each preceding step a prerequisite of the parent task, we can break the the steps down into nice self-contained pieces and have a single task invoke all the others below it.

Ultimately, I wanted to be able to stick a DVD or CDROM into the drive and then call:

	rake burn_electric_guitar_coach_dvd

And have a finished hybrid DVD pop out, fresh off the press.

Break it down

The tasks in the rakefile are roughly as follows:


# Define various constants

# The source repository
REPOSITORY_URL="http://svn.myrepo.com/trunk"

# Where we'll do all this stuff
BUILD_DIR = File.expand_path "/Users/jay/Work/music coach" 

# The copy of the remote repository we'll use locally
CACHED_COPY = "#{BUILD_DIR}/svn-cached-copy"

# shared by all products
COMMON_CONTENT = "'#{PRODUCTION_FOLDER}/help'"

PRODUCTS = {  

  # specific properties for each product
  :guitar_deluxe => { 

    # Mac content
    :volume_name => "Play Electric Guitar",
    :app_folder_name => "Play Electric Guitar v3.0",
    :logo => "guitar deluxe.jpg",    
    :modules => "First Lessons For Guitar", "Guitar Improver", "Guitar Songs And Styles", "Solo Guitar Performance Pieces", "Master Rock Power Chords", "Chord miner"]
    
    # PC content
    :pc_iso => "guitar_deluxe.cdr",
    :pc_iso_volume_name => "GuitarDeluxe",
    
    # details of which files to hide from a PC on a Mac and vice versa
    :hide_hfs => "{Common,Player,program files,Redist,System32,*.exe,*.inf,*.msi,*.ini}",
    :hide_joliet => "{.background,.DS_Store,.Trashes,.com.apple.timemachine.supported,.fseventsd,Play Piano v3.0,Applications}"
  },
  
  :electric_guitar_deluxe => {
    # ...
  },
  
  :piano_deluxe => {
    # ...
  },
  
  
  :play_piano => {
    # ...
  }
  
  # and so on for 7 other products
  
}

# A couple of helpers...
              
# Input helper - gets input from user
def ask message
puts message
STDIN.gets.chomp
end

# Symol helper - converts a string to a symbol
# "Blah blah foo".symbolize = :blah_blah_foo
class String
  def symbolize
    self.downcase.split(" ").join("_").to_sym
  end
end

# Now the tasks themselves

# The default task (runs when rake is called without arguments)
task :default => :create_repository

# The create_repository task - builds a local copy of the repository for us to work from 
desc "Create a cached copy folder where the repository will reside which we can then svn export the installer files from"
task :create_repository do 
    
  # the production files
  unless File.exists?("#{CACHED_COPY}")
    puts "Creating initial cached copy of the repository"
    
    svn_user ||= ask("Enter your svn username: ")
    svn_password ||= ask("Enter your svn password: ")
    sh "svn checkout --username #{svn_user} --password #{svn_password} '#{REPOSITORY_URL}' '#{CACHED_COPY}'"
  end
end

desc "Update the cached copy of the respository to get latest versions of files"
task  :update_repository => [:create_repository] do

  puts "Updating #{topics_product} production files"
  sh "cd '#{CACHED_COPY}'; #{SVN_PATH}/svn update"
  
end
  
# Be DRY about the task creation and use some string to symbol magic to dynamically create the tasks
# 
# This makes three tasks per product (11 products = 33 tasks :)
#
#
# 1. build_#{topics_product}_dmg (with a prerequisite on 1.)
#
# 2. build_#{topics_product}_dvd (with a prerequisite on 2.)
#
# 3. burn_#{topics_product}_dvd (with a prerequisite on 3.)

PRODUCTS.each do |topics_product, data|
    
  desc "Build #{topics_product} for Mac .dmg"
  task "build_#{topics_product}_dmg".symbolize => [:update_repository] do

    # We need to clean up .dmg and any old build folders
    # and make sure no other dmg of the same name is mounted
    sh "sudo umount -f '/Volumes/#{PRODUCTS[topics_product][:app_folder_name]}'" if File.exists?("/Volumes/#{PRODUCTS[topics_product][:app_folder_name]}")
    sh "sudo rm -rf '/Volumes/#{PRODUCTS[topics_product][:app_folder_name]}'" if File.exists?("/Volumes/#{PRODUCTS[topics_product][:app_folder_name]}")
    sh "rm '/tmp/#{PRODUCTS[topics_product][:dmg]}'" if File.exists?("/tmp/#{PRODUCTS[topics_product][:dmg]}")
    sh "rm '/tmp/#{PRODUCTS[topics_product][:app_folder_name]}.dmg'" if File.exists?("/tmp/#{PRODUCTS[topics_product][:app_folder_name]}.dmg")    
      
    # Take the read-only master .dmg that has the backgrounds, .DS_Store and folder stubs
    # and make a copy of it to /tmp, then resize the copy so we can add our content, then mount it
    sh "hdiutil convert '#{CACHED_COPY}/development/mac installer/#{PRODUCTS[topics_product][:dmg]}' -format UDRW -o '/tmp/#{PRODUCTS[topics_product][:dmg]}'"
    sh "hdiutil resize -size 4g '/tmp/#{PRODUCTS[topics_product][:dmg]}'; hdiutil attach '/tmp/#{PRODUCTS[topics_product][:dmg]}'; sleep 5"
        
    # The new, writable dmg is now mounted at '/Volumes/#{PRODUCTS[topics_product][:app_folder_name]}'
    # and it's where we'll assemble the rest of the dmg
    tmp_product_dir = "/Volumes/#{PRODUCTS[topics_product][:app_folder_name]}/#{PRODUCTS[topics_product][:app_folder_name]}"
              
    # Sort the permissions out (99 is the magic OS X user and group that appears to be owned by the current user when viewed; i.e. my uid is 6, but when I ls -l a file owned 99:99 it appears as 6:6)
    sh "sudo chown -R 99:99 '#{tmp_product_dir}'"
    sh "sudo chmod -R 777 '#{tmp_product_dir}'"
            
    # Export the product modules to make this specific .dmg
    PRODUCTS[topics_product][:modules].each do |product_module|
      sh "cd '#{CACHED_COPY}';  #{SVN_PATH}/svn export --force '#{PRODUCTION_FOLDER}/#{product_module}' '#{tmp_product_dir}/modules/';"
    end
  
    # export the help
    sh "cd '#{CACHED_COPY}';#{SVN_PATH}/svn export --force '#{PRODUCTION_FOLDER}/help' '#{tmp_product_dir}/Production File System/help'"   
  
    end  
    
    # Right we're done making the .dmg so unmount it. The file will remain in /tmp
    sh "hdiutil detach -force '/Volumes/#{PRODUCTS[topics_product][:app_folder_name]}'"    
    
  end
  
  desc "Create the hybrid PC & Mac .iso file for #{topics_product}"
  task "build_#{topics_product}_dvd".symbolize => "build_#{topics_product}".symbolize do
    # Create a Hybrid DVD image containing the contents of the PC .iso and the contents of the Mac .dmg
    #
    # get rid of any old tmp files
    sh "sudo rm -rf '/tmp/#{PRODUCTS[topics_product][:pc_iso_volume_name]}'"

    # mount the PC .iso
    sh "hdiutil attach '#{PRODUCTS[topics_product][:pc_iso]}'; sleep 5"

    # mount the Mac .dmg - this is where we'll copy the 
    sh "hdiutil attach '/tmp/#{PRODUCTS[topics_product][:dmg]}'; sleep 5"

    tmp_product_dir = "/Volumes/#{PRODUCTS[topics_product][:app_folder_name]}"
    
    # Copy the contents of the PC .iso to the mounted Mac .dmg (which is why we resized it to 4Gb earlier)
    # hdiutil needs to operate on a mounted volume to successfully create a hybrid iso
    sh "ditto '/Volumes/#{PRODUCTS[topics_product][:pc_iso_volume_name]}' '#{tmp_product_dir}'"    

    # - unmount the PC .iso
    sh "sudo umount -f /Volumes/'#{PRODUCTS[topics_product][:pc_iso_volume_name]}'"

    # Remove any previous hyrid .iso prior to making this one
    sh "rm -f '/tmp/hybrid.iso'"
    
    # Make the hybrid iso
    # exlude PC files from the Mac, and exclude Mac files from the PC
    sh "hdiutil makehybrid -o /tmp/hybrid.iso '#{tmp_product_dir}' \
                           -hfs -iso -joliet \
                           -hide-hfs    '#{tmp_product_dir}/#{PRODUCTS[topics_product][:hide_hfs]}' \
                           -hide-joliet '#{tmp_product_dir}/#{PRODUCTS[topics_product][:hide_joliet]}' \
                           -hide-iso    '#{tmp_product_dir}/#{PRODUCTS[topics_product][:hide_joliet]}'"
                           
  end
  
  desc "Burn the .iso file for #{topics_product} to DVD"
  task "burn_#{topics_product}_dvd".symbolize => "build_#{topics_product}_dvd".symbolize do
    
    # Burn the DVD                          
    # That really long device is the external burner's firmware address (we're using an external writer)
    # I can never remember how to get this. Use 
    # 
    #   hdiutil burn -list | grep IOService
    # 
    # to list your device
    
    sh "hdiutil burn -device IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/PCIB@1E/IOPCI2PCIBridge/FRWR@3/AppleFWOHCI/IOFireWireController/IOFireWireDevice@d04b990c04c4d1/IOFireWireUnit/IOFireWireSBP2Target/IOFireWireSBP2LUN/com_apple_driver_Oxford_Semi_FW934_DSA/IOSCSIPeripheralDeviceNub/IOSCSIPeripheralDeviceType05/IODVDServices /tmp/hybrid.iso"    

  end
end  
	

The end result of this is that we end up with 11 burn_<a product name>_dvd tasks, which each invoke in turn

  1. update_repository
  2. build_<a product name>_dmg
  3. build_<a product name>_dvd

and ends up with a burnt, hybrid DVD being made for you.

The critical part happens at line 163 where the Mac .dmg is mounted under /Volumes, and is writeable, has the PC content written to it. It seems that hdiutil only likes mounted images when creating hybrid images. I experimented with various other options (using directories in /tmp, etc) but for the hybrid image to built correctly, it seems this is your only choice. The -hide switches list via globs, which files to hide from each filesystem.

Our staging server needed to be able to run multiple unicorns, each responsible for a different rails app, e.g. QA and staging

I wanted a simple /etc/init.d script that will start/stop/reload all my unicorns or just a specific one:

# starts all unicorns listed in /etc/unicorn/*.conf
/etc/init.d/unicorn start 

# stops the QA unicorn
/etc/init.d/unicorn stop /etc/unicorn/qa.conf 

The /etc/unicorn files are just simple variable setters. Here’s a sample /etc/unicorn/staging.conf

  RAILS_ROOT=/var/www/apps/e_learning_staging/current
  RAILS_ENV=development

Here’s the script to save as /etc/init.d/unicorn (in case the gist doesn’t embed below) – don’t forget to run sudo /usr/sbin/update-rc.d -f unicorn defaults to link it up to your rc.d scripts for running at boot time.

Enjoy!

Realtime Election Tweets

Tuesday May 04, 2010 @ 03:58 AM (BST)

Real-time UK General Election Tweets with node.js & websockets from jaygooby on Vimeo.

Inspired by Makato’s X-Factor real-time Twitter experiments and with just a couple of days to go until the country goes to the polls, I wondered if we could glean an outcome from the Twittersphere, especially as we get nearer to the actual count and results.

Implementation

My real-time election site, monitors the twitter streaming API for mentions of the four three main British political parties, plus the Greens (hey, I live in Brighton Pavillion and they look like they might get their first seat) and their leaders, and tallies a total score and current ratio of tweets.

I’ve since updated it and it monitors all the parties, plus various independents and niche (read 0 current seats in parliament) parties and also tallies phrases like “I voted for”, “I’m voting” etc to count actual votes as well as mentions.

Using node.js and client-side HTML5 websockets (with a fallback websocket implementation in Flash for older browsers), the site is entirely implemented in Javascript. The server-side JS listens for Tweets and emits the scores as json via the client’s websocket. Some client-side JS handles the screen updates.

Server set-up

The static portions of the site are served by nginx (which is itself an asynchronous evented server like node.js). Initially I’d tried to serve the whole thing via node.js using the paperboy.js static file serving module but I’d need both paperboy and the node.ws.js websocket server to share port 80, which would mean some re-engineering, and given that the election is in two days, wanted to get something up quickly!

So my architecture is static files served out of docroot handled by nginx, and the websocket running on a high port for use by the browser clients.

This high-port usage is itself a problem, as I’d imagine that many corporate firewalls block all bar 80 and maybe 8080, which is why the combined server running entirely out of node would be a good eventual goal.

To try and solve this, I thought I’d try and proxy the high port via nginx. You can turn the proxy buffer off in nginx, making it ideal for this, but I hit another hurdle with the Flash implementation of the websocket that will be used by most browsers (only Chrome is currently able to use native HTML5 websockets).

Flash and the crossdomain policy file

Flash requests its cross-domain policy file in an HTTP call to us (aka our Socket Policy Server) thus:

<policy-file-request/>\x00

Note there’s no GET verb, just the xml invocation and a NULL byte. Nice. Quite rightly nginx chokes on it, and issues an HTTP 400.

There’s an nginx hack I could have used to get the socket policy served and perhaps correctly tunnel the websocket stream itself, but I just wanted to launch, so I’ve left this as a to-do for now.

Versioning your Amazon S3 buckets

Wednesday February 10, 2010 @ 01:36 PM (GMT)

I’m sold hook line and sinker on the AWS platform. I’m especially impressed at the product innovation and ever-reducing prices.

A few days ago Amazon announced versioning for S3. This means that with the versioning flag for a bucket switched on, you can retrieve earlier versions of your files. Sweet.

Now, because I’m lazy, I tend to use S3Fox or Cyberduck for setting ACLs and creating european buckets and so on.

Neither of these have updated yet to support the versioning flag, and the AWS Console doesn’t have an S3 interface, so I thought I’d get my hands dirty and find out how to do it with the REST interface.

You issue a PUT to your bucket with the versioning querystring and the relevant XML:

<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <Status>Enabled</Status>
</VersioningConfiguration>

You can do it all with cURL, but the biggest pain is generating the correct Authorization: header to sign your API call.

Enter Tim Kay’s aws commandline tool – its a swiss knife for S3 and EC2 calls. Enabling the versioning was as simple as:

aws put my.bucket.com?versioning versioning.xml

Where versioning.xml contains the VersioningConfiguration xml snippet listed above.

To check the versioning status of a bucket, you do:

aws get my.bucket.com?versioning

Before you run the aws commandline, you’ll need to create an ~/.awssecret file with your AWS key and secret key. Don’t forget to chmod 600.

Charanga are participating in the 2010 Sussex Internship programme. We’re looking for a usability and user-experience intern to help us make our music e-learning system as easy to use as possible.

The March 2010 internship positions should go live on Monday 7th December. Currently the placements page is only showing 40 positions, but the additional 60 places will be there from Monday. Then you’ll be able to search for our role.

Our instrumental, vocal and curriculum music e-learning system is used by 55 local authorities and thousands of teachers and students. You’ll be undertaking user testing, making recommmendations and working with our developers to implement some of these, gaining invaluable experience in the process.

Thing 1 goes to the Brighton & Hove Montessori School, courtesy of the government’s Early Years Funding Scheme which we top up so she can attend for four mornings week and one full day.

We love the self-directed, independent, mixed age-group learning environment it provides, but once she’s five, we’ll have to dig deep to fund her education.

A group of Brighton & Hove Montessori parents are campaigning for a state-funded Montessori primary school to be opened in the city. This builds on the precedent of five other state-finded primaries that have opened in the UK.

At the moment, I’m only peripherally involved, but I did make this poster. The last time I did any print-work was using a pre-historic version of QuarkXPress, so this was an interesting challenge.

If you’ve got a child aged 3-11 and you’re concerned about or dissatisfied with the increasingly restricted range of schooling choices for your child in Brighton & Hove, then we’d welcome your support

Rendering bitmaps from PDFs at non-native sizes

Tuesday November 17, 2009 @ 04:32 PM (GMT)

Charanga’s primary instrumental teaching resources use a synchronised score and animated instrument to indicate which notes are played during the piece.

Each of the resources begins life as a Sibelius arrangement, from which we use both the midi and score output. We’ve got close to a thousand of these interactive pieces and with any job of this size, scriptable tools can really help speed up the production process.

Pete, our musical arranger, asked me if there was a quicker way for him to generate the PNGs needed by the interactive tool. Up to now he’d been exporting them directly from Sibelius.

Sibelius can batch export PDFs, so converting from these was definitely the way to go. The main problem with a straight imagemagick convert command, like:

  convert score.pdf score.png

is that the native size of the PDF probably isn’t the correct size for the PNG, and when you try and force the correct size with a resize:

  convert -resize 506x517 score.pdf score.png

You end up with a poor bitmapped image, because you might be scaling up from an effective smaller size; e.g. in my score example the native size of the PDF is only 271×276 and I’m trying to go to twice the size [506×517]. Hence the poor quality of the resulting PNG.

What’s required is to up the size of the PDF prior to the convert taking place; it is a vector format after all, so there’ll be no loss of quality with a larger image. A simple way to do this is to up the DPI of the PDF. ImageMagick will default to 72DPI unless told otherwise. Crank up the density (DPI) for a bigger image:

  convert -density 600 -resize 506x517 score.pdf score.png

And the resulting PNG is much more acceptible:

Tweeting Brighton and Hove real-time bus departures


Tweet bird courtesy of Gopal Raju, bus photo Brighton & Hove Bus and Coach Company

Brighton & Hove have real-time departure boards showing when the next bus is due at most of their city-centre bus stops. There’s also good Google Maps integration, showing bus stops and departure times.

A recent addition to the city’s transport infrastructure, is the ability to text a bus stop code from your mobile and to receive back, by text, a list of the buses due at that stop. There’s a 25p charge for the service however, and as I often just want to quickly check what time I should leave work or home to catch the next bus, I thought I’d write a Twitter bot that I could tweet for the information instead.

There’s an excellent ruby library for writing Twitter bots called twibot that uses a Sinatra-inspired DSL for matching incoming tweets against routes that should respond to the tweet.

Data, data, data

The biggest hurdle in getting @bustweet up and running was data. I needed to get a list of all the bus top sms text codes – e.g. brimdgm – and the corresponding bus routes that serviced that stop. I knew it had to be there somewhere, because the slippy maps show you the bus stop, its text code and a list of buses that stop there. Time to dig out Firebug

It took me a couple of hours, but I was pretty certain that I could build something.

It turns out that the text codes are known as Naptan Codes, and although the codes themselves are assigned by the local authority, they’re a nationwide initiative and defacto standard. Indeed, I’m certainly not the first person to have a go at using the Naptan data; James Wheare built LiveBus.org from the various local authority transport data that’s available.

And once you know where to look, you can get a whole pile of JSON (wrapped up as Javascript) sent to you. JSON for services:

"var response = 
{
    service:
    {
        serviceId: \"29\",
         serviceName: \"11X\",
         serviceDescription: \"Hove Town Hall - Kings House - Thistle Hotel\",
         serviceAbbreviatedName: \"\",
         routes: 
        [
            {
                routeId: \"65\",
                 routeName: \"11X Hove - Brighton\"
            },
            {
                routeId: \"66\",
                 routeName: \"11X Brighton - Hove\"
            }
        ]
    }
}
;"

And routes and stops:

"var response = 
	{
	    routeid: \"65\",
	     stops: 
	    [
	        {
	            stopId: \"6915\",
	             stopName: \"Hove Town Hall\",
	             operatorsCode1: \"06915\",
	             operatorsCode2: \"06915\",
	             gpsStopName: \"Hove Town Hall N\",
	             naptanCode: \"brimpmg\",
	             Lat: \"50.828602\",
	             Lng: \"-.170563\"
	        },
	        {
	            stopId: \"6905\",
	             stopName: \"Kings House\",
	             operatorsCode1: \"06905\",
	             operatorsCode2: \"06905\",
	             gpsStopName: \"Kings House\",
	             naptanCode: \"brimpjw\",
	             Lat: \"50.824944\",
	             Lng: \"-.168752\"
	        },
	        {
	            stopId: \"6079\",
	             stopName: \"Brighton Centre\",
	             operatorsCode1: \"06079\",
	             operatorsCode2: \"06079\",
	             gpsStopName: \"Brighton Centre\",
	             naptanCode: \"briamta\",
	             Lat: \"50.820858\",
	             Lng: \"-.145890\"
	        },
	        {
	            stopId: \"6913\",
	             stopName: \"Thistle Hotel\",
	             operatorsCode1: \"06913\",
	             operatorsCode2: \"06913\",
	             gpsStopName: \"Thistle Hotel\",
	             naptanCode: \"brimpga\",
	             Lat: \"50.819883\",
	             Lng: \"-.140137\"
	        }
	    ]
	}
	;"	

I came up with a nice simple schema of three tables; services, routes and stops and populated these with data via a bit of shell scripted curling. As you can see from above, the JSON needed cleaning up too; I wanted to parse it with Ruby not Javascript, and even then, the keynames weren’t validly quoted. A quick pass through sed fixed that and meant I now had details on the 101 bus routes and the 7947 bus stops (who knew there were so many!) that service them.

The final part of the puzzle was getting the actual real-time data for a particular stop. The web-based departure boards are nice and self explanatory via querystring parameters, so I just need to screen scrape the result of any tweeted enquiry.

First I convert the text name for the stop to the stopId value that the departure board needs; a quick join across the database schema, and the resulting web page gets scraped with hpricot and the resulting bus times are tweeted back to the original enquirer.

Give it try! Tweet @bustweet with the text name of a bus stop, e.g. @bustweet brimdmt or with an optional route number to e.g. @bustweet brimdmt 5b

It’s still a little brittle and may well be up and down in the next few days, so please be a little patient.

If you’ve got any ideas for ways to enhance it, feel free drop me a line at jay@gooby.org or @jaygooby or leave a comment below.

If you’ve had problems with wp-super-cache not writing cached files into the cache/supercache/www.yoursite.com folder when running under nginx, then I think I’ve found the reason why…

There are actually two different problems here, but the first is the nginx-specific one and is a side-effect of your nginx configuration.

If you’re using rewrites so you have friendly URLs like /nginx-wp-super-cache-not-writing-cache-files-solved rather than index.php?p= and if like me you grabbed the nginx directives off the web somehere, you’ve probably got some lines like this:

  if (!-e $request_filename) {
    rewrite ^.+/?(/wp-.*) $1 last;
    rewrite ^.+/?(/.*\.php)$ $1 last;
    rewrite ^(.+)$ /index.php?q=$1 last;
  }

The last rewrite directive sets a querystring parameter called q with the friendly URL as its value. Deep in the guts of wp-super-cache there’s a test for any querystring parameters, and if there are, it won’t super-cache the page. Doh! So what to do?

Change line 250 (wp-cache-phase2.php in wp-super-cache version 0.9.4.3) from:

if( ! empty($_GET) || is_feed() || ( $super_cache_enabled == true && is_dir( substr( $supercachedir, 0, -1 ) . '.disabled' ) ) ) {

to:

if( (count($_GET,1) > 1) || is_feed() || ( $super_cache_enabled == true && is_dir( substr( $supercachedir, 0, -1 ) . '.disabled' ) ) ) {

So rather than checking for an empty querystring, we check for more than 1 querystring parameter, because due to our nginx config, we’ll always have one called q with a value of the current URL.

The second problem, and this applies regardless of whether you’re using nginx, apache or any other httpd, is cookies.

We all know that if you’re logged in to Wordpress, wp-super-cache won’t save or serve you super-cached files (it just uses the regular wp-cache files instead). But, during my digging, I also found out that even if you visit any areas of your site that require you to login (i.e. first redirect you to wp-login.php), and then you don’t login, you still will have been cookied with wordpress_test_cookie containing a value of WP Cookie check.

Again, due to the way wp-super-cache works, this will now be enough to stop it saving or serving you super-cached files, until you delete this cookie.

You can either remember to keep deleting this cookie, or you could just patch wp-cache-phase2.php as follows. Change lines 267-270 from:

if( $super_cache_enabled ) {
	$user_info = wp_cache_get_cookies_values();
	$do_cache = apply_filters( 'do_createsupercache', $user_info );
	if( $user_info == '' || $do_cache === true ) {

to:

if( $super_cache_enabled ) {
	$user_info = wp_cache_get_cookies_values();
	if ($user_info == 'WP Cookie Check') $user_info = "";
	$do_cache = apply_filters( 'do_createsupercache', $user_info );
	if( $user_info == '' || $do_cache === true ) {

Better late than never

Thursday March 19, 2009 @ 03:06 PM (GMT)

Since 1999 this domain has had the title “Back in 5 mins”, so here, a whole decade later, is the inaugural post.

At the start of Dune, Frank Herbert says through his narrator Princess Irulan that “A beginning is the time for taking the most delicate care that the balances are correct”. If I tried to take the most delicate care with this post, let alone the site, I think another decade would pass before anything happened.

So as much as I’d like to write a manifesto, put some nice design in place and make everything as POSH as possible, I’m just going to worry about the content, stupid, and sort the rest, later.

To keep me honest, and to make sure I keep my promise with myself not to let this blog stagnate after my first flush of interest, I’m going to commit to a few things:

  1. I’ll be posting about some personal sites I’m just about to start building: BuildYourSiteRight, FamilyAddressBook (no links, because they’re just tumbleweed at the moment).
  2. As well as personal projects, I want to talk about some recent Wordpress work that uses a mother-lode of plugins and has some rather excellent Google Apps and Google Calendar integration and well as some sick iCal aggregation
  3. A quick post about using Dreamhost (Dreamhost, I think I love you) to host private git repositories
  4. Finally, I really ought to write about my adventures in Ruby, seeing as it’s pretty much all I do nowadays

And what blog post wouldn’t be complete without taking up an internet meme? So, to keep up with the Jones’ (so to speak) and to ride the wave of Moleitausian excitement allow me to introduce you to the things that I’ve made this past week or so…

thoth-plugins on github. Rather than take the obvious route of running this blog on Wordpress, I’ve opted to try a different blog engine, Thoth. I heart Wordpress, but it’s an abusive relationship, because once I see that PHP I can’t help myself. I wade on in, a bit of html there, a quick <?php> tag here, and I’m drunk on bad habits. The next most obvious route would be to write my own blogging engine, after all, the seminal Rails screencast is a blog in ten minutes, but that way madness lies – along with anther ten year wait for the first post – as I get sidetracked by all that an under-specified software project entails.

My giddy excitement at finally joining Github meant that I had a quick look around for a blogging system that used Ruby and let you author both posts and pages. I know, I know. I could have just used Mephisto or Typo, but I wanted something simple, that I could understand and customise easily. Thoth seemed to fit the bill nicely. And so that’s what’s driving Back in 5 Mins for the moment.

For recent projects I’ve been used to running a mongrel cluster behind an Nginx front-end, but Rob, has been tempting me with Enterprise Ruby and Phusion Passenger. Thoth got a patch which let it work with Rack, and so after a fresh install of this slice I’m now a happy user.

The first thing I made was a plugin for Thoth, which lets you set up redirects.

As well as some digital making, I’ve made some physical stuff too. I’m attending Scotland on Rails and I haven’t got around to ordering any Moo cards, so I thought I’d have a bash at making my own, what with the ubiquitous message and all.

A quick trip to WHSmith and and bit of moveable type action, and I’m sorted on the card front.

Copyright © 2010 Jay Caines-Gooby. All rights reserved.
Powered by Thoth.