#!/usr/bin/env ruby require 'rubygems' require 'rgl/adjacency' require 'rgl/dot' require 'osx/foundation' class Singleton attr_accessor :members, :label def initialize(label, members=[]) @members = members @label = label end def each_member @members.each do |member| yield member end end end class NIBContext CONNECTIONS_KEY = "com.apple.ibtool.document.connections" SINGLETON_LABEL_KEY = "source-label" MEMBER_LABEL_KEY = "destination-label" def initialize(hash) @singletons = {} hash[CONNECTIONS_KEY].each_value do |connection| key = connection[SINGLETON_LABEL_KEY].to_s @singletons[key] ||= Singleton.new(key) @singletons[key].members << connection[MEMBER_LABEL_KEY].to_s end end def get_graph return @composition_graph if @composition_graph @composition_graph = RGL::DirectedAdjacencyGraph.new @singletons.each_value do |singleton| singleton.each_member do |member| @composition_graph.add_edge member, singleton.label end end @composition_graph end end class Nib2DotGraph include OSX CONNECTIONS_FILE = File.join(ENV['TMPDIR'], "NIBConnections.plist") def usage(err_msg=nil) puts err_msg if err_msg puts "Usage: #{File.basename(__FILE__)} [file] [format]" exit 1 end def run if ARGV.size == 0 raise "NIB file must be provided." elsif !File.exists?(ARGV[0]) raise "Could not find #{ARGV[0]} NIB" end output_file = ARGV[1] || "graph" format = ARGV[2] || "png" File.open(CONNECTIONS_FILE, "w") do |f| f.print(ibtool({:connections => nil}, ARGV[0])) end nib_context = NIBContext.new(NSDictionary.dictionaryWithContentsOfFile(CONNECTIONS_FILE)) nib_context.get_graph.write_to_graphic_file(format, output_file) if %x|which dot|.empty? puts "Unable to write image #{File.basename(output_file)}.#{format} because 'dot' could not be found in $PATH (http://http://www.graphviz.org)" puts "Wrote #{File.basename(output_file)}.dot to #{File.dirname(output_file)}" else puts "Wrote #{File.basename(output_file)}.dot and #{File.basename(output_file)}.#{format} to #{File.dirname(output_file)}" end end private def ibtool(options={}, *args) command_options = options.map { |k,v| "--#{k} #{v}"}.join(" ") %x|ibtool #{command_options} #{args.join(" ")}| end end if $0 == __FILE__ script = Nib2DotGraph.new begin script.run rescue Exception => e script.usage e.message end end