Getting Started with MacRuby

posted by crafterm, 01 September 2009

For the those dual personality developers like myself who love Ruby, Rails and other awesome Ruby tools, but also get a big kick out of Mac and iPhone development, MacRuby is a really exciting project worth taking a look at. Originally a port of Ruby 1.9 to the Cocoa/Foundation frameworks under Mac OSX, MacRuby is now a fully fledged Ruby environment, with an LLVM based interpreter and DSL driven UI toolkit.

MacRuby is still under active development, and isn’t yet finished, but it’s Ruby and standard library compatibility is already quite impressive.

From the perspective of Ruby development, MacRuby can be used to build and run your Ruby and when supported Rails applications. From the perspective of a Mac applications developer, MacRuby can be used to build fully-fledged OSX desktop applications that interface with all the usual Mac development frameworks such as Core Data, Core Image and Cocoa, etc. User interfaces can be designed using Interface Builder, or using HotCocoa, a Ruby based DSL for describing Cocoa based UI’s.

Technically, MacRuby is implemented using the Objective-C common runtime and garbage collector, and the Core Foundation framework. This means that under MacRuby, Ruby objects are NSObjects (eg. a Ruby String is a NSString, likewise Ruby Hashes are NSDictionary’s, without any bindings or conversion required), Ruby classes are Objective-C classes, fully interoperable and interchangeable with each other, and instead of using the Ruby 1.9 garbage collector, the Objective-C 2.0 garbage collector is in full effect.

Most recently with MacRuby 0.5, the current development version of MacRuby, the YARV based interpreter has been removed and a new LLVM code generating interpreter has been implemented for performance and further optimisations down the track.

In this particular post I’ll work through the installation of MacRuby on your system, several follow up posts will discuss developing applications using MacRuby for Ruby and OSX desktop developers.

Installing

To get MacRuby running on your machine you currently need to build and install it from source. The build process is straightforward, but does take a bit of time. I’ll also assume you have the latest version of XCode installed. Here’s what you need to do.

Building LLVM

LLVM is required to build the latest MacRuby source, in particular a specific revision of LLVM for compatibility reasons, so even if you have it installed already as part of Snow Leopard or Ports, you might need to build it again. The particular revision required is 72741.

LLVM is hosted at http://www.llvm.org in Subversion, however personally I found it much easier to check out using a git mirror of the repository rather than attempt it from SVN (which for me took well over an hour just for the checkout that failed before completion resulting in a hosed source tree).

To checkout LLVM using git, and create a branch you can build, based off the right SVN revision number, perform the following commands:

$> git clone git://repo.or.cz/llvm.git
$> cd llvm
$> git checkout -b macruby-reliable ebe2d0079b086caa4d68ea9b63397751e4df6564
$> ./configure
$> UNIVERSAL=1 UNIVERSAL_ARCH="i386 x86_64" ENABLE_OPTIMIZED=1 make
$> sudo env UNIVERSAL=1 UNIVERSAL_ARCH="i386 x86_64" ENABLE_OPTIMIZED=1 make install

Note that ebe2d0079b086caa4d68ea9b63397751e4df6564 above corresponds to the git commit hash of svn commit 72741, you can tell this by looking at the output of git log and searching for 72741 in the git-svn-id section of the log message.

Alternatively, if you really need to check out llvm using Subversion, you can use the following command:

svn co -r 72741 https://llvm.org/svn/llvm*project/llvm/trunk llvm

Compiling and installing LLVM will take a while (45 minutes or more depending on your system), feel free to grab a cup of coffee – I had a nice Cappuccino:

2719.14 real      2429.54 user       183.13 sys

Building MacRuby

After installing LLVM you can now go ahead and build MacRuby itself. MacRuby now has an official git repository you can use to check out the source from. There is also an SVN repository however I’ll always favour git over Subversion:

