Editor's note: Rael Dornfest, coauthor of Mac OS X Panther Hacks, has selected these three hacks from the book for your sampling pleasure. The first two detail how to find anyone in your Address Book who has an Amazon Wish List, and how to build a GUI to your Unix scripts with a bit of Perl or Python; the third is just for fun. Enjoy.
This hack will find anyone in your Address Book that has an Amazon Wish List, but it's up to you buy them something!
The Macintosh operating system has always been a rather scriptable environment. Many applications support scriptable events that let you automate tasks or integrate separate programs. With Mac OS X, there are even more opportunities for scripting, now that the ultimate scriptable environment, Unix, is under the hood. But why limit yourself to the Desktop? The Mac's script-friendly environment can integrate your Desktop application with web applications such as Amazon.
This hack is a quick example that loosely integrates the Mac Address
Book with Amazon. Using AppleScript
and an underlying Unix tool called
curl, this script finds people in your Address
Book that also have an Amazon Wish List. This script might help you
find a gift for someone you know, or it might just give you a
different perspective on someone by finding what
they're interested in. Most importantly, though, it
shows how Desktop applications can become smarter by integrating with
web applications.
|
Related Reading Mac OS X Panther Hacks |
Open the Script Editor (Applications→AppleScript), enter the following code, and save the script with a suitably snappy name, like Get Wish Lists:
(*
Find Wish Lists in Address Book
The script loops through people in your Address Book, checking Amazon
to see if they have a Wish List. If their Wish List is found, you
have the option to view it in your default browser.
by Paul Bausch
*)
-- set some variables for the curl command
set userAgent to "Mozilla/5.0 (Macintosh; U; PPC Mac OSX; en-us)"
set curlCommand to "curl -i -b -A -L \"" & userAgent & "\" "
-- open Address Book and loop through people
tell application "Address Book"
repeat with thisPerson in the people
set thisName to name of thisPerson
repeat with thisAddress in email of thisPerson
set thisEmail to value of thisAddress
-- build the URL that will search for the Wish List
set baseURL to "http://www.amazon.com/gp/registry/search.html"
set thisURL to baseURL & "/?type=wishlist\\&field-name=" & thisEmail
-- use curl to fetch the search page
set thisWishPage to do shell script curlCommand & thisURL
-- if the search returns what appears to be a match, prompt the user
if thisWishPage contains "&id=" then
set theAction to display dialog thisName & ¬
" has an Amazon wishlist." buttons {"View", "Ignore"}
if button returned of theAction is "View" then
-- Find the ID on the page
set beginID to (offset of "&id=" in thisWishPage) + 4
set endID to (offset of "'s Wish List" in thisWishPage) - 1
set thisWishID to get text beginID thru endID of thisWishPage
if thisWishPage contains "&id=" thenset beginID to 1
set endID to (offset of "\">" in thisWishID) - 1
set thisWishID to get text beginID thru endID of thisWishID
-- Open the default browser to the Wishlist page
tell me to open location "http://www.amazon.com/o/registry/" ¬
& thisWishID
end if
end if
end repeat
end repeat
end tell
The script starts by setting some variables for
curl that will be used later.
TIP: If you'd like learn more about
curland what these settings mean, open a Terminal window (Applications→Utilities→Terminal) and typeman curl. You'll get the complete documentation that explains what all of these command switches do. Or, you can read "Downloading Files from the Command Line" [Mac OS X Hacks, Hack #61].
Then, the script loops through all of the entries in the Address
Book, looking for email addresses. It uses each individual email
address to build a URL that queries Amazon for a Wish List associated
with that address. This is where the script needs to step out of its
AppleScript confines to the larger world of Unix commands. With the
do shell script command, curl
contacts Amazon to see if this person has a Wish List.
If a Wish List is found, the display dialog
command brings up a window like the one in Figure 2-6, with two options: View or Ignore.
Clicking View tells the script to open the Wish List with the default
browser. Because this command takes place within the tell
application "AddressBook" block, the
context-switching tell me command needs to precede
AppleScript's open location
function.
You can run the code directly from the Script Editor by clicking the Run button.
To always have it a click away from your Address Book, move the script to the Library/Scripts/Address Book Scripts folder. This way, it'll be available from your Address Book's Scripts menu. Run the script at any time by selecting Script Menu→Get Wish Lists from the Address Book application's menu bar.
—Paul Bausch
|
Build a graphical dialog front-end to your Unix scripts with just a snippet of Perl or Python glue code.
When it comes to combining Unix scripting and GUI, more often than not, a little dab will do. Such was the case with the Mac OS X Installer I was building for Blosxom (http://www.blosxom.com), my designed-for-Mac-yet-runs-anywhere weblog [Hack #30] application.
I'd built a nice Mac OS X package (http://developer.apple.com/documentation/DeveloperTools/Conceptual/SoftwareDistribution), which turned my otherwise rather Unix-y application into a double-clickable install. However, I still needed to set a configuration setting or three and didn't want to require my users to resort to editing Perl scripts by hand. So, I set about divining an elaborate—and not a little bit grotty—scheme involving one part shell scripting, two parts Perl, and a pinch of AppleScript.
As part of its standard procedure, the Mac OS X Installer runs any postflight shell script it finds in a package at the end of the installation process—thus the name postflight (http://developer.apple.com/documentation/DeveloperTools /Conceptual/SoftwareDistribution/Concepts/sd_pre_post_processing.html #//apple_ref/doc/uid/20001945/TPXREF19). This gives the package maker a chance to perform any last-minute reshuffling, fix permissions, run additional scripts, and generally do anything you can do on and from the command line. I slipped a call to a Perl script, configure.pl, into Blosxom's postflight script like so:
echo Running perl/applescript configuration scripts...
sudo /usr/bin/perl "$PACKAGE_PATH/Contents/Resources/configure.pl"
TIP: The output generated by
echoin the preceding shell snippet and
One of the things this
configure.pl Perl script does is call an
AppleScript script, configure.scpt, to prompt
the user through a series of dialog boxes for the answers to various
configuration questions and retrieve the responses as a string of
key/value pairs (stored in $options), later to be
applied automagically to the Blosxom application itself. Calling the
AppleScript script itself is just a matter of passing the script to
the osascript command-line utility that takes care
of actually running it for me:
print "... prompting for answers to configuration questions\n";
my $options = `/usr/bin/osascript
$PACKAGE_PATH/Contents/Resources/configure.scpt 2>&1`;
...
# Do something magical with the contents of $options
...
Finally we come to the GUI bit itself. The
configure.scpt AppleScript consists of nothing
more than a series of display dialog statements,
each prompting for a single configuration setting and saving the
user's responses:
tell application "Finder"
activate
display dialog "You're mere moments away from your very own blog." & return
& return & "Before letting you get on with it, however, let's get a few last
settings taken care of for you." & return & return & "To skip this step,
click Cancel." & return with icon 1
display dialog "What would you like to call your blog?" default answer
"blosxom"
set blog_title to (text returned of result)
display dialog "How would you describe your blog (keep it short)?" default
answer "yet another blosxom blog"
set blog_description to (text returned of result)
display dialog "What language code would you like to associate with
your blog (e.g. en=English, fr=French, de=German)?" default answer "en"
set blog_language to (text returned of result)
display dialog "How many entries would you like displayed on your
blog's home page?" default answer "40"
set blog_num_entries to (text returned of result)
display dialog "What URL should be used for your blog (leave blank
to have Blosxom figure this out for itself)?" default answer ""
set blog_url to (text returned of result)
set result to ("$$$blog_title=" & blog_title & "$$$blog_description=" &
blog_description & "$$$blog_language=" & blog_language & "$$$num_entries="
& blog_num_entries & "$$$url=" & blog_url)
end tell
For instance, line 1 displays prompts for a blog name, as shown in
Figure 2-24, while line 2 saves the result to the
AppleScript variable blog_title.

Figure 2-24. One of a series of AppleScript dialogs
Line 3 strings all the various responses together to be returned to the calling configure.pl Perl script.
While this actually does work remarkably well, it is overly involved (albeit not particularly complex) for the rather simple requirements it has to meet. Also, it has quite a staccato feel to it from the user's point of view, with dialog boxes bouncing up and down one after another. Unfortunately, there was just no way, without doing quite a bit more programming, that I could get around sending the user (very possibly a newbie) to the command line—a no-no in my book.
Thankfully, I stumbled upon Carsten Blüm's Pashua (http://www.bluem.net/downloads/pashua_en; donateware), a rather nifty tool for creating simple, native Aqua GUIs from Perl, Python, PHP, Tcl, Rexx, and AppleScript. Pashua's capabilities are limited to dialog windows, but it does provide a nice collection of widgets: labels, tool tips, text fields, password fields, checkboxes, radio buttons, pop-up and combo boxes, file selectors, and buttons.
I can now replace that staccato series of dialog boxes with a single dialog box that sports all the configuration questions I need—at least one window's worth.
Installing Pashua is just a matter of downloading the disk image and copying the contents across to your hard drive. To have the Pashua.app itself readily accessible from anywhere you care to use it, you might consider dropping it into your Applications folder. You'll also want to make sure that the associated Perl or Python module (Pashua.pm and Pashua.py, respectively) you'll be using in your code is somewhere Perl or Python will expect to find it.
My preference is to keep all of the Pashua bits together, simply creating a Pashuafolder in my Documents/Code directory (where I do all of my coding).
You just need to be sure that the Pashua.app application and appropriate Pashua.pm or Pashua.py module is available to your script. The simplest way to do this is to copy these into the same folder as your script and distribute the entire folder to anyone who'll be using your software.
Pasha scripts are simply wrappers around a configuration string that's fed to the Pashua application. This string defines the dialog window itself and the various GUI widgets you'd like Pashua to display.
While I'll not delve into every widget in turn, each definition looks a little something like this:
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
title_tooltip=Keep it simple.
title is the name I've given to
this particular widget; I could have used any string of letters or
numbers (e.g., title1, thingy,
or wotsit22). Each widget is described by using
attributes, prefixed with an underscore (_) and
appended to the widget's name. Most widgets take
four attributes: type
(textfield, button,
checkbox, etc.), label (what
appears alongside the checkbox or on the button),
width (in pixels), and a
default (which radio button is initially selected,
the initial value of a textfield, etc.). So, this
widget is a 200-pixel-wide textfield named
title, labeled What do you want to
callyour blog?, and with an initial
value of My Blosxom.
TIP: It's all much like designing an HTML form, except that attributes are listed as
name_attribute, one per line, rather than in an HTML tag. The just-describedtextfieldmight look something like this in an HTML form:What do you want to call your blog? <input type="text" name="title" size="200" value="My blosxom" />
Here's another:
future_type=radiobutton
future_label=Should I show entries from the future?
future_option=No
future_option=Yes
future_default=No
This one is a radiobutton group named
future, labeled Should I showentries from the future?, with
No and Yes options, the former
selected by default.
As for the dialog window itself, there's not much to fiddle with:
# Set window title
windowtitle=Blosxom Configuration Wizard
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set the window to brushed metal; the default is regular Aqua
appearance=metal
This window is titled Blosxom Configuration Wizard
and sports a 95% opaque (5% transparent) brushed-metal look.
You'll find complete documentation for these directives in the Pasha package itself (Pashua/Documentation/documentation.html) and various hints in the code and comments of the examples used in this hack.
Let's start out with a simple example, one that displays a single dialog window with some number of GUI widgets representing the minimal configuration settings Blosxom requires.
While I could have chosen any of the supported languages, I thought I'd stick to Perl, because that's what Blosxom is written in and I can write Perl in my sleep (although, admittedly, I'm more of a Python devotee at heart).
To a folder named Simple (Documents/Code/Pashua/Simple), I copied Pashua.app and Pashua.pm. I then wrote the following script, borrowing the basic framework from the example code (Pashua/Examples/example.pl) found in the Pashua distribution. I saved the file as Simple.pl:
# !/usr/bin/perl -w
# Simple.pl
# A simple Pashua example
# Add the local directory to the queue of places to look for Pashua.pm
BEGIN {
use File::Basename;
unshift @INC, dirname($0);
}
use strict;
use Pashua;
# Define what the dialog should be like
# Take a look at Pashua's Readme file for more info on the syntax
my $conf = <<EOCONF;
# Lines starting with a hash character are
# comments, empty lines are ignored
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set window title
windowtitle=Blosxom Configuration Wizard
txt_type=text
txt_text=Welcome to Blosxom.[return][return]You're mere moments
away from your very own blog.[return][return]This wizard will
take you through some last minute configuration settings. If
you're in need of some details, take a gander at the gory
details on the Blosxom Configuration page at
http://www.blosxom.com/documentation/users/configure/.
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
description_type=textfield
description_label=How would you describe your blog?
description_width=400
description_default=Yet another blosxom blog.
language_type=textfield
language_label=What will be your primary written language? (e.g. en=English)
language_width=25
language_default=en
future_type=radiobutton
future_label=Should I show entries from the future (i.e. post-dated entries)?
future_option=No
future_option=Yes
future_default=No
# Add a cancel button - if you like, you can set the
# button label by uncommenting the second line below
cncl_type=cancelbutton
#cncl_label=If you click here, no values will be returned
# A default button is added automatically - if you want to
# change the button title, you should uncomment the next
# two lines to override the "built-in" default button
#default_type=defaultbutton
#default_label=Click here to return the values
EOCONF
# Pass the configuration string to the Pashua module
my %result = Pashua::run($conf);
if (%result) {
print " Pashua returned the following hash keys and values:\n";
while (my($k, $v) = each(%result)) {
print " $k = $v\n";
}
}
else {
print " No result returned. Looks like
the 'Cancel' button has been pressed.\n";
}
Lines 7-12 make the Pashua.pm Perl module in the current folder available to the script.
The heart of the script is a long configuration string that defines
the various aspects of the dialog window itself (lines 20-24) and
component GUI widgets (lines 26-59) stashed in a
$conf variable.
Line 63 is where the actual work gets done: the
$conf configuration string is passed to the
Pashua::run() method of the
Pashua.pm Perl module, which, in turn, asks the
Pashua.app application to display a dialog box
that contains the various described widgets.
Lines 65-73 print the results to the Terminal window, although you'll most likely want to do something far more useful with them in any application you're building with Pashua.
Run the Perl script on the command line, like so:
$ perl Simple.pl
You'll see the Pashua application bounce into being in your Dock and your dialog pop up on the screen, as shown in Figure 2-25.

Figure 2-25. A simple Pashua-generated dialog
Click the OK button or just hit the Return key on your keyboard to send the values of the various dialog widgets to the Terminal:
$ perl Simple.pl
Pashua returned the following hash keys and values:
cncl = 0
title = My Blosxom
future = No
description = Yet another blosxom blog.
language = en
Since the lion's share of your script is Pashua configuration, the process is just about identical regardless of the language you choose. Here's the same thing (the configuration is abbreviated) in Python:
#!/usr/bin/env pythonimport Pashuaconf = """
# Lines starting with a hash character are
# comments, empty lines are ignored
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set window title
windowtitle=Blosxom Configuration Wizard
...
# two lines to override the "built-in" default button
# default_type=defaultbutton
# default_label=Click here to return the values
"""
if Result['cncl'] is '0':
print " Pashua returned the following dictionary keys and values:"
for Key in Result.keys( ):
print " %s = %s" % (Key, Result[Key])
else:
print " No result returned. Looks like the 'Cancel' button has been pressed.";
print
I created a new folder named Wizard(Documents/Code/Pashua/Wizard) and again copied Pashua.app and Pashua.pm into it.
The following script, Wizard.pl, defines two
separate dialog window configurations, $general
and $configurestatic, each to be fed serially to
the Pashua engine (additions and alterations from the code in the
preceding "Simple Example"
section—mostly the addition of a second screen
configuration—are called out in bold):
#!/usr/bin/perl -w
# Wizard.pl
# A multi-screen Pashua Wizard example
BEGIN {
use File::Basename;
unshift @INC, dirname($0);
}
use strict;
use Pashua;
# Define the Wizard's first screen
my $general = <<GENERAL;
# Lines starting with a hash character are
# comments, empty lines are ignored
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set window title
windowtitle=Blosxom Configuration Wizard: General
txt_type=text
txt_text=Welcome to Blosxom.[return][return]You're mere
moments away from your very own blog.[return][return]This
wizard will take you through some last minute configuration
settings. If you're in need of some details, take a gander
at the gory details on the Blosxom Configuration page at
http://www.blosxom.com/documentation/users/configure/.
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
description_type=textfield
description_label=How would you describe your blog?
description_width=400
description_default=Yet another blosxom blog.
language_type=textfield
language_label=What will be your primary written language? (e.g. en=English)
language_width=25
language_default=en
future_type=radiobutton
future_label=Should I show entries from the future (i.e. post-dated entries)?
future_option=No
future_option=Yes
future_default=No
cncl_type=cancelbutton
# A default button is added automatically - if you want to
# change the button title, you should uncomment the next
# two lines to override the "built-in" default button
default_type=defaultbutton
default_label=Next
GENERAL
# Define the Wizard's second screen
my $configurestatic = <<CONFIGURESTATIC;
windowtitle=Blosxom Configuration Wizard: Static Settings
staticdir_type=openbrowser
staticdir_label=Where would you like the static version of you blog to live?
staticdir_width=400
staticdir_default=/Library/WebServer/Documents
staticpwd_label=What would you like to use as your static rendering password?
staticpwd_type=password
staticpwd_width=200
staticpwd_default=
staticentries_type=radiobutton
staticentries_label=Would you like to statically render individual entries?
staticentries_option=No
staticentries_option=Yes
staticentries_default=No
editor_type=popup
editor_label=When would you like static rendering to run?
editor_width=200
editor_option=Manually
editor_option=Every 1/2 Hour
editor_option=Every 1 Hour
editor_option=Every 2 Hours
editor_option=Every 3 Hours
editor_option=Every 4 Hours
editor_option=Every 5 Hours
editor_option=Every 6 Hours
editor_option=Every 12 Hours
editor_option=Every 24 Hours
editor_default=Manually
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
CONFIGURESTATIC
# Define the rest of the wizard's screens
# ...
# Pass each configuration string in turn to the Pashua module to create
# a Wizard-like screen-by-screen interface and gather the results along
# the way
my %result = Pashua::run($general));
%result = (%result, Pashua::run($configurestatic));
# All the rest of the screens go here in the same manner
# %result = (%result, Pashua::run($shareware));
if (%result) {
print " Pashua returned the following hash keys and values:\n";
while (my($k, $v) = each(%result)) {
print " $k = $v\n";
}
}
else {
print " No result returned. Looks like the
'Cancel' button has been pressed.\n";
}
Line 104 runs Pashua::run(), feeding it the first
screen configuration held in the $general
variable. Line 181 calls Pashua::run() again,
this time feeding it the second screen, as described in
$configurestatic.
Run the Perl script on the command line, like so:
$ perl Wizard.pl
Up comes the first screen of your wizard. Click the Next button to move on to the next screen, shown in Figure 2-26.

Figure 2-26. The second screen of a Pashua-generated wizard
Click the Next button again (in your final wizard, a Finish button is probably more appropriate for the last screen) or just hit the Return key on your keyboard to send the values of the various dialog widgets across all the wizard's screens to the Terminal:
$ perl Wizard.pl
Pashua returned the following hash keys and values:
title = My Blosxom
default = 1
description = Yet another blosxom blog.
future = No
staticdir = /Library/WebServer/Documents
staticentries = No
staticpwd = s33kr1t
cncl = 0
editor = Manually
language = en
WARNING Be sure to choose different names for your various variables across wizard screens, because all the key/value pairs are stored in a single hash (at least in this example) and a second widget of the same name on another screen will overwrite the value of the first.
And this gets me to right where I wanted to be: a multiscreen configuration wizard that's capable of being called as a script by the Mac OS X Installer.
But if you try this yourself, you'll notice something slightly irritating: for each screen, the Pashua app is brought to life and killed off, over and over. While not quite as staccato as the grotty way I started out with and far more feature-filled and flexible, it still feels a mite bit rough around the edges. And what if the user wanted to rerun it? She'd have to visit the command line and invoke it from there—still a no-no.
Wouldn't it be nice to make a self-contained, double-clickable application of this, turning your simple Unix script into a GUI wizard that's virtually indistinguishable (except for the more limited form-driven-only functionality) from a first-class application?
The Pashua distribution includes just such a thing in the form of Doubleclickable Example (Pashua/Examples/Doubleclickable Example.app).
Option-drag a copy of Doubleclickable Example somewhere for editing (leaving the original as it is, in case you need it again as a starting point); my copy is Documents/Code/Pashua/Doubleclickable Example.
Control- or right-click the application and select Show Package Contents from the context menu, as shown in Figure 2-27. Browse through the app until you get to Doubleclickable Example/Contents/MacOS. Notice that both the Pashua app and Pashua.pm Perl module are baked right into the application's contents so that they're readily available to the core script. The script itself has the same name as the outer application, Doubleclickable Example.

Figure 2-27. Show Package Contents
Otherwise, open the script in your favorite plain text editor [Hack #78] and edit to your heart's content.
Here's what is essentially the same code as appeared in the preceding "Wizard Example" section. I've added in a few more screens and put in some basic logic for which screens to show, given the input provided on the previous screen and so forth:
#!/usr/bin/perl -w
# Doubleclickable Example
# A double-clickable Pashua Wizard example
BEGIN {
use File::Basename;
unshift @INC, dirname($0);
}
use strict;
use Pashua;
#!/usr/bin/perl -w
use strict;
use Pashua;
my $general = <<GENERAL;
windowtitle=Blosxom Configuration Wizard: General Settings
txt_type=text
txt_text=Welcome to Blosxom.[return][return]You're mere
moments away from your very own blog.[return][return]This
wizard will take you through some last minute configuration
settings. If you're in need of some details, take a gander
at the gory details on the Blosxom Configuration page at
http://www.blosxom.com/documentation/users/configure/.
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
description_type=textfield
description_label=How would you describe your blog?
description_width=400
description_default=Yet another blosxom blog.
language_type=textfield
language_label=What will be your primary written language? (e.g. en=English)
language_width=25
language_default=en
future_type=radiobutton
future_label=Should I show entries from the future (i.e. post-dated entries)?
future_option=No
future_option=Yes
future_default=No
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
GENERAL
my $staticordynamic = <<STATICORDYNAMIC;
windowtitle=Blosxom Configuration Wizard: Dynamic or Static
txt_type=text
txt_text=Blosxom runs either dynamically (on-the-fly) or
statically, rendering the main index, category indexes,
date indexes, and (optionally) individual entries as regular
files on your filesystem. Static rendering is useful for
those who either prefer to or are only able to serve up
files from their Web server. You can render your blog from
locally and then move them to a mounted WebDAV drive, .Mac
iDisk, or FTP/rsync them up to your ISP's server.[return][return]
For more details on static rendering, visit the Blosxom Static
page at http://www.blosxom.com/documentation/users/configure/static.html.
staticdynamic_type=radiobutton
staticdynamic_label=Would you prefer to run your blog in static or dynamic mode?
staticdynamic_option=Dynamic
staticdynamic_option=Static
staticdynamic_default=Dynamic
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
STATICORDYNAMIC
my $configurestatic = <<CONFIGURESTATIC;
windowtitle=Blosxom Configuration Wizard: Static Settings
staticdir_type=openbrowser
staticdir_label=Where would you like the static version of you blog to live?
staticdir_width=400
staticdir_default=/Library/WebServer/Documents
staticpwd_label=What would you like to use as your static rendering password?
staticpwd_type=password
staticpwd_width=200
staticpwd_default=
staticentries_type=radiobutton
staticentries_label=Would you like to statically render individual entries?
staticentries_option=No
staticentries_option=Yes
staticentries_default=No
editor_type=popup
editor_label=When would you like static rendering to run?
editor_width=200
editor_option=Manually
editor_option=Every 1/2 Hour
editor_option=Every 1 Hour
editor_option=Every 2 Hours
editor_option=Every 3 Hours
editor_option=Every 4 Hours
editor_option=Every 5 Hours
editor_option=Every 6 Hours
editor_option=Every 12 Hours
editor_option=Every 24 Hours
editor_default=Manually
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
CONFIGURESTATIC
my $shareware = <<SHAREWARE;
windowtitle=Blosxom Configuration Wizard: Shareware
txt_type=text
txt_text=Blosxom is free for the taking and sharing. That said,
it does take a considerable amount of not-so-free time and loving
care. This Blosxom Installer for Mac OS X is shareware; when you've
yourself situated and have a moment, please do pay the one-time $15
shareware fee at:[return][return]http://www.amazon.com/paypage/P13LC7VUIVY0N .
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
SHAREWARE
my $finish = <<FINISH;
windowtitle=Blosxom Configuration Wizard: Finishing Up
txt_type=text
txt_text=That's all there is to it. Click the Finish button, let the
installer finish up, and you'll be whisked away to your brand new blog.
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Finish
FINISH
my %result;
%result = Pashua::run($general);
$result{cncl} and exit;
%result = (%result, Pashua::run($staticordynamic));
$result{cncl} and exit;
$result{staticdynamic} eq 'Static' and %result =
(%result, Pashua::run($configurestatic));
$result{cncl} and exit;
%result = (%result, Pashua::run($shareware));
$result{cncl} and exit;
%result = (%result, Pashua::run($finish));
# Do something useful with the results (or just save them
# somewhere for now)
open OUT, "> /tmp/pashua.out";
print OUT " Pashua returned the following hash keys and values:\n";
while (my($k, $v) = each(%result)) {
print OUT " $k = $v\n";
}
close OUT;
There are some slight changes—aside from the whole thing running as a first-class, double-clickable application, that is—worth pointing out here.
Line 2 is a cosmetic change, replacing the Next button of the previous screens with a more appropriate Finish button.
Lines 3 through 12 call the Pashua::run() method
for each screen and store the results in a %result
hash. I have added some logic to check after each screen that the
user isn't trying to get out of the wizard by
hitting the Cancel button (Lines 5, 7, 9, and 11). If so, we
terminate the script on the spot.
Now, of course, we could simply have dropped all the screen names into a loop and iterated over them one by one, like so:
my %result;
foreach my $screen ( ($general, $staticordynamic, $configurestatic,
$shareware, $finish) ) {
%result = (%result, Pashua::run($screen));
$result{cncl} and exit;
}
Line 8 (requiring an exception) is why I chose to do things manually.
We offer the user a choice of configuring Blosxom for dynamic or
static rendering. Since only the latter requires any kind of
additional configuration, the $configurestatic
screen is called only if the user selects the Static radio button in
Line 1.
The last few lines of the script do something useful with the results—or, in this example, simply stash them somewhere for now. You can't just print the values out to the Terminal (as in the case in the Simple or Wizard examples), because this script runs as a double-clickable app and doesn't involve a visit to the Terminal at all.
Double-click the Doubleclickable Example application icon in the Finder to open your dialog window. Click the Next button to jump from screen to screen. Be sure to select Static mode on the Dynamic or Static screen; otherwise, you'll never get to see the Static Settings (as well you shouldn't) shown in Figure 2-28. At the end, click the Finish button to finish up. You can quit the app at any time by clicking the Cancel button or hitting the Esc key on your keyboard.

Figure 2-28. The Static Settings screen of a Pashua-powered, double-clickable application
Notice that the Pashua app no longer ebbs and flows between each screen, making for a nice, smooth experience for the end user.
If you're interested in what shows up in the temporary output file, open a Terminal window and type the following command:
$ less /tmp/pashua.out
Pashua returned the following hash keys and values:
description = Yet another blosxom blog.
editor = Every 1 Hour
staticpwd =
language = en
cncl = 0
title = My Blosxom
staticdir = /Library/WebServer/Documents
future = No
staticentries = No
staticdynamic = Static
default = 1
/tmp/pashua.out (END)
While the documentation says that to change the name of the app you need to change both the name of the outer application, Doubleclickable Example.app, and inner Doubleclickable Example Perl script, I found that changing the name of the outer without touching the inner worked like a charm; indeed, changing the inner script's name caused the app not to run at all.
|
Get in enough face time with your fans by means of an iSight, an oscillating fan, a little ingenuity, and a well-developed sense of play.
If you've ever actually tried to do any video-conferencing using iChat and an iSight (or equivalent camera [Hack #34] ), you've no doubt found that it works surprisingly well. Sure, there are sound hiccups and video burps, but most of these can be ameliorated. Add a tad more bandwidth (e.g., DSL instead of 56K modem dialup). Don't download large MP3 files during the call. Shutting off email stems the tide of those large attachments washing in from the office. Or simply use an actual telephone (gasp!) for audio.
But try it with a roomful of people spread unevenly around a conference table and you're sure to find yourself staring at a stray notepad, box of tissues, hopelessly out-of-date organizational chart, or the one person in the room not saying a thing or moving a muscle. Now, you'd think some kind-hearted soul would move the camera every so often, pointing it at least at another unmoving, unblinking participant or different notepad; they probably won't. You'd hope someone would be nominated to or just take charge of pointing the camera at whoever is speaking; it doesn't usually happen. Even when talking directly to the poor schlub on the far end of the call, people will actually stare at the side of the camera, as if doing so somehow provided more presence.
So, what's a telecommuter with poor iSight to do? Why, oscillate, of course.
An iSight mounted to the top of debladed oscillating fan, as shown in Figure 5-50, sweeps out up to a 180-degree field of view. While this doesn't mean you're necessarily going to be looking at the person speaking for more than a split second or so, it does provide more of a sense of actually being there—albeit in an admittedly nauseating fashion.

Figure 5-50. iSight + oscillating fan = iOscillate
Intrigued? I was too when the idea first struck, so I set about building one.
Throwing together an iOscillate of your very own is trivial, eating up a scant 15 minutes or so. It requires little in the way of parts, and no tools are necessary.
Appropriate a disused or otherwise available oscillating fan—probably not best done on the hottest day of the summer.
Strip it of its blade and metal or plastic cage. Your average Walmart unit ships with these parts preremoved for your convenience. If it's already assembled, disassembly usually entails only two or three steps and requires no tools. Unclip the cage edges to separate the front portion. Unscrew the nose (usually clockwise in the U.S.) that holds the blade in place and remove the blade. Unscrew the washer that holds the back portion of the cage in place and remove it. If possible, replace the nose so that the spinning metal shaft doesn't hurt anyone.
Attach one of the various plastic connectoids that came with your iSight or other webcam to the top of the fan. I found that the flat, sticky-based iMac mount worked nicely with my iSight.
You can even just use Scotch or duct tape if all else fails. This, however, does mean that it'll be difficult to impossible to point the camera up or down as needed to catch the faces rather than ties or toupees of the participants.
Try to keep the camera itself away from the fan, to avoid vibration and cut down on the noise of the motor (if your webcam has a built-in microphone).
Do make sure that the camera is upright. And whatever you do, don't even think of strapping it to the soon-to-be-spinning metal shaft.
Drape or stick down the webcam's USB or FireWire cable in such a way that it has more than enough play yet is well clear of the metal shaft that is used to turn the blade, the mechanics involved in oscillation of the fan head, and anything else electro-mechanical on the fan.
That's all there is to building this wondrous Rube Goldberg device (http://www.rube-goldberg.com). Let's give it a whirl, shall we?
Place this contraption on a conference table, such that it is most likely to provide a sweeping view of all participants—not to mention the occasional glimpse of that gorgeous oak tree outside the window. This generally works best with all participants arranged in an arc slightly shorter than 180 degrees, close enough together so that the camera doesn't try to focus on the wall behind when there's a wide enough gap between two people.
Hook the iOscillate up to a Mac (or PC if compatible), orienting the screen so that most of the participants can see the person on the other end of the line.
Start your engines!—or sufficiently quiet, steady, and well-geared motor. While the speed setting you choose should have no bearing on oscillation, I did find that my fan's High setting made for a smoother ride.
Fire up iChat or the equivalent and ring your remote peer.
You might suggest he pop some Dramamine or wear those oddly effective seasickness wristbands. These things do whiz along at quite a clip and the camera can sometimes get confused while trying to maintain focus.
Picking your oscillating fan is key. While any old fan will do, if you're going to go out and buy one—really you shouldn't, not unless you're hot, that is—you might see if you can find one with an adjustable oscillation speed. Also, pay attention to the vibration-to-dollar ratio of some of the cheapest models.
A friend suggested actually leaving the fan blade and cage assembly intact, so as to actually cool the participants and make for a nice, wind-blown supermodel effect. If you mount the webcam behind the cage, know that the blades will confuse the iSight's autofocus to no end. If you mount it to the top, you'll find it vibrates considerably and there's a risk of catching some part of the USB or FireWire cable in the blade.
For a decidedly manual version of this hack, try placing your webcam on a lazy Susan: that revolving tray one finds in the center of large round dining tables. You'll still have to remember to aim the camera at whoever is talking, but it then becomes a group endeavor (and makes for a smoother ride than the usual jiggly reorientation). Place the laptop that's hosting the session next to the camera so that participants can see to whom they are speaking. Put the speakerphone on the tray too for greater sound quality on the listener's end. Or, if it's a lunch meeting, use it as intended: to pass the Kung Pao and rice.
Return to the Mac DevCenter
Copyright © 2009 O'Reilly Media, Inc.