Build Monitoring Widgets with Ruby and BitBar
Ruby developers are used to finding incredibly useful open source projects packaged as gems. In this tutorial, we’ll take a look at BitBar, an open source project that isn’t built in Ruby, but when combined with a small bit of Ruby can be incredibly powerful.
While the original project is no longer actively maintained, an updated fork of the project can be found here: https://github.com/kamenevn/bitbar
BitBar provides Mac OS X users an easy way to create a menu bar item from the standard output of a script. I’ll show you how to create a simple site ping monitor, and then we’ll create a Twitter follower counter.
Installing BitBar
To install BitBar, visit the GitHub repository at https://github.com/kamenevn/bitbar. Download the zip file (the button to download is in the righthand sidebar). Inside the archived folder, you will find a “Releases” folder, which holds the BitBar.app
file. Put this in your Applications directory. Opening this application launches BitBar.
How BitBar Works: File Structure
BitBar is a simple application that runs all shell scripts in a given directory on an interval that is determined by the filename. Of course, you should be very careful when adding scripts to BitBar, especially if you are adding a low interval time.
To run a given file every 5 minutes, the naming structure would look something like this:
my_script.5m.sh
Of course, you will also want to make your scripts executable. Note that BitBar refers to these scripts as “plugins” in their documentation.
How BitBar Works: Script Output
In order to add visible text to your BitBar plugin, you will need to send output to STDOUT
. In Ruby, this is simply the output of puts
. By default, BitBar will cycle through each line of the output, unless you output the specific string “—“; anything after this line will show up in a dropdown after you click the BitBar item in the menu bar.
BitBar also allows changing the text color and responding to clicks. Clicks can either open a URL or trigger another shell script, and can be set to do so in the background.
Here’s a simple Ruby script that would create a link to StackOverflow in BitBar:
url = "https://stackoverflow.com"
puts "Go to StackOverflow | href=#{url}"
Notice the |
separating the outputted text and the parameters for clicking. The href
parameter tells BitBar to open this URL when clicked. Here’s a more complex example, showing some red text that triggers another executable script:
puts "Run My Script | color=#ff0000 bash=/path/to/your/script.sh"
Of course, once again, you’ll want that script to be set to be executable using chmod
.
Finally, if the script takes parameters, you can add them using param1
, param2
, and param3
. If you would like to execute the bash script in the background on click, set the parameter terminal=
to false
. For example, you might want to use the current time as a parameter for a script that runs in the background:
current_time = Time.now.to_i
puts "Run My Script | color=#ff0000 bash=/path/to/your/background_script.sh terminal=false param1=#{current_time}"
Creating a Site Monitor
Now that we know how BitBar works, let’s create a site monitor in Ruby that we can then use with BitBar. As a disclaimer, you should know that only checking a site’s status from your own computer may not give you the full picture. To really know if your sites are down, use monitoring services and sites like uptimerobot.com. But this little widget will provide a quick glance and signal potential problems as they occur.
The required behavior is a bullet point that is red or green, depending on the status of a list of sites. I want to be able to keep that list of sites in an easy-to-update YAML file. When one of the sites goes down, I want to be able to click the bullet point in my menu bar and see the failing site. I also want to be able to click that menu item to visit the site in my browser.
Let’s get started with some Ruby!
The Site Checker Class
First, we’ll create a simple class called SiteChecker
:
# /path/to/bitbar_plugins/site_checker/site_checker.rb
require 'yaml'
require 'net/http'
class SiteChecker
def initialize(sites)
@sites = sites
end
def up?(server, port=80)
http = Net::HTTP.start(server, port, {open_timeout: 5, read_timeout: 5})
response = http.head("/")
response.code == "200"
rescue Timeout::Error, SocketError
false
end
def run
results = {}
@sites.map {|s| results[s] = up?(s) }
results
end
end
Great! Now we have a simple Ruby class that can check any given site, can be initialized with a list of sites to check, and returns a hash of results.
Next, let’s write the actual BitBar plugin that will use the SiteChecker
class. Add a folder to the BitBar plugins folder called site_checker
and save the site_checker.rb file there. Next, create a file called site_checker.3m.sh, and place it in the BitBar plugins directory. Here’s what that file looks like:
#!/usr/bin/env ruby
# Note: the ruby path above should point to your Ruby installation.
# You can verify this by checking `which ruby`
$:.unshift File.dirname(__FILE__)
require 'site_checker/site_checker.rb'
require 'yaml'
def run
sites = YAML.load_file(File.join(__dir__, 'site_checker/sites.yml'))["sites"]
site_checker = SiteChecker.new(sites)
results = site_checker.run
if results.values.include? false
puts "• | color=#ff0000"
puts "---"
results.select {|key, val| val == false }.keys.each do |site|
puts "#{site} down | href=http://#{site}"
end
else
puts "• | color=#82B021"
end
end
run
This simple script formats the output of our SiteChecker
class for BitBar to use. Finally, we need to make sure we’ve added our sites.yml file inside our site_checker directory:
sites:
- "google.com"
- "twitter.com"
- "yoursite.com"
Make sure your site uses only its domain name (without any protocols).
The final step is to make the script executable. You can do this by running chmod +x site_checker.3m.sh
. The resulting file structure should look like this:
├── site_checker
│ ├── site_checker.rb
│ └── sites.yml
├── site_checker.3m.sh
Twitter Follower Count
Next, let’s do something a bit more involved. We’ll show how many users we are following and how many users are following us on Twitter.
The first step is to register an application with Twitter. Once you’ve followed the steps and successfully created your project, you will need to locate the API keys and access tokens for the application.
Next, create a folder inside your BitBar plugins directory called twitter_counter, and an associated plugin file called twitter_counter.10m.sh.
Inside of the newly created twitter_counter folder, create an account_detail.rb file. We will use this to create a simple class that will return follow count information for a Twitter account.
The AccountDetail
class looks like this:
require 'twitter'
class AccountDetail
attr_accessor :user
def initialize(username)
@client = create_client
@user = @client.user(username)
end
def followers
user.followers_count
end
def followings
user.friends_count
end
private
def create_client
Twitter::REST::Client.new do |config|
config.consumer_key = "YOUR CONSUMER KEY"
config.consumer_secret = "YOUR CONSUMER SECRET"
config.access_token = "YOUR ACCESS TOKEN"
config.access_token_secret = "YOUR ACCESS TOKEN SECRET"
end
end
end
Make sure you run gem install twitter
if you have not already. In the previous example, we used built-in libraries. Twitter is a third-party gem that must be installed before this class will work.
The AccountDetail
class essentially wraps a very small subsection of the Twitter gem for our use case. Next, edit the twitter_counter.10m.sh file to look like this:
#!/usr/bin/env
$:.unshift File.dirname(__FILE__)
require 'twitter_counter/account_detail'
def run
@account_detail = AccountDetail.new('your_twitter_name')
puts "#{@account_detail.followers} followers"
puts "#{@account_detail.followings} following"
end
run
Finally, edit the permissions on the script to make it executable. As long as everything is set up correctly, you should see a simple widget in the menu that cycles between your “follower” count and your “following” count. Of course, we could use much more of the Twitter API to do even more interesting things!
Conclusion
While this tool may not be used in a project you are intending to ship to your clients, it certainly can be a useful and fun tool to create your own native applications. Here are some ideas for more projects using BitBar:
- Build status indicator (integrating with something like CircleCI or Codeship)
- Stripe sales ticker
- A simple easy-to-access action list for performing common scriptable tasks or macro-functions
- Visual graphing of things with something like (Spark)[https://github.com/holman/spark]
Share your ideas with us here! Happy hacking!