MacRuby with Objective-C associated objects
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <MacRuby/MacRuby.h>
@interface NSObject (MRAssociatedObjects)
- (id)associatedObjectForSymbol:(NSString *)rubySymbolName;
- (void)setAssociatedObject:(id)value forSymbol:(NSString *)rubySymbolName;
@end
@implementation NSObject (MRAssociatedObjects)
static VALUE
symbolNameToSymbol(id rubySymbolName)
{
  VALUE sym;
  ID id_to_s = rb_intern("to_s");
  if (SYMBOL_P(rubySymbolName)) {
    sym = (VALUE)rubySymbolName;
  } else if ([rubySymbolName isKindOfClass:[NSString class]]) {
    sym = rb_name2sym([rubySymbolName UTF8String]);
  } else {
      if (rb_respond_to((VALUE)rubySymbolName, id_to_s)) {
	  VALUE str = rb_funcall((VALUE)rubySymbolName, id_to_s, 0, Qnil);
	  sym = rb_name2sym(RSTRING_PTR(str));
      }
      else {
	  [NSException raise:@"ArgumentError"
                format:@"Expected a Symbol or NSString, but got `%@'", rubySymbolName];
      }
  }
  return sym;
}
- (id)associatedObjectForSymbol:(id)rubySymbolName
{
  return objc_getAssociatedObject(self, (void *)symbolNameToSymbol(rubySymbolName));
}
- (void)setAssociatedObject:(id)value forSymbol:(id)rubySymbolName
{
  objc_setAssociatedObject(self, (void *)symbolNameToSymbol(rubySymbolName), value, OBJC_ASSOCIATION_RETAIN);
}
@end
// Ruby expects this
void
Init_associated_objects() {}
@interface TestAssociatedObject : NSObject
@end
@implementation TestAssociatedObject
- (void)computeAnswerInObjC
{
  if ([[self associatedObjectForSymbol:@"question"] isEqual:@"the answer to everything"]) {
    [self setAssociatedObject:[NSNumber numberWithInt:42] forSymbol:@"computed_answer"];
  }
}
@end
% clang -fobjc-gc -framework Foundation -framework MacRuby -bundle -o associated_objects.bundle associated_objects.m
% macruby test.rb                                                                                                   
42
/Users/eloy/tmp/associated-objects/test.rb:6:in `[]=:': ArgumentError: Expected a Symbol or NSString, but got `42' (RuntimeError)
	from /Users/eloy/tmp/associated-objects/test.rb:13:in `<main>'require "associated_objects"
class NSObject
  alias_method :[], :associatedObjectForSymbol
  def []=(symbol, value)
    setAssociatedObject(value, forSymbol:symbol)
  end
end
o = TestAssociatedObject.new
o[:question] = "the answer to everything"
o.computeAnswerInObjC
p o[:computed_answer] # => 42
# anything other than a string/symbol makes it go boom
o["string is ok too"] = "yup"
o[42] = "ohnoes" # => test.rb:6:in `[]=:': ArgumentError: Expected a Symbol or NSString, but got `42' (RuntimeError)
p o[42]