$> git clone git://git.macruby.org/macruby/MacRuby.git
$> cd MacRuby
$> rake
....
/usr/bin/install -c -m 0755 libyaml.bundle /Library/Frameworks/MacRuby.framework/Versions/0.5/usr/lib/ruby/site_ruby/1.9.0/universal-darwin9.0
cd ext/fcntl
/usr/bin/make top_srcdir=../.. ruby="../../miniruby -I../.. -I../../lib" extout=../../.ext hdrdir=../../include arch_hdrdir=../../include install
/usr/bin/install -c -m 0755 fcntl.bundle /Library/Frameworks/MacRuby.framework/Versions/0.5/usr/lib/ruby/site_ruby/1.9.0/universal-darwin9.0
cd ext/zlib
/usr/bin/make top_srcdir=../.. ruby="../../miniruby -I../.. -I../../lib" extout=../../.ext hdrdir=../../include arch_hdrdir=../../include install
/usr/bin/install -c -m 0755 zlib.bundle /Library/Frameworks/MacRuby.framework/Versions/0.5/usr/lib/ruby/site_ruby/1.9.0/universal-darwin9.0
./miniruby instruby.rb --make="/usr/bin/make" --dest-dir="" --extout=".ext" --mflags="" --make-flags="" --data-mode=0644 --prog-mode=0755 --installed-list .installed.list --mantype="doc" --sym-dest-dir="/usr/local"
installing binary commands
installing command scripts
installing library scripts
installing headers
installing manpages
installing data files
installing extension objects
installing extension scripts
installing Xcode templates
installing Xcode 3.1 templates
installing samples
installing framework
installing IB support
$> sudo rake install

After installing you can test your MacRuby build by using the bundled spec suite:

$> rake spec:ci

Please note that this installs the latest version of MacRuby under development which is a moving target, however being on the bleeding edge means you can update to the latest source at any time with the latest features and fixes, and makes it much easier for contributing back to the project with patches, etc.

Later, if you'd like to update your installation, just return to the source directory, update your source from the git repository and reinstall.

$> git pull origin master
$> rake
$> sudo rake install

MacRuby Usage

Now that you have MacRuby installed, you can start using it. gems, ri, irb, etc, are all provided as part of your MacRuby installation with a mac* prefix so they don’t conflict with your existing MRI based installation (eg. macri, macirb, macgem, etc). In addition to this further interesting extensions such as XCode templates are included to get started building graphical Cocoa based apps using Interface Builder, etc.

$> macruby -v
MacRuby version 0.5 (ruby 1.9.0) [universal-darwin9.0, x86_64]

$> macirb
irb(main):001:0> puts "Hello World"
Hello World
=> nil
irb(main):002:0>

Summary

We've stepped through the installation of the latest version of MacRuby on your system, including it’s primary dependency LLVM, and described how to familiarise yourself with its environment. In future posts, I’ll write further about how to get started creating Cocoa applications using Mac OSX technologies such as XIBs, Bindings, Core Data, and Core Image with your apps, and also how to use HotCocoa, the nice and concise Ruby DSL for building OSX UI’s in Ruby.

If you can’t wait till then there’s a few further resources I'd recommend taking a look at:

  1. The MacRuby site
  2. The MacRuby Example Applications
  3. Rich Kilmer’s MacRuby and HotCocoa presentation from the Ruby on OSX conference in Amsterdam

There’s also @macruby on Twitter where regular updates are posted, an IRC channel, and two mailing lists. I also follow one of the Github mirrors to see each commit that’s made to the MacRuby source, together with all the other projects I'm actively following.

Looking forward to writing more MacRuby posts in the future, enjoy MacRuby!

RabbitMQ

posted by crafterm, 31 August 2009

Last week I was privileged to present at our local Melbourne Ruby/Rails user group with fellow CLEAR Interactive colleague Daniel Neighman. Daniel and I gave a talk about RabbitMQ, the exciting AMQP based messaging platform.

We focused on discussing how RabbitMQ and AMQP came into existence and its architecture. I also showed a few demo applications I'd prepared, one a Rails application that used RabbitMQ to resize and process images via Core Image in the background, the other, a RubyCocoa Desktop client that posted surf report measurements to a fanout exchange that drove a video news feed of surfer quotes.

The slides for the presentation are available at slideshare.

The example applications I demonstrated during the talk are available as a GitHub project as well.

Big thanks to Nick Marfleet for organising and Square Circle Triangle for hosting the night, looking forward to next month already!

