You're viewing all posts tagged with jruby

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();
    }
}

Source code and executable are on Github.

i stuck my cucumber in it

We’re building out an application to support this high profile effort that involves something like 10 different teams.  My team’s application has over a million lines of code, 8000 classes, and Needs To Work(tm).  Or heads will roll.  And mine doesn’t like to do that, because it might get all scruffed up.  So naturally we decided to get down with the BDD and use the Cucumber.  But since we’re enterprise Java developers, things get a little more complicated.  Because that’s how we roll.

We use JRuby so our Ruby code can talk to our Java code and it’s totally awesome and all that.  But when it comes to deploying, right now we have a simple process that involves a script (using a proprietary internal tool that is kind of like SCP except it leaves me wishing it actually was) that bundles up all my deliverables and sticks it on the target server.  The application servers and Java platforms and so on are already there, usually maintained by other teams.

I wanted to build the application on a build server where the features will be run whenever code changes (using hudson to trigger the scripts).  I know what versions of Java will be available on the target servers, since there are architectural standards within the enterprise that I can count on.  And, among those standards, I can count on not having any variety of Ruby anywhere unless I put it there myself.  But that complicates our process, because I really shouldn’t be adding things to the server (which my team doesn’t own or control).  And even if I did, then the server would have an additional component that existed nowhere else which would need to be maintained and loved and cared for, and I’m not at all into that sort of thing.

So I says to myself: how do I simplify this process?  And myself says back: stick your cucumber in it.

So I did.  But what does that mean, you say?  Well, besides being what the cool Java kids are calling it these days, what I mean is that I stuck Cucumber and its dependent gems inside the JRuby jar which meant that all I needed to do was bundle a single extra jar in with the application that we build.  This makes deploying the exact same version of Ruby with the exact same gems to any machine or environment really easy: you just copy one file around the network.  Even a caveman could do it.  But TCP/Smoke Signal was notoriously slow and error prone so they usually didn’t.

The process is super easy to do.  First of all, you need to download the source code.  You can get it from github (http://github.com/jruby/jruby) or just download the source jar: http://jruby.org/download

What we want to do is build the jar-complete version of the distributable, which is a completely standalone JRuby environment encapsulated as a single jar.  If you inspect the ant build script (which is like a make file in the Java world), you’ll see the target “jar-complete” invokes a command to install additional gems defined in the “complete.jar.gems” property.  This will by default be found in the “default.build.properties” file.  I just edited it directly, and added a reference to a new property “cucumber.gems” to the end:

complete.jar.gems=${rspec.gem} ${rake.gem} {$ruby.debug.gem} ${ruby.debug.base.gem} ${columnize.gem} ${cucumber.gems}

Then we need to define the cucumber gems we want.  To ensure the build is predictable, I specify each gem and dependency along with its version:

cucumber.gems=${build.lib.dir}/cucumber-0.9.2.gem ${build.lib.dir}/builder-2.1.2.gem ${build.lib.dir}/diff-lcs-1.1.2.gem ${build.lib.dir}/gherkin-2.2.9-java.gem ${build.lib.dir}/json-1.4.6-java.gem ${build.lib.dir}/term-ansicolor-1.0.5.gem

Now you need to get the gem files and put them in the jruby_src/build_lib folder.  When you run the build, it will download the actual gem sources.  I just copied the gem files from an existing JRuby installation’s gem cache (jruby/lib/ruby/gems/1.8/cache), but you can do it any way you want.

Now run “ant dist-jar-complete” and you’ll be presented with a lovely, customized version of jruby-complete-1.x.y.jar in your jruby_src/dist folder ready for the cukes or whatever else you want.

You could then invoke it by calling:

java -jar jruby-complete-1.5.3.jar -S cucumber features