[MacRuby-devel] Mixing Objective-C and Ruby classes

Laurent Sansonetti lsansonetti at apple.com
Fri Apr 17 22:58:41 PDT 2009


Hi Victor,

On Apr 16, 2009, at 4:23 AM, macruby-devel at principia.info wrote:

> Hi,
>
>  I'm new both to this list and to MacRuby. Let this message serve as  
> an introduction.
>
>  I have some questions that have not seen answered either in the  
> docs or in the list archives. I have been known to miss things  
> before, so please kindly point  me in the right direction if this is  
> documented somewhere.
>
> I have seen the embedding sample but I stil can't figure out how I  
> can pull out the following:
>
> 1. Mix Obj-C and Ruby Classes: ie, from Ruby be able to extend or  
> use Obj-C classes that are lying around in my project. How do I  
> "require" them? When I try to use them, ruby doesn't seem to  
> recognize them (specifically, it recognizes the classes but not  
> their methods)

Normally MacRuby will recognize your Objective-C classes and their  
methods. If you see the classes from Ruby but not the methods it may  
be a problem in the way you use MacRuby (or a bug in MacRuby).

Also, keep in mind that calling #methods on an object in Ruby won't  
show the Objective-C selectors, you need to pass the second parameter  
as true to show them (this is to keep compatibility with Ruby).

$ cat hello.m
#import <Foundation/Foundation.h>

@interface Foo : NSObject
@end

@implementation Foo

- (void)sayHello:(id)sender
{
     NSLog(@"hey %@!", sender);
}

@end

// We declare an Init_ method so that we can load this extension from  
MacRuby
// using #require.
void Init_hello(void) {}

$ gcc hello.m -o hello.bundle -g -framework Foundation -dynamiclib - 
fobjc-gc -arch i386 -arch x86_64

$ macruby -e "require 'hello'; Foo.new.sayHello('MacRuby')"
2009-04-17 22:44:49.495 macruby[67329:10b] hey MacRuby!

> 2. The same question, but the other way around: how can I use or  
> extend a class declared in Ruby from Obj-C? The only thing that I  
> can come up with is to declare them with @class, but it didn't work  
> (or I didn't do it properly)

This is also possible, but it requires some runtime calls since  
Objective-C is more static than Ruby (the problem is that Objective-C  
declares classes at compilation time but Ruby does it at runtime).  
Here is a dirty way to do this (note that it uses a deprecated API in  
the runtime). Another cleaner way would be to subclass the Ruby class  
dynamically too, by creating the class programmatically right after  
loading the Ruby expression.

$ cat hello2.m #import <Foundation/Foundation.h>
#import <MacRuby/MacRuby.h>

@interface NSObject (FooInterface)
- (id)foo;
@end

@interface ObjCFoo : NSObject
@end

@implementation ObjCFoo

- (id)foo
{
     NSLog(@"objc_foo");
     [super foo];
}

@end

int main(void)
{
     [[MacRuby sharedRuntime] evaluateString:@"class Foo; def foo;  
p :ruby_foo; end; end"];
     Class k = NSClassFromString(@"ObjCFoo");
     class_setSuperclass(k, NSClassFromString(@"Foo"));

     ObjCFoo *o = [[ObjCFoo alloc] init];
     [o foo];

     return 0;
}

$ gcc hello2.m -o hello2 -framework Foundation -framework MacRuby - 
fobjc-gc -arch i386 -arch x86_64
hello2.m: In function ‘main’:
hello2.m:25: warning: ‘class_setSuperclass’ is deprecated (declared  
at /usr/include/objc/runtime.h:126)
hello2.m:25: warning: ‘class_setSuperclass’ is unavailable (declared  
at /usr/include/objc/runtime.h:126)
hello2.m: In function ‘main’:
hello2.m:25: warning: ‘class_setSuperclass’ is deprecated (declared  
at /usr/include/objc/runtime.h:126)

$ ./hello22009-04-17 22:53:55.596 hello2[67436:10b] objc_foo
:ruby_foo

> 3. If I were to call arbitrary scripts as in the embedding sample,  
> how can I pre-initialize ruby objects so that they are available in  
> the environment? I.e, if I allow the user to extend my app by  
> running their ruby code, how can I make my objects available to him?  
> And also, how could I prevent certain objects from being available  
> to him, so that he doesn't break anything?

Once you link against MacRuby and call the -[sharedRuntime] method,  
all Ruby classes should be available in Objective-C. Ruby objects  
created from Objective-C must be referenced by Objective-C otherwise  
the garbage collector will free them. It is up to the developer to  
manage the Ruby objects created from Objective-C.

Vice-versa, Objective-C classes & objects are available in Ruby. There  
is no way to hide classes/objects, everything is dynamic.

HTH,
Laurent


More information about the MacRuby-devel mailing list