I use Launchd to run some scripts on a Mac with cron-like regularity. (OS X provides cron, but Launchd is apparently the preferred approach.) Many of those scripts are written in Ruby, and I’m trying to migrate them to Ruby 1.9.2 as part of my overall migration. OS X has Ruby 1.8.7p174 installed by default. I use RVM to manage other versions including 1.9.2. The scripts are executable and start with a “magic shebang” line of #!/usr/bin/env ruby. Run from my shell, where RVM is configured and 1.9.2 is my default interpreter, everything just works. When run from Launchd, however, RVM is not configured. The system ruby is used instead, and as I don’t generally install any gems for the system Ruby, and since some of the scripts aren’t even compatible with Ruby 1.8, things blow up.

To work around this, I installed a wrapper as $HOME/bin/_rvmruby that sets up RVM and then execs ruby:

#!/bin/sh
# usage: _rvmruby <ruby version> [ruby arguments]

if [[ -s ~/.rvm/scripts/rvm ]]; then
  . ~/.rvm/scripts/rvm
fi

ruby_version=$1; shift
rvm $ruby_version
exec ruby "$@"

It requires you to specify the version of Ruby you want. You can even include the gemset if you want. A little extra logic could get it to ue a sensible default, but I’m too lazy.

Next I modify the plist file for the “cron” job to use the wrapper:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>com.example.wozziegoggle</string>
        <key>ProgramArguments</key>
        <array>
                <string>PATH_TO_WRAPPER/_rvmruby</string>
                <string>RUBY_VERSION</string>
                <string>PATH_TO_RUBY_SCRIPT</string>
        </array>
        <key>StartCalendarInterval</key>
        <dict>
                <key>Hour</key>
                <integer>6</integer>
                <key>Minute</key>
                <integer>0</integer>
        </dict>
</dict>
</plist>

The array of strings provided to ProgramArguments is the command and arguments you want Launchd to to run. Obviously replace PATH_TO_WRAPPER, RUBY_VERSION and PATH_TO_RUBY_SCRIPT with the actual path to _rvmruby, the version of Ruby you want to use and the path to the script you want to run, respectively.