using jruby with aptana
I enjoy programming in the Ruby language, and I recently started to make the migration to JRuby. However, I am rather fond of using an IDE for programming. I know it’s not trendy; some programmers wear their use of vi as a badge of honor. I consider it to be archaic. It’s like being proud you grunt and swing a bone around. What separates man from apes is our use of tools, and IDEs are very useful tools. (Of course, there is a danger with IDEs in that the programmer won’t learn what is going on behind the scenes: while I agree that every programmer should know HOW to run, maintain, debug, and execute their programs without an IDE, or from within vi and the command line, I strongly disagree that we should always want to.)
I am also an unabashed fan of Windows. Yes, it’s not trendy, but I like the customizability of it. And, for the record, I haven’t had any viruses or trojans and I haven’t seen a blue screen in years.
On Windows, the best Ruby IDE is Aptana Studio. I’ve enjoyed it for quite some time now, but recently I decided to make a more permanent switch to using JRuby. Unfortunately, Aptana doesn’t play well with JRuby. One problem is that it seems to have the Ruby interpreter hardcoded as “ruby.exe”. Not just “ruby” (I initially tried making a ruby.bat batch file that would redirect calls to my actual jruby installation), but “ruby.exe”.
So, for a workaround, I created an .exe that spawns jruby.exe (or any other executable) and passes all the same command line arguments on. So what I do is rename my process launcher as “ruby.exe”, stick that in the PATH so Aptana finds the executable it expects to find, configure my launcher .exe to run jruby.exe, and then spawn the child process. Aptana is none the wiser and everything works beautifully. Problem solved.
The code is quite simple. We simply figure out what directory we are in (so we know where to look for the config file), read it (to figure out what process to launch), fix up the command line arguments (by convention, the first argument should point to the process that is being started, and we don’t want our child process to get a path to our launcher), and then use _spawnv to launch it. We do have to take care to quote any arguments with spaces in them as well.
int main( int argc, char *argv[] ) {
// figure out what directory this exe is in
char path[MAX_PATH];
string filename(path, GetModuleFileName(NULL, path, MAX_PATH));
filename = filename.substr(0, filename.find_last_of('\\')+1);
filename.append("\\predirect.config");
// open the config file
ifstream configfile(filename.c_str());
if (configfile.is_open()) {
// read path to jproc from config file
string proc;
getline(configfile, proc);
configfile.close();
// call our real proc .exe (or whatever else we
const char* new_argv[argc+1];
for (int i = 0; i < argc; i++) {
if (i == 0)
new_argv[0] = quote(proc.c_str());
else
new_argv[i] = quote(argv[i]);
}
new_argv[argc] = NULL;
return _spawnv(_P_WAIT, proc.c_str(), new_argv);
} else {
cout << "could not find " << filename.c_str();
}
}