4. Appendix: Going Further

There are many useful things that can be done with Cocoa classes inside Ruby scripts. Here’s another simple example. I recently wanted to size some Photo Booth pictures down by 50%. With Ruby and RubyCocoa it was easy to get Cocoa to do the work for me.

Resize Images with Cocoa [ruby]
require 'osx/cocoa'

# magic to prevent a Cocoa warning when this is run from the command line
# is this a "bug" (Apple's problem) or "abuse of the framework" (mine)?
OSX::NSApplication.sharedApplication

# use Ruby's open classes to add some new features to NSImage
class OSX::NSImage
  def writeJPEG(filename)
    bits = OSX::NSBitmapImageRep.alloc.initWithData(self.TIFFRepresentation)
    data = bits.representationUsingType_properties(OSX::NSJPEGFileType, nil)
    data.writeToFile_atomically(filename, false)
  end

  def copyAndScale(factor)
    newWidth, newHeight = size.width*factor, size.height*factor
    newImage = OSX::NSImage.alloc.initWithSize [newWidth, newHeight]
    newImage.lockFocus
    drawInRect_fromRect_operation_fraction(
      [0, 0, newWidth, newHeight],
      [0, 0, size.width, size.height],
      OSX::NSCompositeSourceOver,
      1.0)
    newImage.unlockFocus
    newImage
  end
end

def resizeImage(sourceFileName, scaledFileName)
  sourceImage = OSX::NSImage.alloc.initWithContentsOfFile(sourceFileName)
  scaledImage = sourceImage.copyAndScale 0.5
  scaledImage.writeJPEG scaledFileName
end

resizeImage("image.jpg", "half-sized.jpg")
If you try this script, take out the line calling OSX::NSApplication.sharedApplication and look at the results. Everything seems to run fine, but Cocoa prints two cryptic warning messages:
% ruby resize.rb
2006-06-29 08:06:40.372 ruby[536] _NXCreateWindow: error setting window property (1002)
2006-06-29 08:06:40.390 ruby[536] _NXTermWindow: error releasing window (1002)
 

I found a discussion of the problem on codecomments.com. Apparently, the AppKit code is a bit tangled, and it is necessary to have access to the window server to use NSImage. The needed initialization occurs when we call sharedApplication. Obvious? The discussion also contained an interesting comment by a Cocoa programmer: “Learning to program sometimes resembles blindly groping in dark.” To me, that’s a big disadvantage of working with a closed-source framework like Cocoa. Using Ruby to write quick scripts and explore interactively helps a lot, but doesn’t eliminate the problem.

Did you find an error? Is something missing? Post your comment or suggestion below!

Comments (1) post
  1. Chris L Wed Mar 07 15:38:53 +0000 2007

    Thanks for the tip. I had the same warnings when using App Kit from a non-GUI app written in C. The following function call probably does the same trick but is a bit more obvious what is happening. (And it is documented by Apple) NSApplicationLoad();