Holy Hacking, Batman! Create Alfred Workflows in Ruby

Share this article

alfred
This article was peer reviewed by Fred Heath. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

The past several years have seen a sea of keyboard productivity tools introduced:

  • Gnome Do
  • Launchy
  • Quicksilver
  • Synapse
  • LaunchBar

These tools enable users to associate complex actions with keywords or shortcuts. Alfred is similar to these solutions but has grown very popular thanks to its emphasis on user friendliness and flexibility.

Note: Alfred is OSX-exclusive.

One of the great things about Alfred is the ability to compose your own workflows. These can be constructed using the built-in editor, optionally including your own scripts written in PHP, Shell, Python…

…and Ruby.

In this tutorial, we will create a simple random number generator workflow using a Ruby script.

Custom Searches

Before we get into workflows, let’s see what Alfred can do by creating a custom web search for searching SitePoint. This is simple enough to do just in the editor. First, we need to figure out the URL that we can use to search the site. Enter something in the SitePoint search field and see what kind of URL is generated.

As of this writing, searching for ‘ruby’ generated sitepoint.com/?s=ruby.

Open Alfred’s preferences window and navigate to the ‘Features’ tab. On the left side, select ‘Web Search’. In the bottom right, click ‘Add Custom Search’.

web_searches

The title is the name that will show up in Alfred. SitePoint works fine. The keyword is what we will type. For example, we could use sp.

For the search URL, enter: https://www.sitepoint.com/?s={query}. {query} is the universal Alfred way of referring to the argument provided after the keyword.

sitepoint_web_search

After the custom search is created, activate Alfred (default cmd+space) and type the keyword and a query. Once you see that the keyword is recognized, hit enter and the default browser should open SitePoint’s search results for that query.

This is great so far, but to do more advanced operations in Alfred, we will need to use workflows.

Alfred Workflows?

First, before you try to do anything with workflows, make sure you have the Alfred Powerpack – basically the ‘Pro’ version of Alfred.

Workflows can accomplish different tasks with the most basic being search. They are represented with sets of nodes corresponding to different workflow objects:

  1. Triggers – Activated by a hotkey or external event
  2. Inputs – Activated with a keyword, argument optional
  3. Actions – Open or reveal files, perform web searches, run scripts/commands
  4. Outputs – Use information from other nodes to generate notifications, copy data to the clipboard, or run scripts

A workflow may contain more than one node path. For example, you could have a keyword for one thing and a hotkey for something else.

colors_workflow

Colors is an example of a complex workflow.

Thankfully, for a lot of common tasks you won’t need to make your own workflows. Users have been uploading them for quite a while, and now you can find most at http://www.packal.org/.

However, as developers, we have differing approaches and styles to solving problems, so to improve our productivity at an individual level we will need to make our own.

Let’s convert our SitePoint search into a workflow so that we can share it with our friends. This is simple enough to do just in the editor without any code.

Open up Alfred’s preferences window and go to the ‘Workflows’ tab. The left side has a pane with a ‘+’ at the bottom. Click it and select Templates->Web and URLs->Open Custom URL in Specified Browser.

sitepoint_workflow_create

This will create a workflow with a Keyword node and an Open URL node.

sitepoint_workflow_nodes

For the Keyword node, provide the keyword and title. You can also put something in the subtext field to make it more clear what the keyword does.

sitepoint_workflow_keyword

For the Open URL node, add the URL we used for our previous custom search, https://www.sitepoint.com/?s={query}.

sitepoint_workflow_url_open

Now if you open Alfred and enter the keyword you used in the keyword node, you should see it come up. Give it an argument, and your default browser will open with the search results on SitePoint (provided the URL parameter for searching hasn’t changed since this writing – which is certainly possible if you are reading in the distant future).

One of the reasons for making a workflow is so we can share it or store it. Below the workflow list, there is a share button. Click it and the “Export Workflow” button that appears. After you have saved it somewhere, you can verify that it works by removing the workflow from the list and opening (double-clicking) the newly created .workflow file. It will reappear in the list and be usable once again. When we start adding resources like scripts, they will become encapsulated within the .workflow file, so it’s the only thing that will need to be shared.

Note: You may need to tell OSX to associate .workflow files with Alfred. There does not currently appear to be a way to import workflows from within Alfred.

Inviting Ruby to The Party

Let’s make a workflow that provides a keyword that generates a random number. You don’t need any libraries to use Alfred in Ruby. Here are the two main things needed for a keyword scripting workflow:

  1. {query} used as the keyword argument if needed
  2. An XML string of items sent to stdout

