<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>OnRails.org: Scripting the Leopard Terminal</title>
    <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Ruby On Rails and related matters.</description>
    <item>
      <title>Scripting the Leopard Terminal</title>
      <description>&lt;p&gt;Hypothetical situation: you&amp;#8217;re sitting down with your favorite tasty beverage close at hand for some Rails hacking, and what commands do you run every single time?  It&amp;#8217;s probably something like this:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;&lt;span class="caps"&gt;RSI&lt;/span&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_sh "&gt;cd Projects/KillerApp
mate .
rake log:clear
tail -f log/development.log

[Command - T for new tab]
cd Projects/KillerApp
mongrel_rails start

[Command - T for new tab]
cd Projects/KillerApp
ruby script/console

[Command - T for new tab]
cd Projects/KillerApp
rake

...etc...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Hmmm.  We&amp;#8217;re coding &lt;span class="caps"&gt;DRY&lt;/span&gt;, but this bootstrap process doesn&amp;#8217;t seem very &lt;span class="caps"&gt;DRY&lt;/span&gt;.  This had been bugging me, so I set out on a Google quest to learn to script Terminal.app in Leopard so that I could do something about it.  I first looked at &lt;a href="http://www.apple.com/applescript/"&gt;AppleScript&lt;/a&gt;, since that&amp;#8217;s the de facto scripting language of all things Apple.  Given a specimen, I could decipher what it was trying to do reasonably well, but going the other way was difficult&amp;#8212;starting with a goal, I was generally unsuccessful in trying to express it in AppleScript.  This was the best I could come up with:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;utopia&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_AppleScript "&gt;tell executive SteveJobs
   tell developers at Apple
      set theScriptingLanguage of OSX to Ruby
   end tell
end tell&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Which was a good thought, but didn&amp;#8217;t actually do much.  At &lt;a href="http://rubyconf.org/"&gt;RubyConf&lt;/a&gt; last year, &lt;a href="http://chopine.be/lrz/"&gt;Laurent Sansonetti&lt;/a&gt; talked about &lt;a href="http://rubyosa.rubyforge.org/"&gt;RubyOSA&lt;/a&gt;, a scripting bridge between Ruby and the Apple Event Manager, which sounds great, because you can code in Ruby and control AppleScript-able applications, like iTunes.  So I ran this:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;fail&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rbosa&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;terminal&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;OSA&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;app&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;Terminal&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Which resulted in this rather discouraging output:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;rubyosa-error&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;RuntimeError&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="constant"&gt;Can&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;t get the target bundle signature
    from /Library/Ruby/Gems/1.8/gems/rubyosa-0.4.0/lib/rbosa.rb:329:in `__scripting_info__&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="ident"&gt;from&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="constant"&gt;Library&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="constant"&gt;Ruby&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="constant"&gt;Gems&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="number"&gt;1.8&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;gems&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;rubyosa&lt;/span&gt;&lt;span class="punct"&gt;-&lt;/span&gt;&lt;span class="number"&gt;0.4&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;lib&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;rbosa&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;rb&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;&lt;span class="number"&gt;329&lt;/span&gt;&lt;span class="symbol"&gt;:in&lt;/span&gt; `&lt;span class="ident"&gt;app&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;
    from (irb):3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Hmmm.  Not exactly useful.  Maybe if I did my coding in iTunes, I could use RubyOSA to build some useful automation.  Hopefully someone at Apple can fix Terminal&amp;#8212;Hint, Hint.  Back to Google&amp;#8230;&lt;/p&gt;


	&lt;p&gt;I eventually ran across Matt Mower&amp;#8217;s &lt;a href="http://matt.blogs.it/entries/00002674.html"&gt;scripting a better &amp;#8216;cd&amp;#8217; and then some&lt;/a&gt; article.  Beautiful.  Exactly what I wanted.  Except I didn&amp;#8217;t want to have to switch to iTerm just to use it.  I&amp;#8217;m fairly happy with the native Terminal.app in Leopard&amp;#8212;the tabs are nice.  My biggest complaint is that you cannot name individual tabs, which seems like a glaring omission.  Hopefully that will be updated soon.&lt;/p&gt;


	&lt;p&gt;So Matt&amp;#8217;s gp command uses another Ruby / Apple event bridge, called &lt;a href="http://rb-appscript.rubyforge.org/"&gt;Appscript&lt;/a&gt;, which &amp;#8220;allows you to control scriptable Mac &lt;span class="caps"&gt;OS X&lt;/span&gt; applications using ordinary Ruby scripts&amp;#8221;.  Sounds cool, and most of the examples feature TextEdit, which isn&amp;#8217;t Terminal, but at least is more of a developer application than iTunes, so we seem to be on the right track.&lt;/p&gt;


	&lt;p&gt;&lt;code&gt;gem install rb-appscript&lt;/code&gt;, and let&amp;#8217;s play.  In &lt;span class="caps"&gt;IRB&lt;/span&gt;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;appscript1&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_irb "&gt;&amp;gt;&amp;gt; require 'appscript'
