- Published
- 2 March 2013
- Tagged
A few nights ago, I upgraded to ruby 2.0.0-dev through rbenv
. Loading up a script, I got the following:
LoadError: cannot load such file -- rb-appscript
Not too surprising - a bunch of gems needed to get installed, so I quickly fired up the install script. Except...
> gem i rb-appscript
Building native extensions. This could take a while...
ERROR: Error installing rb-appscript:
ERROR: Failed to build gem native extension.
This had been coming for a while. The developers of rb-appscript
have said that the project has been abandoned, which is usually a good time to leave the ship.
Next step was to find a replacement. As far as I'm aware, the big popular base that everyone likes for getting ruby to play nice with OS X these days is MacRuby, which (it appears) can do all kinds of voodoo when it comes to interacting with .nib
files and reading Core Data. I'm not into all that right now, though - what I really wanted was a way to interact with OS X programs, get data out of them, and use said data to my own nefarious purposes.
One common use here is to get data out of OmniFocus - for example, for my ongoing project to set up a personal kanban (part two is in the works, and will get published at some point, I swear). Here's something simple to fetch some data from OmniFocus using rb-appscript
:
#!/usr/bin/env ruby
require 'jcache'
require 'rb-appscript'
require 'date'
include Appscript
exit unless app('System Events').processes[its.name.eq 'OmniFocus'].get.size > 0
cache = JCache::Cache['of-cache']
of = app('OmniFocus').default_document
today = Date.today
cache['done'] = of.flattened_tasks[its.completed.eq(true).and(its.completion_date.eq(today))].get.map{ |t| t.name.get }
cache['flagged'] = of.flattened_tasks[its.completed.eq(false).and(its.blocked.eq(false).and(its.flagged.eq(true)))].get.map{ |t| t.name.get }
cache.save
The change over to MacRuby is relatively painless - the main difference I noticed is that everything that was once snake_case
is now camelCase
, 50% of your usual get calls can be removed, and you need to require "rubygems"
once again:
#!/usr/local/bin/macruby
require 'rubygems'
require 'jcache'
require 'date'
framework 'ScriptingBridge'
def app str
SBApplication.applicationWithBundleIdentifier(str)
end
exit unless app('com.apple.systemevents').processes.any?{ |p| p.name == 'OmniFocus' }
cache = JCache::Cache['of-cache']
of = app('com.omnigroup.omnifocus').defaultDocument
today = Date.today
ft = of.flattenedTasks
cache['done'] = ft.select{ |t| t.completed && t.completionDate.get.to_date == today}.map(&:name)
cache['flagged'] = ft.select{ |t| t.flagged &&!t.blocked &&!t.completed }.map(&:name)
cache.save
There is one interesting bug I found - I have no idea if the problem is on MacRuby's end, or the scripting bridge, or the program itself, or buried somewhere in my system:
#!/usr/local/bin/macruby
framework 'ScriptingBridge'
app = SBApplication.applicationWithBundleIdentifier('com.omnigroup.omnifocus')
first_project = app.defaultDocument.projects[0]
# This works
num_tasks = first_project.rootTask.tasks.size
# => 20
# But this doesn't...
first_project.rootTask.tasks.each{ |t| puts t.name }
# => [SBObject classForCode:]: unrecognized selector sent to instance 0x400178dc0
The equivalent in AppleScript works fine:
tell application "OmniFocus"
tell default document
set firstProject to first project
set theTasks to tasks of root task of firstProject
display alert (length of theTasks as string)
repeat with theTask in theTasks
display alert (name of theTask as string)
end repeat
end tell
end tell
For the moment, I'm using the following workaround:
class Task
@tasks = {}
def self.load
@tasks = omnifocus.defaultDocument.flattenedTasks.select{|t|!t.completed && t.tasks.empty? }.group_by{ |t| (cp = t.containingProject.get) && cp.id }
end
def self.[] p
@tasks[p.id]
end
end