Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bundle a ruby package into an [Objective-C] cocoa application?

I'm trying to figure out how I could bundle a package, written in Ruby (Sass) into a Cocoa application (Objective-C, not Ruby Cocoa) for me to execute (via NSTask is fine, unless there is an easy way to bridge ObjC<->Cocoa that I'm not aware of).

The Sass package is something you have to install, using "gem install" or "rake install" -- doing so puts a ton of files in my ~/.gem directory. Since I want anybody who has installed my Cocoa-based application to be able to execute this tool from within my app, I don't want to have the user go through a process of installing anything additional, so I'm hoping to be able to embed everything I need in the Resources directory of my app package.

However, not being that familiar with the internals and structure of Ruby (Sorry, I'm having trouble just keeping ObjC/Cocoa in my head!), It's not clear to me just which of the 1,444 files that got installed in the ~/.gem directory (Yes, I counted) I need to embed in the application and what I might need to do to get the directory references, etc. working right.

If anybody has any experience with embedding a ruby tool into a Cocoa application, I would really appreciate your input. I wasn't expecting this to be so hard, considering that ruby is installed on Mac OS X ... but apparently this package is (typically? atypically?) more than just a single script file....

like image 231
danwood Avatar asked Jun 19 '10 05:06

danwood


2 Answers

You can install gems into a specific location:

GEM_HOME=path/to/your/project/gems gem install sass

Then, as part of your build process, copy the folder into your resources. When you want to run sass, find your gem folder. Call ruby like so:

NSString *gems_path = …;
NSTask *task = [[NSTask alloc] init];
// you may want to copy NSProcessInfo.processInfo.environment and modify it instead
task.environment = @{ @"GEM_HOME": gems_path };
task.launchPath = @"/usr/bin/ruby";
task.arguments = @[[gems_path stringByAppendingPaths:@[@"bin",@"sass"]], @"rest", @"of", @"your", @"arguments"];
// add handling for I/O
[task launch];

(Typed into github, may have silly mistakes)

Note that you may want to bundle ruby as well (perhaps macruby), to prevent compatibility issues. If not, ensure that you test all versions of OS X that you support, especially 10.9, as ruby was upgrade to 2.0.

like image 184
Jacob Lukas Avatar answered Sep 21 '22 05:09

Jacob Lukas


Major thanks to @Jacob Lukas, I found that simply setting GEM_HOME on gem install <gem> didn't correctly attach the dependencies. So, for my case--where I just need to run a script I'm generating in an Xcode plugin--I ended up with:

gem install -i ~/xCodeProjects/PluginOne/gems xcodeproj --verbose

to get the gem(s) & dependencies. Then, I used:

NSString *gems_path = [[bundle resourcePath] stringByAppendingString:@"/gems"];

NSTask *task = [[NSTask alloc] init];
task.environment = @{ @"GEM_HOME" : gems_path };
task.launchPath = @"/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/bin/ruby";
task.arguments = @[generatedRubyFilePath];

[task launch];
like image 25
Stan Avatar answered Sep 23 '22 05:09

Stan