Turns out Ruby is great for scripting!

Earlier last year, I gave myself two challenges:

  • write automation scripts in Ruby (instead of giving up on writing them in shell)
  • use system debugging tools (strace, lsof, gdb, etc.) more often to figure out why programs are behaving some way

Of course, I was almost immediately stymied on the second one:

sudo dtruss -t write ruby -e "puts 'hi!'"

dtrace: failed to execute ruby: dtrace cannot control executables signed with restricted entitlements

dtruss is the dtrace-powered macOS-equivalent of strace. It is very cool when it works. But. It turns out Apple has a thing that protects users from code injection hijinks, which makes dtrace not work. You can turn it off but that requires hijinks of its own.

I did end up troubleshooting some production problems via strace and lsof. That was fun, very educational, and slightly helpful. Would do again.

I did not end up using gdb to poke inside any Ruby programs. On the whole, this is probably for the better.

I was more successful in using Ruby as a gasp scripting language. I gave myself some principles for writing Ruby automation:

  • only use core/standard library; no gem requires, no bundles, etc.
  • thus, shell out to programs likely to be available, e.g. curl
  • if a script starts to get involved, add subcommands
  • don’t worry about Ruby’s (weird-to-me) flags for emulating sed and awk; stick to the IRB-friendly stuff I’m used to

These were good principles.

At first I tried writing Ruby scripts as command suites via sub. sub is a really cool idea, very easy to start with, makes discovery of functionality easy for others, and Just Works. You should try it some time!

That said, often I didn’t need anything fancy. Just run a few commands. Sometimes I even wrote those with bash!

But if I needed to do something less straightforward, I used template like this:

#!/usr/bin/env ruby

test # Run the test suite
test ci # Run test with CI options enabled
test acceptance # Run acceptance tests (grab a coffee....)

module Test

def test
`rspec spec`

# ... more methods for each subcommand


if __FILE == $0
cmd = ARGV.first

case cmd
when "ci"
# ... a block for each subcommand

This was a good, friction-eliminating starting skeleton.

The template I settled on eliminated the friction of starting something new. I’d write down the subcommands or workflow I imagined I needed and get started. I wrote several scripts, delete or consolidated a few of them after a while, and still use a few of them daily.

If you’re using a “scripting” language to build apps and have never tried using it to “script” things I “recommend” you try it!