First, create a new blank workflow (call it “Random Number”). In the top right corner of the Node workspace, click on the “+” icon:

skich

Add an Inputs->Script Filter node. A script filter runs the script as we make changes to the keyword field and can show us a list of items (often suggestions or search results). Choose a keyword (I used ‘rando’) and set Ruby as the language.

At a minimum our XML output needs the nodes xml->items->item->title.

Put the following in the scripting area:

random = rand(1000)

xml = <<EOS
<xml>
<items>
  <item>
   <title>#{random}</title>
  </item>
</items>
</xml>
EOS

puts xml

Note: It’s a heredoc, so indention matters…

Save the node and try the keyword. If you set “argument optional”, then you can keep generating random numbers by typing characters after the keyword.

Alfred has more than input nodes. Let’s have the workflow copy the random number generated to the clipboard. In order for Alfred workflow objects after the script filter to receive data, we must specify the arg property in the XML item nodes:

  <item arg="#{random}">
   <title>#{random}</title>
  </item>

Next, add an Outputs->Copy to Clipboard node and drag a connection from the script filter to it.

It’s a bit unintuitive, but it’s {query} and not {arg} that refers to the value set for the arg attribute in the items XML nodes. So, in the clipboard output node, just enter {query}.

Now, if you invoke the random keyword again and hit enter, the number should be the next text pasted.

Although this works, it would be helpful if there was more feedback for the user. Create an Outputs->Post Notification node and drag a connection from the script filter to it. For the text, use {query} was copied to the clipboard

random_workflow_nodes

And now if you try the keyword again, you should see the OSX notification upon hitting enter.

Bash Is The Only Alfred Script Type

This example was fairly trivial, but editing complex Ruby scripts in the plain Alfred script field is not going to work. So we’ll move the Ruby code into a file and then execute it with a bash Alfred script.

This is where things can get tricky.

If you are using a Ruby version manager like rvm or rbenv, then you probably don’t want to mix gems between Alfred and your other Ruby projects. The standard procedure is to use /usr/bin/ruby in Alfred scripts, so you will want to find a way to add gems for it as opposed to your Ruby manager. If you normally use the system Ruby and depend on bundler for gem management, then you’re all set.

Otherwise, if you use rvm use your system ruby before installing gems with:

$ rvm system

Now change the script type from /usr/bin/ruby to /bin/bash and move the
script to a file arbitrarily called main.rb in the workflow’s folder. You can get to the folder with the ‘Open Workflow Folder’ in the bottom right corner of the script filter node view.

Now, for our script, we just need:

/usr/bin/ruby main.rb {query}

final_random_script_filter

The keyword should work in Alfred again. Now that we’re back in the friendly syntax-highlighted space of our favorite text editor, we can work on more complex scripts.

A Better Representation Format

XML is not the most malleable format. As our Alfred scripts get more complex, we will want something more terse/native to work with like Ruby hashes. There are several solutions available, but let’s try gyoku here:

$ gem install gyoku --version 1.3.1

Now we can replace the XML with a hash:

require 'gyoku'

random = rand(1000)

hash = {
  xml: {
    items: {
      item: [
        {
          title: random,
          :@arg => random
        }
      ]
    }
  }
}

xml = Gyoku.xml(hash)
puts xml

A couple of things worth mentioning:

  • :@arg is how we specify the XML attribute arg in gyoku.
  • The array is on the :item key and not :items

You may have noticed that our workflow doesn’t actually do anything with the {query} given to the keyword. Let’s fix that with the last code sample. The final workflow will generate a number of random integers that the user specifies:

require 'gyoku'
DEFAULT_ITEMS = 3

num_items = query = (ARGV[0] || DEFAULT_ITEMS).to_i

hash = {
  xml: {
    items: {
      item: []
    }
  }
}

num_items.times do
  random = rand(1000)

  item = {
    :title => random,
    :@arg => random
  }

  hash[:xml][:items][:item] << item
end

xml = Gyoku.xml(hash)
puts xml

Because we are passing {query} to /usr/bin/ruby main.rb in the script filter node, we access it via ARGV[0] inside the script. It’s a string, so we convert it to an integer with String#to_i.

Conclusion

Alfred does, in fact, have a Ruby library. But by understanding how things really work, you won’t need to depend on it and risk being in the dark if it doesn’t function the way you expect.

In a future article, I hope to look at creating practical Ruby workflows that interact with web services and OSX applications.

Robert QuallsRobert Qualls
View Author

Robert is a voracious reader, Ruby aficionado, and other big words. He is currently looking for interesting projects to work on and can be found at his website.

alfredbash scriptingGlennGworkflow
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week