MacRuby Screencast, and an iPhone Studio

(Wed May 06, 2009) [/Ruby#

If you're a faithful Rubyist with a Mac, you owe it to yourself to check out MacRuby. It's an implementation of Ruby 1.9 that runs on the Objective-C 2.0 runtime (the two languages share a common ancestor in Smalltalk). That means you have the full power of Ruby and all the Objective-C and Cocoa frameworks at your fingertips. And unlike RubyCocoa, with MacRuby there's no bridge to cross. So there's a lot of potential upside when it comes to performance. And I prefer the MacRuby messaging-sending syntax over the style used in RubyCocoa.

So, in hopes of helping spread the good word, I've put together a free, 20-minute MacRuby screencast.

Now, MacRuby is still a work in progress. I've found the current version (0.4) to be quite stable for Cocoa development, though I've run into problems using RubyGems that have C extensions. Nevertheless, I continue to be impressed with MacRuby. And there are some exciting developments on the horizon, including a new virtual machine based on the LLVM compiler. Does that mean you'll be able to use Ruby to write iPhone apps some day? Only Apple knows. But given that MacRuby relies on the Objective-C garbage collector (which isn't on the phone), I'm not holding my breath. In the meantime, I think MacRuby is going to become increasingly popular, especially among Rubyists interested in building Cocoa apps.

On a related note, we've opened registration for the next iPhone Development Studio in Reston, VA on August 4-7. We're hoping that by delaying it until a while after WWDC that iPhone OS 3.0 will be released. This is a fun class, and a great way to quickly come up to speed on iPhone development. Plus you'll learn Cocoa patterns and techniques you can apply to your MacRuby application. Come for the phone, stay for the Cocoa!

Gem Survey