&amp;gt;&amp;gt; include Appscript
&amp;gt;&amp;gt; term = app('Terminal')
=&amp;gt; app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;)
&amp;gt;&amp;gt; term.windows
=&amp;gt; app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows
&amp;gt;&amp;gt; term.windows.first
=&amp;gt; app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.first&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Okay, this is a bit strange&amp;#8212;it seems to just repeat what you say back to you.  After a bit of playing, I understand what&amp;#8217;s going on.  Some of the methods aren&amp;#8217;t executed until you explicitly tell them to execute.  For instance:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;appscript2&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_irb "&gt;&amp;gt;&amp;gt; term.windows.get
=&amp;gt; [app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.ID(915), app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.ID(498), app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.ID(667)]
&amp;gt;&amp;gt; term.windows.first.get
=&amp;gt; app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.ID(915)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Why?  Most likely, this is for efficiency reasons.  With many of these &amp;#8220;Event Bridge&amp;#8221; solutions, there is a significant &amp;#8220;toll&amp;#8221; that must be paid to cross the bridge.  If we can batch together a string of method invocations and send them across the bridge for a single round trip, it performs much better than multiple round trips would.&lt;/p&gt;


	&lt;p&gt;At any rate, we can now get a handle on the Terminal application and its already-open windows.  The next step is to be able to open new windows and tabs.  There is a somewhat useful tool on the rb-appscript download page called ASDictionary, which can examine an application and dump out the objects and methods that it exposes to the bridge.  Kinda like rdoc for AppleScript.  Running the dictionary against Terminal.app, I found the &lt;code&gt;do_script(command)&lt;/code&gt; method, which, when called on the terminal application object, launches a new terminal window and runs the specified &lt;span class="caps"&gt;UNIX&lt;/span&gt; command in it.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;appscript3&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_irb "&gt;&amp;gt;&amp;gt; term.do_script(&amp;quot;ls&amp;quot;)
=&amp;gt; app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.ID(951).tabs[1]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;If you&amp;#8217;re following along, you should have a new Terminal window containing a listing of the files and folders in your home directory.  Also, notice that the command returns a reference to the first (and only) tab in the new window&amp;#8212;we&amp;#8217;ll come back to that later.  So, new windows, check; now for new tabs.&lt;/p&gt;


	&lt;p&gt;The Appscript examples show creating new TextEdit documents by executing:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;appscript4&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_irb "&gt;app('TextEdit').documents.end.make(:new =&amp;gt; :document)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;And the Appscript dictionary of Terminal showed the &lt;code&gt;make&lt;/code&gt; method, and &lt;code&gt;window&lt;/code&gt; and &lt;code&gt;tab&lt;/code&gt; classes, so I figured something like this might work:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;appscript5&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_irb "&gt;&amp;gt;&amp;gt; window = term.windows.first.get
