The JRuby Tutorial #1.5: Using JIRB to check Java behaviour


I was planning on this issue to be on Camping, but I realized that it would be good to show how to use IRB with JRuby to make life as a Java developer easier. Since Java is a compiled language, it’s exceedingly hard to just test things out. My hard drive is completely littered with 5-line programs testing one aspect of the Java libraries or whatnot. If I could have an easy way to interface with the libraries without having to go through a compile cycle, this would ease my life very much. And actually, the last months I have been using JIRB in this way. Of course there is BeanShell and Groovy, but I feel they get in the way. And I’m very fond of IRB, of course.

IRB stands for Interactive Ruby, and JIRB is the name of the program for starting IRB with JRuby. Starting it should be as easy as:

%JRUBY_HOME%\bin\jirb

and after two or three seconds you will get the Ruby prompt:

irb(main):001:0>

At this point, the first thing to do is to require the java libraries, so we can have something to work with:

irb(main):001:0> require ‘java’
=> true

I would first like to show how I would go about finding the behaviour of the caret in a Java regexp. (I know I could find it in the API, but this is just to show how you could do it.)

First of all, I will include the relevant class and see what I can do:

irb(main):002:0> include_class ‘java.util.regex.Pattern’
=> [“java.util.regex.Pattern”]
irb(main):003:0> Pattern.new
NameError: wrong # of arguments for constructor
from D:\Project\jruby-trunk\src\builtin\javasupport.rb:231:in `initialize’
from (irb):1:in `new’
from (irb):1:in `binding’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:150:in `eval_input’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:70:in `signal_status’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:189:in `eval_input’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:70:in `each_top_level_statement’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:190:in `loop’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:190:in `catch’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:190:in `eval_input’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:70:in `start’
from D:\project\jruby-trunk\bin\jirb:13:in `catch’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:71:in `start’
from D:\project\jruby-trunk\bin\jirb:13
irb(main):004:0> Pattern.methods
=> [“matches”, “quote”, “compile”, “setup_constants”, “setup”, “singleton_class”, “setup_attributes”, “new_instance_for”, “[]”, “const_missing” …]
irb(main):005:0> Pattern.methods.grep /compile/
=> [“compile”]

Ok, first I include the class, then I try it out. Whoopsie, there was no empty constructor for Pattern. I’m don’t really remember what methods are in there, though, so I check out which methods Pattern have. There is a good many of them (and I haven’t written them all out either), so I try again by grepping for a method name I believe is in there. Once I’ve found the method I’m looking for, I call it with the pattern I want to compile, and inspect the result in various ways. I vaguely remember a matcher method, and it seems to work as I remember:

irb(main):008:0> pt = Pattern.compile(“^foo”)
=> ^foo
irb(main):009:0> pt.inspect
=> “^foo”
irb(main):010:0> pt.java_class
=> java.util.regex.Pattern
irb(main):011:0> pt.class
=> Pattern
irb(main):012:0> m = pt.matcher(“foo”)
=> java.util.regex.Matcher[pattern=^foo region=0,3 lastmatch=]

Ok, now I have something to work with. Good, and the standard output from JRuby gives me some insight into the internals of the matcher too; interesting stuff. There is a find method, and a matches method on the Matcher:

irb(main):013:0> m.find
=> true
irb(main):014:0> m.find
=> false
irb(main):015:0> m.matches
=> true
irb(main):016:0> m.search
NoMethodError: undefined method `search’ for java.util.regex.Matcher[pattern=^foo region=0,3 lastmatch=foo]:#
from (irb):1:in `method_missing’
from (irb):1:in `binding’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:150:in `eval_input’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:70:in `signal_status’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:189:in `eval_input’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:70:in `each_top_level_statement’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:190:in `loop’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:190:in `catch’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:190:in `eval_input’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:70:in `start’
from D:\project\jruby-trunk\bin\jirb:13:in `catch’
from D:/project/jruby-trunk/lib/ruby/1.8/irb.rb:71:in `start’
from D:\project\jruby-trunk\bin\jirb:13

