Archive for December, 2009

“Ruby Rocks” ported to MacRuby

MacRuby logoWhen I first started playing around with writing Cocoa apps using Ruby, I was using RubyCocoa. I did the Ruby Rocks tutorial and was pretty impressed by how easy it was to write graphics apps using Ruby and Cocoa. I quickly discovered MacRuby and decided to use it going forward. The first thing I did was to port my finished Ruby Rocks to MacRuby, so I’m posting in as a MacRuby sample.

Possibly at some point I will write up the conversion to MacRuby as a tutorial. Anywhere, here it is. You can see that I kept playing for a while after finishing the tutorial…


A Dirt-Simple Hierarchical, Configurable Logger for Ruby

log4r is currently broken in MacRuby, and I was desperate for a log4j-ish logger in the MacRuby app I’m working on, so I put together this very basic hierarchical logger based on the standard ruby logger. I’m posting it in case anybody else has the same problem with MacRuby, or wants something that is a little simpler than log4j to configure.

require 'logger'
require 'forwardable'

class LogGroup < Logger
  extend Forwardable

  def_delegators :@child_loggers, :[]=, :has_key?

  attr_accessor :child_loggers

  def initialize(logdev, shift_age = 0, shift_size = 1048576)
    @child_loggers = {}

  def self.init_with_config(config)
    logdev = config['device'] || STDOUT
    shift_age = config['shift_age'] || 0
    shift_size = config['shift_size'] || 1048576
    root =, shift_age, shift_size).with_name('root')

    config['loggers'].each do |name, level|
      name_parts = name.split('.')
      log_group = root
      name_parts.each do |name_part|
        unless log_group.has_key?(name_part)
          log_group[name_part] =, shift_age, shift_size).with_name("#{log_group.progname}.#{name_part}")
        log_group = log_group[name_part]
      log_group.level =  Logger::Severity.const_get(level.upcase.to_sym)

  def with_name(name)
    @progname = name

  def inspect
    "#{@progname}(#{@level}) -- #{@child_loggers.inspect}"

  def [](dotpath)
    logger = self
    name_parts = dotpath.split('.')
    while (name = name_parts.shift) && logger.has_key?(name)
      logger = logger.child_loggers[name]

To use it, create a config file like so:

device: logfile
  cocoa.document:           debug
  cocoa.codeview:           warn
  cocoa.diagramview:        debug

And use it like this:

require "yaml"
require "log_group"
log_cfg = YAML.load_file("log_config.yaml")

$log = LogGroup.init_with_config(log_cfg)

$log['cocoa'].debug("this is a debug message from cocoa")
$log['cocoa.codeview'].debug("this is a debug message from cocoa.codeview")
$log['cocoa.diagramview'].debug("this is a debug message from cocoa.diagramview")

This will output to logfile:

  # Logfile created on 2009-12-19 02:42:05 -0800 by logger.rb/20321
  D, [2009-12-19T02:42:05.549777 #49807] DEBUG -- root.cocoa: this is a debug message from cocoa
  D, [2009-12-19T02:42:05.586840 #49807] DEBUG -- root.cocoa.diagramview: this is a debug message from cocoa.diagramview
Return top

Web Application Developer

LastObelus hacks on web applications for enterprise. Usually in Rails or Merb these days, but he has worked WebObjects, J2EE, and various PHP frameworks.