=&amp;gt; app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.ID(915)
&amp;gt;&amp;gt; window.make(:new =&amp;gt; :tab)
Appscript::CommandError: CommandError
        OSERROR: -10000
        MESSAGE: Apple event handler failed.
        COMMAND: app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.ID(915).make({:new=&amp;gt;:tab})

    from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.4.0/lib/appscript.rb:505:in `_send_command'
    from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.4.0/lib/appscript.rb:585:in `method_missing'
    from (irb):11&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Appscript is having none of that.  After many frustrating, fruitless attempts to create a new tab, I found a workaround in native AppleScript &lt;a href="http://hohonuuli.blogspot.com/2007/11/applescript-for-open-terminal-here.html"&gt;here&lt;/a&gt;.  Here&amp;#8217;s the Appscript translation:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;appscript6&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_irb "&gt;app(&amp;quot;System Events&amp;quot;).application_processes[
        &amp;quot;Terminal.app&amp;quot;
     ].keystroke(&amp;quot;t&amp;quot;, :using =&amp;gt; :command_down)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Well, okay, sending a Command-T keystroke works, but it&amp;#8217;s a little disappointing.  Anyone who knows how to programatically create a new tab, feel free to chime in on the comments, and I&amp;#8217;ll update the script.&lt;/p&gt;


	&lt;p&gt;&lt;code&gt;do_script(command)&lt;/code&gt; also takes an optional parameter specifying options.  One of the available options is &lt;code&gt;:in&lt;/code&gt;, which tells terminal in which window and tab to run the command.  Putting this together, we can run a command in the new tab we just created:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;appscript7&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_irb "&gt;app('Terminal').do_script(&amp;quot;ls&amp;quot;, :in =&amp;gt; window.tabs.last.get)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;We don&amp;#8217;t have a handle to the new tab, because it was created via hackery, so we need a handle to its parent window object so we can get at its tabs.  Well, our first command we ran with do_script returned us a handle to the first tab of the window, surely we can get the window from that.  Right?  Anyone?&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;appscript8&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_irb "&gt;&amp;gt;&amp;gt; tab = term.do_script(&amp;quot;ls&amp;quot;)
