How do I properly spawn an external process on OS X from Ruby?

I've been struggling with this for quite a long time now (weeks?) and
figure it's time to get some advice from folks that know what they're
doing.

Essentially, I am trying to launch an OSX GUI application (The Adobe
Flash Player) from a ruby process, send this new process a file target
to open, and keep a handle to this process so that I can find out when
it exits, or even force it to exit in some circumstances.

I have had no trouble at all performing this task in the Windows XP
Shell and Cygwin, but OS X is giving me much heartache.

The Flash Player does not read or write to standard in, standard out
or standard error, so I'm not sure if popen is really the way to go or
not.

If you're going to try this at home, you'll need three things:
1) A SWF File
2) The desktop debug Flash Player for OS X
3) The attached ruby script

Unfortunately, I'm not allowed to distribute the Flash Player, so
you'll have to grab it from here:
http://tinyurl.com/yuly87
or
http://download.macromedia.com/pub/flashplayer/updaters/9/sa_flashplayer_9_all_debug_ub.dmg

You can download the ruby script and a swf file from here:
http://www.asserttrue.com/files/Published.zip
(I have also included the latest script below)

The behavior that I'm getting is as follows:

a) If I use the recommended OS X 'open -a' command against the "Flash
Player.app" folder/executable, the Flash Player opens, takes focus and
displays the SWF file perfectly, but I lose all references to the
spawned process in ruby. So I can't tell when it closes or force it to
close when I want it to.

b) If I use sh, exec, system or popen, I cannot target the .app folder
but instead must point at "Flash Player.app/Content/MacOS/standalone".
This approach will launch the Flash Player and keep a handle to it in
ruby so that the processes are perfectly tied, but I can't seem to
send in the target to the SWF file.

I'm pretty sure there is a simple fix to this problem, but I can't
seem to find it anywhere. Any help would be greatly appreciated.

Thanks,

Luke Bayes
lbayes [at] p a t t e r n p a r k [dot] com
www.asunit.org

File: flash_player.rb
----------------8<------------------

class FlashPlayer
  def initialize
    # This will launch the Player with the swf
    # but I cannot hold onto the process because
    # 'open' seems to fork and I'm not sure how
    # to grab the forked process id...
    execute_with_open

    # This will launch the player and hold onto
    # the process, but I can't get it to accept
    # the requisite swf file
    #execute_without_open
  end

  def execute_with_open
    player = "Flash\ Player.app"
    swf = "Published.swf"

    IO.popen("open -a '#{player}' #{swf}") do |p|
      puts 'Player opened'
    end
    puts 'Player thread returned'
  end

  def execute_without_open
    player = "Flash\ Player.app/Contents/MacOS/standalone"
    swf = "Published.swf"

    t = Thread.new {
      IO.popen("'#{player}' #{swf}") do |p|
        puts 'Player opened - but not focused?'
      end
    }
    while(t.alive?)
      sleep(0.3)
      puts 'Player still open'
    end
    puts 'Player closed'
  end
end

FlashPlayer.new

---------------->8------------------

It sounds like the 'applescript' gem might be what you are looking for.
Here is an intro:

and here is the rubyforge page:
http://rubyforge.org/projects/rb-appscript/

There is also Ruby OSA:
http://rubyosa.rubyforge.org/

Gary Wright

···

On Mar 22, 2007, at 5:45 PM, Luke wrote:

I've been struggling with this for quite a long time now (weeks?) and
figure it's time to get some advice from folks that know what they're
doing.

Essentially, I am trying to launch an OSX GUI application (The Adobe
Flash Player) from a ruby process, send this new process a file target
to open, and keep a handle to this process so that I can find out when
it exits, or even force it to exit in some circumstances.

Here are some tips on OS X.
in Apple script it is very English like, "Tell application appname, ..."
also there is a command line tool in OS X called (of all things)
open
open takes the application name or file name as required argument.
If you only pass it the file name, it will open the default app for the file type.
You can tell it to open a file with a particular app too.
Too bad this puppy isn't on Linux.
Syntax is very easy too.
check out the man page on open.

···

On Mar 23, 2007, at 7:43 AM, Gary Wright wrote:

On Mar 22, 2007, at 5:45 PM, Luke wrote:

I've been struggling with this for quite a long time now (weeks?) and
figure it's time to get some advice from folks that know what they're
doing.

Essentially, I am trying to launch an OSX GUI application (The Adobe
Flash Player) from a ruby process, send this new process a file target
to open, and keep a handle to this process so that I can find out when
it exits, or even force it to exit in some circumstances.

It sounds like the 'applescript' gem might be what you are looking for.
Here is an intro:
O'Reilly Media - Technology and Business Training

and here is the rubyforge page:
http://rubyforge.org/projects/rb-appscript/

There is also Ruby OSA:
http://rubyosa.rubyforge.org/

Gary Wright

Hey John,

Thanks for the response. I'm using open in the example that I
provided, but open actually spawns a new process outside of Ruby and I
can't figure out how to tell when the launched application is closed.
Basically, the IO.popen("open -a #{some_app}") call gives me a handle
to the "open" process which is disconnected from the process that I'm
actually interested in.

It seems to do the same thing as:

sh ./some_app &

Is there some way to tell "open" that I want the process initiated and
"blocked"?

Gary,

Thanks for the heads up on the AppleScript gem, I checked that out
also and had some problems because I'm trying to deploy a cross-
platform gem and creating a dependency on those tools looked like they
may cause problems for win users. Gem auto-unpacked the exts when it
was resolving dependencies.

The closest thing I've come up with so far is initiating the process
with "open" and then using "ps -a" and regex to poll for the status of
the launched process. This is definitely pretty far from ideal, so I
would be surprised if that's what it's going to take.

Any ideas?

Thanks,

Luke Bayes
www.asunit.org

···

On Mar 22, 7:38 pm, John Joyce <dangerwillrobinsondan...@gmail.com> wrote:
also there is a command line tool in OS X called (of all things) open

You can do something like...
Fork.
In the child, detach and exec (some people call for a double-fork).
In the server, store the PID of the child to a file (if needed).
To find out of the process is alive, send signal 0 to the saved ID.
[There isn't really a signal 0 but trying to send on will let you know
if a process with that ID is alive.]

This works fine on systems that support `fork' and signals nicely so,
not windows.

You might be able to glean more interesting tidbits from Proc::Daemon
on CPAN (yes, it's Perl).
There are likely similar libraries for Ruby but I haven't looked (try
RAA/rubyforge as usual...)

Paul

Thanks for the heads up on the AppleScript gem, I checked that out
also and had some problems because I'm trying to deploy a cross-
platform gem and creating a dependency on those tools looked like they
may cause problems for win users. Gem auto-unpacked the exts when it
was resolving dependencies.

OS X's GUI and CLI environments have distinctly different ways of
doing business and don't integrate too well out of the box, so
conventional Unixisms like fork, argv and kill tend not to go down so
well with GUI apps (as you've noticed). The standard API for launching
apps and opening documents is LaunchServices and IPC is normally done
via Apple events, but if Mac-specific C extensions are out then I
think you're about down to kludging it.

The closest thing I've come up with so far is initiating the process
with "open" and then using "ps -a" and regex to poll for the status of
the launched process. This is definitely pretty far from ideal, so I
would be surprised if that's what it's going to take.

See also 'osascript', which can be used to execute AppleScripts on the
fly.

One other thing to remember: as a rule, OS X shouldn't have more than
one instance of a given application running at a time (it's
technically possible, but is largely frowned upon). Aside from being
another reason not to mess about with fork; this also means it's
normally pretty safe to identify apps by bundle id/bundle path (or
even file name), so obtaining and working with a process id generally
isn't essential.

HTH

has

···

On 23 Mar, 04:24, "Luke" <lba...@gmail.com> wrote:
--
http://appscript.sourceforge.net
http://rb-appscript.rubyforge.org
http://appscript.sourceforge.net/objc-appscript.html

It might be worth your time to check out Automator actions or AppleScript more before creating daemons.
If you use the AppleScript method there is definitely a way to send a file to an app and then take the result and send it to a Ruby file. You don't necessarily have to leave the Ruby process running, but you can. Depends what you want to do.
The trouble might be, OS X applications must have AppleScript enabled in them. Not everything in the app will be available to you, but passing a file should be painless and easy. Depends on the app though.