Programming With Cocoa
by Christopher Roach
An Introduction to RubyCocoa, Part 1
This article, and the second installment that follows next week, can be considered the fourth and fifth in a series covering Ruby programming on Mac OS X. However, unlike the first three articles of this series, this tutorial can be used as a standalone piece. You only need some knowledge of the Ruby programming language with a little prior experience in Xcode to understand the content found here.
That being said, if you wish to pursue Ruby programming further, feel free to check out my first three articles in the series on GUI programming with Ruby/Tk. Also, if you don't have much experience with Ruby, my first article is a handy tutorial of some of the basics. So, with all of that out of the way, let's jump into today's text.
What is RubyCocoa?
RubyCocoa is a framework that provides a bridge between the Ruby programming language and the Cocoa framework of the Mac OS X operating system. This framework allows you to create Mac OS X native, Cocoa-based applications using Ruby. It was created by Hisakuni Fujimoto and is currently in version 0.4.0. It seems to be pretty stable for application development.
In this article I hope to demonstrate the uses of this framework through the creation of a simple GUI for the Unix
tar program. First, I'll cover the installation of the RubyCocoa framework, then some of the basics of using the framework. Finally, I'll demonstrate how to create the
tar GUI using Xcode, Ruby, and the RubyCocoa framework.
If all goes well, when you're finished with this tutorial, you'll have both a working knowledge of developing Cocoa-based applications with Ruby, and a functioning application for creating and extracting
tarred and compressed files.
Getting and Installing RubyCocoa
I wrote this article, and the program for it, on my PowerBook G4 running Mac OS X 10.3.5, and as such I have based my installation instructions on these parameters. If you are running the Jaguar version of OS X, you may want to try installing RubyCocoa using the disk image provided here. However, if you are running Panther, as I am, you may find it necessary to download and install the latest Panther version from the CVS repository. To do this, open up an instance of the Terminal application and type in the following commands, ignoring the dollar marks ($), of course.
$ cvs -d:pserver:firstname.lastname@example.org:/cvsroot/rubycocoa login $ cvs -z3 -d:pserver:email@example.com:/cvsroot/rubycocoa co \ -P -r branch-devel-panther -d rubycocoa-panther src $ cd rubycocoa-panther $ cvs update -d -P
Once you have the Panther version of the RubyCocoa framework, you can install it by typing in the following commands at the same command prompt (make sure you are still in the rubycocoa-panther directory created during the CVS session).
$ ruby install.rb config $ ruby install.rb setup $ sudo ruby install.rb install
The best way to find out if everything installed correctly is to open up Xcode and select "New Project" from the "File" menu. You should see in the "New Project" dialogue a couple of choices under "Application" with the titles "Cocoa-Ruby Application" and "Cocoa-Ruby Document-based Application." If so, you've installed everything correctly. You can cancel out of the dialogue and read on to get the basics down.
The RubyCocoa framework forms a bridge between the Cocoa classes in Objective-C and their Ruby representations. All of the classes available in the Cocoa framework can be found in the RubyCocoa module
OSX, which can be mixed-in to our classes to give them access to everything the module provides.
Methods in Objective-C have a strange syntax to those of us used to just about any language outside of Smalltalk. It uses spaces and colons to denote the different parameters that will be passed into the method at invocation. Thus, a typical Objective-C method call looks like the following code sample:
[oPanel runModalForDirectory:self file:nil types nil]
In RubyCocoa it can be rewritten using two different techniques. The first replaces all of the colons and spaces with underscores so that the same method looks like the following in Ruby:
oPanel.runModalForDirectory_file_types(self, nil, nil)
The other technique can be used to make a little more sense of method names that are extremely long. Using this technique, the method name consists of everything in the Objective-C method's name up to the first detached parameter name. The rest of the parameter names are moved into the argument list. Thus, every argument after the first is prefaced with another argument that is a Ruby symbol with the same name as the parameter it represents. It sounds confusing, I know, but the example below shows you how easy it is once you get used to the syntax.
oPanel.runModalForDirectory(self, :file, nil, :types, nil)
Another gotcha that you'll come across when using RubyCocoa is in the values returned by the Cocoa methods. Cocoa methods will return a Cocoa value and not its Ruby counterpart. Thus, when a method returns a string or an array, it is returning an
NSString or an
NSArray and not their Ruby equivalents. In order to manipulate these properly you will need to convert them into their Ruby counterparts. The
NSString classes provide the
to_s methods respectively. These methods should be called when a Ruby array or string is needed.
Predicate methods (methods that return a Boolean value) can also foul up the works a bit. When calling a predicate method found in a RubyCocoa class, the method will return a 0 or 1, which both evaluate to
true in Ruby. In order to avoid any mix-ups when calling a predicate method, you should suffix the method name with a question mark (
?). Doing this will make the method return a Ruby Boolean value and will make the method name look like the following snippet of code:
Finally, one last problem you may run into when calling RubyCocoa methods is when you come across a method with a name that conflicts with a Ruby method. When this happens, just prefix the RubyCocoa method with "
When instantiating RubyCocoa classes you will use the same methods that you do in Cocoa with Objective-C rather than the
new method supplied by Ruby classes. Thus, an instance of the
NSObject class can be created with the following code:
obj = NSObject.alloc.init
Even though you use Cocoa methods to create Cocoa objects, it is not necessary to use methods such as
retain to manage the memory allocated to each object since Ruby performs garbage collection for all of its objects.
Anything else you may need to know when writing RubyCocoa-based applications can be found on the RubyCocoa programming page.
So, now that you've got the basics of creating RubyCocoa applications out of the way, let's move on to the fun stuff. The next section will cover creating our GUI wrapper for the
tar program. So, get Xcode running (if you don't still have it running) and read on.