I also belived there was a search method, and also a way to get back to the initial state of the Matcher. Hmm. Time to check out the methods of this object too:

irb(main):017:0> m.methods
=> [“hashCode”, “start”, “matches”, “useAnchoringBounds”, “region”, “reset”…] irb(main):019:0> m.reset
=> java.util.regex.Matcher[pattern=^foo region=0,3 lastmatch=]
irb(main):020:0> m.find
=> true
irb(main):021:0> m.group 0
=> “foo”
irb(main):022:0>

I found the method I was looking for; reset, calls it and tries find again, and then gets the group with index 0, which is the complete match.

Some Swinging
JIRB works great for trying out Swing interactively. I’m just going to show a tidbit of this, and since this is one of the more common examples of JRuby, you will be able to find many different demonstrations of this on the net.

We first require java-support, and including the required classes for this example:

irb(main):001:0> require ‘java’
=> true
irb(main):002:0> include_class([‘javax.swing.JFrame’,’javax.swing.JLabel’,’javax.swing.JButton’,’javax.swing.JPanel’])
=> [“javax.swing.JFrame”, “javax.swing.JLabel”, “javax.swing.JButton”, “javax.swing.JPanel”]

Then we create a frame and some panels:

irb(main):003:0> frame = JFrame.new(“JRuby panel”)
=> javax.swing.JFrame[frame0,0,0,0×0,invalid,hidden,layout=java.awt.BorderLayout,title=JRuby panel,resizable,normal,defaultCloseOperation=HI
DE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0×0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,fl
ags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
irb(main):004:0> basePanel = JPanel.new
=> javax.swing.JPanel[,0,0,0×0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,pr
eferredSize=]
irb(main):005:0> labelPanel = JPanel.new
=> javax.swing.JPanel[,0,0,0×0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,pr
eferredSize=]
irb(main):006:0> buttonPanel = JPanel.new
=> javax.swing.JPanel[,0,0,0×0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,pr
eferredSize=]

After this, we add some labels and some buttons to the panes, and then add the panes to the base pane, and the base pane to the frame.

irb(main):007:0> labelPanel.add JLabel.new(“Isn’t JRuby cool?”)
=> javax.swing.JLabel[,0,0,0×0,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=8388608,maximumSize=,minimumSize=,preferredSize=,defaultI
con=,disabledIcon=,horizontalAlignment=LEADING,horizontalTextPosition=TRAILING,iconTextGap=4,labelFor=,text=Isn’t JRuby cool?,verticalAlignm
ent=CENTER,verticalTextPosition=CENTER]
irb(main):008:0> labelPanel.add JLabel.new(“JIrb is very useful”)
=> javax.swing.JLabel[,0,0,0×0,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=8388608,maximumSize=,minimumSize=,preferredSize=,defaultI
con=,disabledIcon=,horizontalAlignment=LEADING,horizontalTextPosition=TRAILING,iconTextGap=4,labelFor=,text=JIrb is very useful,verticalAlig
nment=CENTER,verticalTextPosition=CENTER]
irb(main):009:0> buttonPanel.add JButton.new(“Pushbutton without action”)
=> javax.swing.JButton[,0,0,0×0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@1354
355,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIReso
urce[top=2,left=14,bottom=2,right=14],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=
,selectedIcon=,text=Pushbutton without action,defaultCapable=true]
irb(main):010:0> basePanel.add labelPanel
=> javax.swing.JPanel[,0,0,0×0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,pr
eferredSize=]
irb(main):011:0> basePanel.add buttonPanel
=> javax.swing.JPanel[,0,0,0×0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,pr
eferredSize=]
irb(main):012:0> frame.add basePanel
=> javax.swing.JPanel[,0,0,0×0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,pr
eferredSize=]

Then it’s just to pack the contents and show the frame:

irb(main):013:0> frame.pack
=> nil
irb(main):014:0> frame.show
=> nil

To continue playing the Swing, just poke away at the methods. You can always change it if something goes wrong. After you’re finished you can dump most of the structure very easy. Just remember to call show again to see some of the updates. Certain changes call redraw directly, but mostly it’s always a good choice to call show after updates on the structures.



The JRuby Tutorial #1: Getting started


This is the first installment in a series of tutorials I will write about how to use JRuby for fun and profit. I’m planning on covering a wide range of things you can do with JRuby, from Web frameworks with JDBC to writing your own JRuby extensions in Java. This time I’ll take some time covering the basics; what you can do and how you can do it.

I’m writing these tutorials on Windows 2003, but most operations are not OS-centric, so hopefully people with other systems can use these instructions unchanged.

This tutorial will not teach neither Java or Ruby. A basic knowledge about both programming languages are assumed, but most examples will be in Ruby and focus on Ruby development with help from Java (the issue on JRuby extensions is a notable exception.)

What is JRuby
To quote the homepage: “JRuby is a 100% pure-Java implementation of the Ruby programming language.” Almost complete support for all Ruby constructs work, and the idea is that you should be able to just use your Ruby scripts with JRuby instead of MRI (Matz‘ Ruby Implementation, the original Ruby, written in C). But the goal goes beyond that. Java is a powerful platform and there are millions of lines of Java code being written each month, that the world will have to live with for a long time from now. By leveraging Java the platform with the power of the Ruby programming language, programmers get the best from both worlds.

Limitations and caveats
Of course, nothing is simple, so JRuby can’t do everything that MRI can. Some of these limitations are due to basic constraints in the Java platform. For example, JRuby will never support a fine-grained timed, until Java does it. Some File-operations just won’t work through the Java API. Other limitations are due to differences in implementation, and will probably go away Any Time Now (TM). Some of these include the lack of support for continuations (which Ruby 2.0 won’t support anyway), that JRuby’s threads are real operating system threads instead of Green Threads (which Ruby 2.0 will have too). Some problems exist with extending Java classes from Ruby and having the result show up from Java. These issues are under development and will be available as soon as enough man hours get put on the problem.

Downloading and installing
The first step in downloading JRuby is to decide whether you should go with a released version (which would be 0.9.0 at this time of writing), or run JRuby from trunk. The last few months, development on JRuby have been quite hectic. Much has been done, and trunk already is quite far away from 0.9.0, both in terms of features, performance and bug fixes. This tutorial will be based on trunk, and if there are any special considerations regarding version, this will be noted in the relevant issue.

To download JRuby from trunk, you need to install a Subversion client. One of these can be found at http://subversion.tigris.org and is very easy to install. After you’ve downloaded and installed Subversion you’re ready to decide where you want JRuby to call home. After you’ve decided this, you have to set an environment variable called JRUBY_HOME to this directory. For example, I would do this:

set JRUBY_HOME=d:\project\jruby-trunk

in a command window. (In the long run, it would be better to put this somewhere so that it always get set. In Win32 I go to the System configuration on my Control Panel, choose the Advanced tab and press Environment Variables.)

Now we’re ready to check out the latest JRuby version:

svn co svn://svn.codehaus.org/jruby/trunk/jruby %JRUBY_HOME%

After you have downloaded JRuby you have to build it, so you have to have a recent version of Ant installed. To build and test JRuby, just enter these commands:

cd %JRUBY_HOME%
ant test

The testing will take a while, but it’s good to make sure your installation is fully functional. After you’ve got the final “BUILD SUCCESSFUL”, you can be reasonably sure that your JRuby is functional.

Using Java from Ruby
OK, so now that we’ve got JRuby up and running, we should try a simple example to see that it actually works correctly and that we can use Java classes to. Write this into a file called testJava.rb:


require 'java'
include_class 'java.util.TreeSet'
set = TreeSet.new
set.add "foo"
set.add "Bar"
set.add "baz"
set.each do |v|
puts "value: #{v}"
end

and run with the command

jruby testJava.rb

You will get this output:

value: Bar
value: baz
value: foo

So, what’s happening in this small script is that we first include JRuby’s java-support. This provides lots of interesting capabilities, but the most important one is the method Kernel#include_class, which is used to get hold of a Java-class. In most cases you can just include the classes you’re interested in and use them as is. In our case, we instantiated the TreeSet as usual in Ruby, by calling new on the class. After this we added some strings to the set, and since JRuby automatically transforms Ruby-Strings into Java Strings, these get sorted by TreeSet in ASCII-order. You may have noticed that we could use a standard each-call to iterate over the entries in the set. This is one of the nice features of JRuby. Even though you’re actually working with a Java Collection, it looks and feels (and quacks) like a regular Ruby collection object. All Java classes that implements java.util.Collection automatically gets an each method and includes Enumerable.

The regular include_class function does not work correctly if you try to import a Java class with the same name as an already existing Ruby class. This won’t work, for example:

include_class ‘java.lang.String’

since there already exists a class called String in Ruby. The first way of handling this problem is to rename the class when including it, by adding a block to the call to include_class:

include_class(‘java.lang.String’) {|package,name| “J#{name} }

In this case, the class added to the Ruby name space is called JString and doesn’t collide with the Ruby String.

The second solution uses the fact that include_class includes the classes mentioned into the current name space, so you can call include_class from inside a module definition, and the classes will be available there:

module Java
include_class 'java.lang.String'
end

The Java String is now available as the class Java::String. If you don’t want to include all classes specifically, you can also include a complete package, with include_package. It works exactly like include_class, but takes a package name instead, and makes all classes in that package (but not sub packages) available as classes in the current context.

This is mostly it. JRuby tries as hard as possible to follow the Ruby way, and make Java integration as natural as possible. The ideal is that you should be able to guess how it works, and it will work that way.

Conclusion
The last 6 months, JRuby have become a very real alternative to MRI. As performance gets better and better, the promise of integration with legacy Java systems will get more and more convincing. And using Ruby for tools and tasks where Java only incurs overhead is a very compelling way for Java developers to get the most out of their current tools.

The next issue is slated to be about using Camping from JRuby, together with ActiveRecord and JDBC. Camping is microframework, that together with ActiveRecord lets you write small applications very compact and efficient.



Byte-lexing JRuby.


Since I begun the effort to recreate our lexer with JFlex, me, Charles and Wes Nakamura have discussed how MRI’s lexer can be so much faster than JRuby’s. One thing discussed was that it could maybe be because JRuby lexes with Readers, instead of handling bytes directly. I didn’t believe this would be a big difference, but I set out to test it, anyway. Basically, I replaced all references to chars with bytes, and Readers to InputStreams in our Lexer. There were a few other places that had to be changed. I haven’t done anything else to the lexer (there were many places were we could do some good optimization). The results amazed me. My first test case was completely focused on only the parsing step. There is no evaluation in these numbers. First, trunk JRuby:

Did 10_000 parses in 4336
Did 10_000 parses in 3885
Did 10_000 parses in 3866
Did 10_000 parses in 3835
Did 10_000 parses in 3836
Did 10_000 parses in 3815
Did 10_000 parses in 3836
Did 10_000 parses in 3896
Did 10_000 parses in 3845
Did 10_000 parses in 3926
— Full time for 1_000_000 parses: 39076

and with my byte enhancements:

Did 10_000 parses in 3595
Did 10_000 parses in 3214
Did 10_000 parses in 3215
Did 10_000 parses in 3205
Did 10_000 parses in 3204
Did 10_000 parses in 3184
Did 10_000 parses in 3205
Did 10_000 parses in 3235
Did 10_000 parses in 3204
Did 10_000 parses in 3215
— Full time for 1_000_000 parses: 32476

These times show about 18% increase in parsing speed, just by working with raw bytes instead of characters.

After I integrated the changes and fixed a few bugs, I finally could test the gain in regular JRuby evaluation speed. My test case this time consists of requiring webrick and IRB. First, trunk JRuby:

real 0m5.191s
user 0m0.000s
sys 0m0.010s

And with the byte-patch:

real 0m4.743s
user 0m0.010s
sys 0m0.010s

So, for this particular test case, we get about 5-8%, depending on circumstances. I would never have imagined that it was such an expense to work with Readers instead of InputStreams. The question now is; is it worth it? Should we sacrifice full unicode (or at least non-astral plane) reading for speed? I’m not sure.



Lexing Ruby.


It has become apparent that JRuby’s hand coded Lexer is a liability, in the long run. The code is hard to maintain and probably suboptimal with regards to speed. So two weeks ago I decided to check out the possibility of a Lexer generator. I haven’t had much time, but the last days I’ve started reimplementing the JRuby lexer with the help of JFlex.

The first parts have been really easy, actually. I already have a simple version tied in with JRuby, working. There’s not much of the syntax there yet, but some of it works really fine. Fine enough to check performance and see the road ahead. So, I have two test cases, the first one looks like this:

class H < Hash
end

puts H.new.class
puts H[:foo => :bar].class


and the second like this:


class H < Hash
def abc a, b, *c
1_0.times {
puts "Hellu"
}
end

def / g, &blk
2.times do
puts "well"
end
end
end

H.new.abc 1, 2
H.new/3


The first one test a corner case in subclassing core JRuby classes. The second one is just nonsense to test different parts of the syntax. Both of these parse and run correctly on JRuby with the JFlex-based lexer. It’s not much, but it’s something to build on.

Regarding performance, I’ve created a test program that uses the JRuby parser to parse a specified file 10 x 10_000 times, reporting each time and the total. I’ve run it on both the first and the second example, and both are about 8 to 10 percent faster with the new lexer. I also expect performance to improve more for bigger files. Right now the lexer keeps track on line number, offset and column number, which is also a performance drain. Removing it gives about 2-3 percent more.



Speed in JRuby.


I had really wanted my next post here to be a tutorial about how to get Camping working with ActiveRecord-JDBC and JRuby, but I managed to find two quite serious bugs related to blocks while trying things out. One of the bugs is really strange, and manifests in some of the Markaby CSS-Proxy internals. So, Markaby works, if you refrain from add css class names to tags.

Since Monday I’ve been busy with various things in JRuby. I’ve looked around for easy performance fixes, which there are loads of. Many of them didn’t give much, but a few things actually had a noticeable difference. One of these places was in the loading process, where it’s important to do as few searches as possibly, since most of them are really expensive. Actually, in a few situations JRuby actually parsed and ran a file several times. This happened in WEBrick among others.

I’ve also implemented a Java version of Enumerable, which was very trick to get working at first, since JRuby don’t really support calling methods that yield from Java. So, I had to devise a way of doing this. The generic case seems to be to hard to do right now, but the specific case of calling each and collect the results in a List are finally working well.

I devised a test case where all Enumerable-operations are used 2 or 3 times, and ran this test case 1_000.times. The results for trunk JRuby was ~7s, while the Java implementation of Enumerable took that down to ~5s, which is nice.

Anyway. That’s mostly all. I’m off on vacation now.



JvYAML status


I have finally started work on JvYAML again, and I hope to have a new release together within 2 weeks, which will contain JavaBean materialization and the completed emitter. Hopefully I’ll manage to integrate this with JRuby not long after.

It’s been really hard to get going with this, since I have so many different projects going simultaneously.



Some JRuby tidbits


I’ve spent some time this weekend taking a look at various JRuby issues.

  • Camping:
    The most interesting was trying to get Camping to work. Camping is a microframework for certain kinds of web applications. Very neat, but uses lots of Ruby tricks to function. As such it’s a really good test of JRuby capabilities, but it’s quite hard to debug. I have got it working really good for basic applications, in the process finding a bug in our Hash#[] implementation that fortunately was easy to fix. But today I’ve unearthed something really hairy. The test case demands two classes with two methods each, and it’s some really strange block tweaking that happens. I hope Charlie is up to the challenge. Markaby (which Camping uses), needs this functionality to work.
  • Mongrel:
    I’ve started to take a serious look at the Mongrel support. Danny’s work on the parser seems really great, so I’ve added basic JRuby integration to our Subversion, and plan to try getting it to work with Mongrel-0.4 later on today.
  • ActiveRecord-JDBC:
    Last week I added some Oracle functionality to it, but this still needs some work. I’m thinking that we need to factor out some driver-specific functionality soon. Anyway, I released the a version 0.0.1-gem so that people easily can use what functionality we have.
  • JvYAML:
    I’ve finally begun writing on the Emitter in ernest again. I hope to have most of it finished by Friday, before I go on vacation.

Expect a tutorial on using JRuby and Camping together very soon; hopefully tomorrow. And in a few days after, maybe we can showcase a functional Mongrel in JRuby too?



Announcing Swedish Rails


I would like to announce my plugin Swedish Rails.

Rails provide many goodies and helpers for you, but some of these are dependant on your user interface being in english. Date controls, for example. Pluralization is another. And have you ever tried to capitalize a string with Swedish letters in them? Then you know it doesn’t work that well. Until now, that is. You install this plugin, make sure all your Swedish source uses UTF-8, and most of these problems disappear. Downcase, upcase, swapcase and capitalize work as expected. Integer#ordinalize gives Swedish ordinals. And month names and weekday names are in Swedish.

This plugin isn’t only for people looking to create Swedish Web interfaces. It can also act as a map for creating a plugin like this for any western language. Just replace all the text-strings in this plugin, change the module name and you’re set to go. (Of course you’ll have to know the language you’re porting too, though).

The plugin can be found at:
http://svn.ki.se/rails/plugins/swe_rails
and some information here:
http://opensource.ki.se/swe_rails.html

The plugin itself is fairly simple. Most of it monkey patches different parts of Ruby proper or aspects of Rails. One of the more interesting parts (which could also be usable by itself) translates all internal month and weekday names in Swedish. This is probably the most interesting when doing Rails GUI’s, since you don’t have to create your own date_select tag, and all the other variations on this. I’ve also added som inflector rules, but only for the pluralize helper (so don’t start naming database tables in Swedish, please!). The problem is that english pluralization rules are pretty simple. Just tuck an -s on the end and hope for the best. Swedish uses 5 or six declinations for nouns, and most of these are irregular. This makes it slightly hard to describe inflection rules for Swedish, but I gave it a try anyway.

So, enjoy! I just wish I had done this a year ago. Then I wouldn’t have had to write sublty different versions of this code all over the place.



Rails, SOAP4R and Java.


I’ve spent the last three weeks working part time on a project called LPW at work. LPW is a set of web services that talk with the Swedish student databases. The libraries are implemented in Java and deployed with Axist. My project was to create a web system that students can use to access various LPW services. We had a an old implementation of this, written in Java with uPortal as a framework, but for the new implementation we decided that using Ruby on Rails would be interesting and probably worthwhile.

I’ve spent about 40 hours on the implementation. I did everything except the user interface. I got finished HTML-pages and integrated these with the system. Initially we expected the complete development to take about 120-150 hours with Ruby and about 250 if we did it in Java. In retrospect, I’m pretty sure the Java version would have taken more than that; probably between 350 and 400 hours. Since I wrote the first version in Java I can say this pretty accurately.

So, did I have any interesting experiences while writing this system? Oh yes; otherwise I wouldn’t be writing. First of all, I managed to release two plugins in the process of this project. More information about them can be found in older blog entries. I also tried Mongrel for the first time, and I just have to say that I will never go back to WEBrick.

But the most interesting part with using Ruby was that I would have to get SOAP4R and Axis to work together. In the process I found some interesting things. Let me describe the relevant layout of my application.

The project goal was to implement 4-6 different services, which are all available as separate WSDL-files. I generated the drivers for these with wsdl2ruby. After generating the clients for each, I renamed the driver.rb into for example addr_driver.rb, and created a file called lpw_valueobjects.rb where I put all valueobjects found in defaultDriver.rb. I then repeated this process for each service. I then put all these files into the directory RAILS_ROOT/vendor/lpw.

Now, hitting one or two web services each time a student goes to a page isn’t really realistic, so I decided early to implement some way of caching the results of the service calls. Since the data in question is pretty static this works well.

So, to integrate the services into Rails, I created a base class called LPWObject in the models-directory. In this class I defined self.wsdl_class and a few other helpers that let me transparently handle the caching of service-classes without actually having them inside the LPWObject itself. This becomes important when I want to save the results in the session. Ruby can’t marshal classes which have singleton methods, and WSDL2Ruby depends heavily on singleton methods for the service client.

The first problem with getting Ruby and Java to work over Soap was with swedish characters. When I added something like “Gävlegatan 74″ to an attribute and then tried to send this over soap, Soap4R transformed the attribute type into Base64 instead of xsd:String. Suffice to say, Axis didn’t really like this. The solution was to add $KCODE=”UTF8” to environment.rb. This let’s Soap4R believe that åäöÅÄÖ is part of regular strings.

The next problem came when I tried to save some of the value objects into the session. After looking for a long while, I found that if the value object had an attribute called “not”, WSDL2Ruby didn’t generate an
attr_accessor :not
for this until at runtime, which creates singleton methods on the value object. The solution was to add these accessors by myself. I’m not sure why wsdl2ruby does it like this, but probably there is some weird interaction with the not keyword in Ruby.

The final problem – which I’m not sure if I should blame Axis or Soap4R for – came when one of the value objects contained a byte array with a PDF-file. For some reason Axis sends the regular response XML looking fine, but before and after there are some garbled data. It looks like Axis actually sends the byte data as an attachment too, not just inside the byte array. Soap4R didn’t handle this at all, and I got an “illegal token” from the XML parser. The solution to this problem is the worst one, and I should really send a bug report about this, but I haven’t had the time yet. Anyway, to fix it, I monkeypatched SOAP::HTTPStreamHandler, and aliased the method send. Inside my new send method I first called the old one, then use a regexp to extract only the xml-parts of the response and reset the receive_string to this. It fixes my problem and works fairly well, but it isn’t pretty.

So, in conclusion, Soap4R and Axis seem to work good together, except for a few corner cases. I’m really happy about a project that could’ve taken 10 times longer to complete, though.



InPlaceEditor with Autocompletion.


Since the AJAX controls in Rails are so neat, and the InPlaceEditor is probably the neatest since it’s so easy and useful, I immediately decided to start fixing one small feature I needed/wanted very much. That is, autocompletion on InPlaceEditor-fields. So, I’ve created a plugin that does this. It was actually much easier than I thought, since the prototype JavaScript library makes JavaScript almost pain-less to work with. Not quite nice, but not bad either.

Anyway, it’s dead simple to use, do it like you’ve done it with InPlaceEditor and you’ll be find. More information can be found here (I’ve finally convinced my employer to host a place where we can release open source, so it can be seen that KI actually supports open source). The Subversion path is: http://svn.ki.se/rails/plugins/in_place_completer.

Much joy!