ruby
Writing shell scripts for xbar using Ruby, rbenv, and Bundler
Shell scripts for xbar can be written in any language, including Ruby! But I found that getting these scripts to run using a modern Ruby stack was a little tricky. Here’s what I figured out.
Using a modern Ruby version
Xbar runs as a background service that does not have access to the user’s interactive shell environment. That means that a Ruby script will run using the system-provided Ruby by default. I don’t want to use the system Ruby on macOS, because it is extremely old (Ruby 2.6, as of macOS Ventura in March 2023), and has reached end-of-life status.
To make Ruby shell scripts use an rbenv-managed Ruby instead, I’m now using this shebang header:
#!/usr/bin/env -S-P${HOME}/.rbenv/shims:${PATH} ruby
ruby
is subsequently executed, rbenv’s Ruby is selected instead of the system default.
Managing gems with Bundler
When I first discovered xbar, I immediately thought of building scripts that would leverage external APIs (one of my first ideas was to display an air quality indicator using data from the airnow.gov API). To make those scripts easier to write, I would need to use some gems. Depending on the situation, I’ve learned there are a couple different ways to manage gems with Bundler in a shell script.
bundler/inline
Bundler can be embedded directly into a shell script using bundler/inline
. Whenever the script is run, Bundler will download and install the specified gems automatically, as needed.
#!/usr/bin/env -S-P${HOME}/.rbenv/shims:${PATH} ruby
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "faraday"
end
# Use gems listed in the gemfile block, no `require` needed
Faraday.get(...)
gemfile
block work just like an external Gemfile. Similar to Rails, the gems do not need an explicit require
before using.
Gemfile
In the scenario where multiple shell scripts use the same gem dependencies, an alternative is to place a separate Gemfile
in the same directory as the scripts and reference it within each script like so:
#!/usr/bin/env -S-P${HOME}/.rbenv/shims:${PATH} ruby
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("Gemfile", __dir__)
require "bundler/setup"
Bundler.require
# Use gems listed in the Gemfile, no `require` needed
Faraday.get(...)
config/boot.rb
in a typical Rails app.
Unlike the bundler/inline
method, bundle install
needs to be run manually to ensure the gems are downloaded and installed prior to running the script.
Aside: What is xbar?
xbar is a plugin system for custom menubar items on macOS. Given a shell script (which can be written in any language), xbar will execute the script at regular intervals and turn the script’s stdout into a dynamic menubar item.
You just read
Writing shell scripts for xbar using Ruby, rbenv, and Bundler
Share this post? Copy link
About the author
Hi! I’m a Ruby and CSS enthusiast, regular open source contributor, software engineer, and occasional blogger writing from the San Francisco Bay Area. Thanks for stopping by! —Matt