(Sat Feb 10, 2007) [/Ruby#

Just for fun, here are the RubyGems I currently have installed:

actionmailer (1.3.2)
actionpack (1.13.2)
actionwebservice (1.2.2)
activerecord (1.15.2)
activesupport (1.4.1)
aws-s3 (0.3.0)
builder (2.0.0)
capistrano (1.4.0)
cgi_multipart_eof_fix (1.0.0)
cheat (1.2.1)
daemons (1.0.3)
fastthread (0.4)
flexmock (0.4.5)
gem_plugin (0.2.1)
gruff (0.2.8)
hoe (1.1.7, 1.1.4)
hpricot (0.5)
mime-types (1.15)
mocha (0.4.0)
mongrel (0.3.13.2)
mongrel_cluster (0.2.0)
net-sftp (1.1.0)
net-ssh (1.0.10)
piston (1.3.0)
rails (1.2.2)
rake (0.7.1)
rb-appscript (0.3.0)
rcov (0.7.0.1)
RedCloth (3.0.4)
rmagick (1.14.1)
ruby-prof (0.4.1)
rubyforge (0.4.0, 0.3.1)
rubygems-update (0.9.2, 0.9.1)
safariwatir (0.2.2)
sources (0.0.1)
test-spec (0.3.0)
xml-simple (1.0.10)

This list may be interesting to look back on, say, 2 years from now. (Don't count on it.) Who knows which gems will stand the test of time, and if so, what versions we'll be using by then.

Which RubyGems are you sporting?

Getting Started with the Amazon S3 Library for Ruby

(Thu Feb 01, 2007) [/Ruby#

Every once in a while you run across a library that makes your day. Everything just works as you'd expect and you can focus on getting something done quickly. Today, for example, I needed to share a relatively large file with a significant number of people. Linking it up on our host just didn't make sense. Added to which, just last week Marcel Molina Jr. had given a talk at The Rails Edge about his new AWS::S3 library. Coincidence? No, opportunity!

Getting started was so doggone easy that I just had to share...

1. Sign Up

Signing up is free, then you pay as you go. It's currently $0.20 per gigabyte of data transferred and $0.15 per gigabyte stored each month.

Using your existing Amazon account, simply create an Amazon Web Services (AWS) account. Then just activate the S3 Web Service. You'll receive an email that includes a link for fetching two keys: an access key id and a secret access key. You need these to make requests against the S3 service.

2. Download the AWS::S3 Library
gem install aws-s3
3. Play Around

Before you start shuttling bits around from inside an application, I recommend playing around with the API a bit. It's fun, and made easier with an interactive shell called s3sh that comes with the AWS::S3 library. It's your irb for Amazon S3.

$ s3sh

The first thing you'll need to do is make a connection to your S3 account:

AWS::S3::Base.establish_connection!(
  :access_key_id     => 'your access key id goes here',
  :secret_access_key => 'your secret key goes here'
)

Then create a unique container, called a bucket, for your objects:

Bucket.create('intergalactic')

The bucket also serves as the global namespace for your objects. Here's where the magic happens. Add an object to your bucket:

AWS::S3::S3Object.store('flight_manual.pdf', 
                        open('/path/to/local/flight_manual.pdf'), 
                        'intergalatic',
                        :access => :public_read)

Once the object has been stored in the big hard-drive in the sky, you can access it at http://s3.amazonaws.com/intergalactic/flight_manual.pdf.

That's really all there is to it! It's a convenient service fronted by an elegant Ruby library that adds up to a whole lot of fun and productivity. The API supports a boatload more options and operations, and it's extremely well designed. Having polished off the task in a couple minutes, I found the short and sweet documentation to be just what I needed for diving deeper.

4. Integrate

For my one-off task today, using s3sh was super convenient. But at some point you'll probably want to manage buckets and objects from within a larger program, such as a Rails application. That's pretty easy given the AWS::S3 Ruby library. But just to get you started, I'll leave you with this handy s3.rb file for storing an object in one fell swoop:

#!/usr/bin/env ruby
require 'rubygems'
require 'aws/s3'

AWS::S3::Base.establish_connection!(
  :access_key_id => ENV['AMAZON_ACCESS_KEY_ID'],
  :secret_access_key => ENV['AMAZON_SECRET_ACCESS_KEY']
)

file = ARGV.first
bucket = "intergalactic"

AWS::S3::S3Object.store(File.basename(file), 
                        open(file), 
                        bucket, 
                        :access => :public_read)

puts AWS::S3::S3Object.url_for(File.basename(file), bucket)[/[^?]+/]

To store the same object we did in the previous step, you'd type:

s3.rb /path/to/local/flight_manual.pdf

When all the bits have been safely tucked away, the public URL is printed. Share your objects at will, but remember that the meter's running.

5. Enjoy

Thanks Marcel!

RubyCocoa Sneak Preview

(Wed Jan 17, 2007) [/Ruby#

It's great to see a flurry of activity around RubyCocoa: the bridge for writing Cocoa apps using Ruby. Word on the street is that it'll ship with Leopard...

RubyOSA: The End of AppleScript?

(Tue Oct 24, 2006) [/Ruby#

At RubyConf this weekend, Laurent Sansonetti from Apple gave a talk and demonstration of RubyOSA. It's a bridge that connects Ruby to the AppleEvent infrastructure. Translated, that means you can do in Ruby what before you could only do in the insufferable language called AppleScript.

RubyOSA is still under heavy development, so watch your head. The official release is rumored to be out Any Day Now (TM). Until then, here's a quick way to start playing:

1. Install libxml and its Ruby bindings:

sudo port install libxml
sudo gem install libxml-ruby

2. Checkout and build RubyOSA:

svn co svn://rubyforge.org/var/svn/rubyosa/trunk rubyosa
cd rubyosa/
ruby extconf.rb
make
ln -s src/lib/rbosa.rb rbosa.rb

3. Fire it up in irb:

irb -r osa.bundle -r rbosa.rb

4. Start controlling applications, such as iTunes:

>> require 'rbosa'
=> false
>> itunes = OSA.app_with_name('iTunes')
>> track = itunes.current_track
>> p track.name
=> "Blue Dress"
>> track.rating = 20
=> 20
>> itunes.next_track
=> nil
>> itunes.previous_track
=> nil
>> itunes.sound_volume = 50
=> 50
>> itunes.pause
=> nil
>> itunes.play
=> nil
>> itunes.current_playlist.tracks.size
=> 2110
>> itunes.public_methods(false).size
=> 68

Or iChat:

>> ichat = OSA.app('ichat')
>> ichat.status_message = "Live from RubyOSA"
=> "Live from RubyOSA"
>> windows = ichat.windows.to_a
>> windows.first.name
=> "Chat with Marcel Molina"
>> ichat.public_methods(false).size
=> 20

Taking that a step further, you can hold yourself accountable with Rake Shame. Let your friends motivate you!

In theory, you can use Ruby to control any application that has a scriptable definition. We're still poking and prodding on the boundaries. For example, OSA.app('finder') works, but it's uncharted territory. And yet the way forward looks a lot clearer than it used to.

Major props to Marcel for helping me get started. :-)

Have fun!