=&amp;gt; app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.ID(1159).tabs[1]
&amp;gt;&amp;gt; tab.window
RuntimeError: Unknown property, element or command: 'window'
    from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.4.0/lib/appscript.rb:591:in `method_missing'
    from (irb):11
    from :0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;(Sigh)  Not so much.  Time for a hack on a hack, and this one is just embarrassing.  Observant readers have probably noticed that the tab referenced shows its parent window id in the string of object references.  What if we use that to get a handle to the parent window?  Something like this:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;evil-hack&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_irb "&gt;&amp;gt;&amp;gt; window = eval(&amp;quot;app(\&amp;quot;/Applications/Utilities/Terminal.app\&amp;quot;).windows.ID(1159)&amp;quot;)
=&amp;gt; app(&amp;quot;/Applications/Utilities/Terminal.app&amp;quot;).windows.ID(1159)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;I&amp;#8217;m not proud of it, but it works.  Again, if someone knows the &amp;#8220;right&amp;#8221; way to do this, please let me know&amp;#8212;although programatically creating a tab should obviate the need for this hack, too.&lt;/p&gt;


	&lt;p&gt;Another thing I wanted to change from the original script was that with Matt&amp;#8217;s solution, you need to specify the type of project you are opening, either as a command-line argument, or by symlinking the script to a new name with the type embedded.  I want the computer to figure all that out for me.  Computers are real smart.  So my script includes some configurable heuristics for determining project type based on the folder contents.  You should be able to specify set of files and folders that defines a project type.  For Rails, I used the &amp;#8220;skeleton&amp;#8221; folders that every Rails app starts with.  I haven&amp;#8217;t been programming in Erlang very long, so honestly I just guessed at the folders based on some projects I have seen.  If you&amp;#8217;re a more experienced Erlang programmer, and feel that the project detection or task list should be changed, please let me know.&lt;/p&gt;


	&lt;p&gt;Matt later enhanced his script to &lt;a href="http://matt.blogs.it/entries/00002676.html"&gt;label the iTerm tabs&lt;/a&gt; so that you can easily find the tab you need, so naturally I &lt;del&gt;stole&lt;/del&gt; incorporated this idea too.  It sorta works in Leopard&amp;#8217;s Terminal&amp;#8212;it does change the name of the Terminal window, but the tab name is unaffected, so you still have to flip through the tabs and watch the window name.  This is my biggest request for a feature enhancement for the Terminal,  Hint, Hint again, Apple.&lt;/p&gt;


	&lt;p&gt;So enjoy.  You can use the script anywhere&amp;#8212;it will look first in your current directory for a matching folder (you only have to specify a non-ambiguous substring of the project folder name), then falls back to the &amp;#8220;project root&amp;#8221; folder you specify (currently ~/development, &amp;#8216;cause that&amp;#8217;s what I use.).  You can configure how deep it should recurse your projects so that the disk seeking time doesn&amp;#8217;t eat up all the time you&amp;#8217;re saving from not typing the same &amp;#8220;cd&amp;#8221; commands over and over.&lt;/p&gt;


	&lt;p&gt;You can download the finished script &lt;a href="/files/hack"&gt;here&lt;/a&gt;.  I call it hack, because &lt;code&gt;hack KillerApp&lt;/code&gt; flows so nicely as a command.  Also, that&amp;#8217;s the way I roll.  Feel free to send in money, or flattery, or hate mail, I suppose.  Thanks to Matt Mower for the inspiration and also to all the other references I&amp;#8217;ve linked!&lt;/p&gt;</description>
      <pubDate>Wed, 28 Nov 2007 19:38:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:d820d000-a3ff-4930-920c-f3e4e64bae80</guid>
      <author>Solomon White</author>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by Tony</title>
      <description>&lt;p&gt;Thank You!&lt;/p&gt;</description>
      <pubDate>Tue, 03 Jun 2008 06:16:54 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:0b19f558-f46b-4bdb-93fb-2297be013990</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-3657</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by Tony</title>
      <description>&lt;p&gt;Thank You!&lt;/p&gt;</description>
      <pubDate>Tue, 03 Jun 2008 06:16:49 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:fd9f2b36-88c8-40be-8b5a-165ffefae448</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-3656</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by Chris Blackburn</title>
      <description>&lt;p&gt;Thanks Solomon!  I took what you discovered and made my own script to set different colors for each type of tab in my workflow.&lt;/p&gt;


	&lt;p&gt;It is &lt;a href="http://blog.cbciweb.com/articles/2008/05/02/scripting-mac-terminal-using-ruby" rel="nofollow"&gt;here&lt;/a&gt; for anyone interested&lt;/p&gt;


	&lt;p&gt;Thanks again and keep posting!&lt;/p&gt;</description>
      <pubDate>Sat, 03 May 2008 01:51:33 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:ac97570f-9641-4b8f-bada-22837aed62f7</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-3517</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by nuzz</title>
      <description>&lt;p&gt;well I think I answered my own question.&lt;/p&gt;


	&lt;p&gt;app(&amp;#8220;System Events&amp;#8221;).application_processes[&amp;#8220;Terminal.app&amp;#8221;].keystroke(&amp;#8220;t&amp;#8221;, :using =&amp;gt; [:command_down, :shift_down])&lt;/p&gt;


	&lt;p&gt;Now all I need to figure out is how to programatically supply text to the tab naming plugin that the cmd+shift+t keystroke initiates.&lt;/p&gt;</description>
      <pubDate>Fri, 11 Apr 2008 02:09:49 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:dc7ef330-548a-4b9f-83fa-70e05113e058</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-3462</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by nuzz</title>
      <description>&lt;p&gt;is there a way to add modifiers to the keystroke? ex. command + shift + t I can&amp;#8217;t seem to figure out this can be done. I started using this SIMBL based terminal.app plugin that allows you to rename the tabs properly.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://ericanderson.us/2008/03/02/terminalapp-tab-namer-v01-alpha/" rel="nofollow"&gt;http://ericanderson.us/2008/03/02/terminalapp-tab-namer-v01-alpha/&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Great writeup, this has helped me a lot.&lt;/p&gt;</description>
      <pubDate>Fri, 11 Apr 2008 01:46:58 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:f88d3a27-d125-48dc-aa1d-49d6ad2eb8d9</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-3461</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by nb</title>
      <description>&lt;p&gt;Cool stuff, Solomon.  Based on your work, I created a similar script that suits my work flow.  Check it out:&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://the-banana-peel.saltybanana.com/2008/02/new-terminal-tab-in-current-directory.html" rel="nofollow"&gt;http://the-banana-peel.saltybanana.com/2008/02/new-terminal-tab-in-current-directory.html&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 19 Feb 2008 08:36:46 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:412906fa-6b67-404b-b29c-0a72e5b30d7e</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-3268</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by nb</title>
      <description>&lt;p&gt;Cool stuff, Solomon.  Based on your work, I created a similar script that suits my work flow.  Check it out:&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://the-banana-peel.saltybanana.com/2008/02/new-terminal-tab-in-current-directory.html" rel="nofollow"&gt;http://the-banana-peel.saltybanana.com/2008/02/new-terminal-tab-in-current-directory.html&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 19 Feb 2008 08:36:31 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:0539bd2c-371e-468f-8d16-ca20e5f94856</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-3267</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by jeff</title>
      <description>&lt;p&gt;Nice work Solomon!  I&amp;#8217;ve been looking for something like this.  I took your examples and packaged it up as a little utility:&lt;/p&gt;


	&lt;p&gt;&lt;a href="https://wush.net/svn/public/terminit/" rel="nofollow"&gt;https://wush.net/svn/public/terminit/&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Wed, 16 Jan 2008 17:13:26 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:d1da8039-f544-4535-8cf8-370efc31be3f</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-3219</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by Matt Schick</title>
      <description>&lt;p&gt;Thanks for the new tab work around via SystemEvents.  I was trying to do the same thing for safari and it wasn&amp;#8217;t having any of that &amp;#8217;.make(:new =&amp;gt; :tab &amp;#8230;&amp;#8217; either.&lt;/p&gt;</description>
      <pubDate>Mon, 10 Dec 2007 04:18:17 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:f8a86816-21d2-4c6c-8351-157badd0be70</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-3027</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by Solomon White</title>
      <description>&lt;p&gt;Thanks for commenting on my script.  (I tried to post this comment on your blog, but I get a 403).  &lt;code&gt;find&lt;/code&gt; is working for me so far&amp;#8212;it was quite slow until I started &lt;code&gt;prune&lt;/code&gt;-ing result branches and limiting recursion depth.  I&amp;#8217;ve seen that you have mentioned releasing gp as a gem.  Maybe we should combine our efforts?&lt;/p&gt;


	&lt;p&gt;Thanks,&lt;/p&gt;


	&lt;p&gt;Solomon&lt;/p&gt;</description>
      <pubDate>Thu, 29 Nov 2007 16:18:37 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:d86b50dd-97cf-40e3-b0d4-38c0c37e509e</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-2918</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by Matt Mower</title>
      <description>&lt;p&gt;Hi Solomon.&lt;/p&gt;


	&lt;p&gt;Thanks for the link back. I&amp;#8217;m glad you took on the challenge of Terminal.app, I had a go but didn&amp;#8217;t have your grit. I made a few further comments:&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://matt.blogs.it/entries/00002722.html"&gt;http://matt.blogs.it/entries/00002722.html&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Cheers,&lt;/p&gt;


	&lt;p&gt;Matt.&lt;/p&gt;</description>
      <pubDate>Thu, 29 Nov 2007 12:47:12 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:006be0eb-ff98-42b9-a7a3-faf0a70be7b1</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-2917</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by has</title>
      <description>&lt;blockquote&gt;
Sometimes, after I set the terminal window foremost, and before system events can send the keystroke, TextMate will pop up and eat the Command – T. 
&lt;/blockquote&gt;

	&lt;p&gt;Use &lt;code&gt;app('Terminal').activate&lt;/code&gt; to ensure the Terminal application is frontmost before sending the keystroke. While some GUI Scripting commands allow you to specify a target process, I think keystrokes automatically go to the frontmost application.&lt;/p&gt;


	&lt;p&gt;HTH&lt;/p&gt;</description>
      <pubDate>Thu, 29 Nov 2007 11:59:12 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:f7456f5f-10ee-4f36-a2fe-0086d1589244</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-2916</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by Solomon White</title>
      <description>&lt;p&gt;@has:&lt;/p&gt;


	&lt;p&gt;Wow, excellent info&amp;#8212;thanks for the response.  Straight from the source!&lt;/p&gt;


	&lt;p&gt;Regarding timing / stacking issues, I&amp;#8217;ve run into this with &lt;code&gt;hack&lt;/code&gt;.  Sometimes, after I set the terminal window foremost, and before system events can send the keystroke, TextMate will pop up and eat the Command &amp;#8211; T.  (so I get the ever-so-useful &amp;#8220;jump to file&amp;#8221; dialog, and no new tab)  This was a bit surprising, since I&amp;#8217;m asking system events to send the keystroke specifically to the Terminal application.&lt;/p&gt;


	&lt;p&gt;If anyone runs into this, you might have to fiddle with the order of your tasks&amp;#8212;commands that launch new windows might be best served last.&lt;/p&gt;


	&lt;p&gt;Thanks again!&lt;/p&gt;</description>
      <pubDate>Thu, 29 Nov 2007 07:17:40 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:d2bd6cc4-6881-46fa-b617-efaf31073cc3</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-2909</link>
    </item>
    <item>
      <title>"Scripting the Leopard Terminal" by has</title>
      <description>&lt;blockquote&gt; Okay, this is a bit strange—it seems to just repeat what you say back to you. After a bit of playing, I understand what’s going on. Some of the methods aren’t executed until you explicitly tell them to execute.&lt;/blockquote&gt;

	&lt;p&gt;Technically, what happens is that all &amp;#8216;property&amp;#8217; and &amp;#8216;element&amp;#8217; methods return an Appscript::Reference instance; only &amp;#8216;command&amp;#8217; methods actually send an Apple event to the target application.&lt;/p&gt;


	&lt;p&gt;Apple event IPC is quite unusual in that it&amp;#8217;s actually based on remote procedure calls plus queries, rather than the more common object-oriented approaches used by COM, CORBA, Distributed Objects, etc. As you say, this is for sake of efficiency: Mac OS 7-9 had pretty awful multiprocessing support, and context switches were very expensive and limited to a maximum of 60 per second. The query-based &amp;#8220;do more with less&amp;#8221; approach minimises the number of Apple events that need to be sent to get things done. Takes a bit of getting used to if you&amp;#8217;re coming from an OOP background, and isn&amp;#8217;t without its problems (it&amp;#8217;s notoriously tricky to implement reliably on the application side), but when it does work it&amp;#8217;s really quite elegant.&lt;/p&gt;


&lt;blockquote&gt; Anyone who knows how to programatically create a new tab, feel free to chime in on the comments, and I’ll update the script.&lt;/blockquote&gt;

	&lt;p&gt;Looks like Terminal&amp;#8217;s &amp;#8216;make&amp;#8217; command is broken, unfortunately; normally I&amp;#8217;d expect at least one of the following to work (depending on how the application likes the insertion location constructed):&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
app('Terminal').make(:new=&amp;gt;:tab, :at=&amp;gt;app.windows[1])
app('Terminal').make(:new=&amp;gt;:tab, :at=&amp;gt;app.windows[1].tabs.end)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;but neither of these worked, nor did any other variations I tried. I also tried making new windows, but that returned errors too:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
app('Terminal').make(:new=&amp;gt;:window)
app('Terminal').make(:new=&amp;gt;:window, :at=&amp;gt;app.windows.end)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;It&amp;#8217;s frustrating, but as longtime AppleScripters can tell you, these sorts of obvious bugs are unfortunately not that uncommon, even in Apple applications. All I can suggest is filing a bug report with Apple and keep using your System Events-based workaround to script Terminal&amp;#8217;s GUI in the meantime.&lt;/p&gt;


&lt;blockquote&gt;Well, our first command we ran with do_script returned us a handle to the first tab of the window, surely we can get the window from that. Right? ... Not so much. Time for a hack on a hack, and this one is just embarrassing. ... Again, if someone knows the “right” way to do this, please let me know&lt;/blockquote&gt;

	&lt;p&gt;If you dig down through appscript&amp;#8217;s API layers you do eventually come to a visitor-style API that allows you to get at the innards of application references. However, it&amp;#8217;s not publicly documented (info is available on request though) and not something I&amp;#8217;d encourage- as you say, it&amp;#8217;s really the application&amp;#8217;s job to provide users with the info they need, and anything else is just a hack.&lt;/p&gt;


	&lt;p&gt;Since Cmd-T always creates the new tab in the frontmost window, personally I&amp;#8217;d just wing it and use:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
window_ref = app('Terminal').windows[1].get
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;By-index references aren&amp;#8217;t as reliable as by-id reference since they tend to change according to stacking order, but if you send the above immediately after the tab is created (i.e. before anyone has a chance to bring another Terminal window frontmost) you should get a permanent by-id reference to the correct window back from Terminal:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;
app("/Applications/Utilities/Terminal.app").windows.ID(1159)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Unfortunately, while windows can be identified by unique id, it looks like tabs are still stuck with the by-index form, so if someone or something else creates a new tab in the same window later on I&amp;#8217;m not quite sure how you&amp;#8217;re meant to keep a reliable handle on your own tab. Though again this is a not uncommon dilemma when scripting applications, and I expect most folks just wing it here too, but feel free to file a feature request with Apple asking for Terminal tabs to support the by-id reference form in future releases.&lt;/p&gt;


	&lt;p&gt;HTH&lt;/p&gt;


	&lt;p&gt;has &lt;br /&gt;
&amp;#8212;&lt;br /&gt;
&lt;a href="http://appscript.sourceforge.net"&gt;http://appscript.sourceforge.net&lt;/a&gt; &lt;br /&gt;
&lt;a href="http://rb-appscript.rubyforge.org"&gt;http://rb-appscript.rubyforge.org&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 29 Nov 2007 00:05:18 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:10aab4a3-394d-410f-9029-610ffbfa8406</guid>
      <link>http://onrails.org/articles/2007/11/28/scripting-the-leopard-terminal#comment-2900</link>
    </item>
  </channel>
</rss>