Comma, CSV for all

posted by crafterm, 10 March 2009

CSV can be quite uninspiring at times, but as I'm sure many of you are all too familiar, many modern applications still require parsing and generation of CSV to interface with legacy systems and/or desktop software, notably Excel.

One of my Ruby on Rails clients required CSV data generation to support an ‘export to excel’ feature – so I embarked on a journey to look at the various CSV gems/plugins available at the time to export our data.

The result of this adventure gave birth to Comma, a small and simple (just over 60 lines implementation) gem that adds CSV generation support to arbitrary Ruby objects.

Using a declarative approach, you specify the output CSV format naming attributes, methods, associations, etc, all within a block with optional header names. Comma traverses these definitions to fetch model data, with conventions inferring headers when not specified using sensible defaults.

I had a few particular requirements while researching, which led to Comma’s development:

  1. Support pure Ruby objects

    I wanted to export arbitrary instances to CSV, not just ActiveRecord derived objects, and hence didn’t want to use a plugin specific to Rails, or one that had internal knowledge of ActiveRecord or similar models for inferring information such as associations and attributes.

  2. Flexibility

    Transparency across associations, attributes and methods – they should all be treated the same. Some of the plugins I looked at required different configuration to name methods or associations to use, as opposed to attributes. I wanted to be able to cleanly define where the data for export should come from, and have Comma transparently access to it (after all, Ruby’s #send mechanism provides the base foundations for this).

  3. Multiple CSV output formats per class

    One class we have requires several CSV output formats, one for delivery to end users, and another for escrow purposes. I wanted to be able to define multiple output formats per class, and be able to call upon them when required.

  4. Integration

    We’re using Ruby on Rails, so integration with Rails would be useful, particularly at the controller level, which should be DRY and able to ‘render :csv => @objects’.

  5. Simplicity

    CSV export shouldn’t be that hard on the plugin/gem implementer, nor the plugin/gem user – ideally I wanted to be able to define a CSV configuration (with an optional name) using a declarative syntax that names what should be exported, and have that same definition used for data access and header name generation.

An example use of Comma follows:

class Book < ActiveRecord::Base

  # ================
  # = Associations =
  # ================
  has_many   :pages
  has_one    :isbn
  belongs_to :publisher

  # ===============
  # = CSV support =
  # ===============
  comma do

    name
    description

    pages :size => 'Pages'
    publisher :name
    isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'
    blurb 'Summary'

  end

end

Annotated, the ‘Comma’ description includes:

# starts a Comma description block, generating 2 methods #to_comma and #to_comma_headers for this class.
comma do

  # name, description are attributes of Book with the header being reflected as 'Name', 'Description'
  name
  description

  # pages is an association returning an array, :size is called on the association results, with the header name specifed as 'Pages'
  pages :size => 'Pages'

  # publisher is an association returning an object, :name is called on the associated object, with the reflected header 'Name'
  publisher :name

  # isbn is an association returning an object, :number_10 and :number_13 are called on the object with the specified headers 'ISBN-10' and 'ISBN-13'
  isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'

  # blurb is an attribute of Book, with the header being specified directly as 'Summary'
  blurb 'Summary'

end

Notice above how attributes and associations are all specified and treated the same, header names are reflected from the method names using sensible conventions unless provided directly, and more complex combinations of data can be grouped together into methods if required.

Multiple descriptions can be specified with a named Comma block:

# ===============
# = CSV support =
# ===============
comma do  # implicitly named :default

  name
  description

  pages :size => 'Pages'
  publisher :name
  isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'
  blurb 'Summary'

end

comma :brief do

  name
  description
  blurb 'Summary'

end

You can specify which format you'd prefer as an optional parameter to #to_comma.

If you’re using Ruby on Rails, your controllers automatically gain Comma-fu.

class BooksController < ApplicationController

  def index
    respond_to do |format|
      format.csv { render :csv => Book.limited(50) }
    end
  end

end

Comma is licensed under the MIT License, and can be installed directly from github’s gem server.

sudo gem install crafterm-comma

Please feel free to contact me if you have any questions and/or feedback regarding Comma.