Jan 30 2007

F3/swiby style declaration blocks

Julian Doherty

swiby is a proof of concept framework inspired by F3. Both use a hierarchical block format for defining Java/Swing visual elements. eg:

Frame {
  title "Hello World F3"
  width 200
  content {
    Label {
      text bind(model, :saying)
    }
  }
  visible true
}

I was looking through the documentation, and got curious as to how the class level block declaration is actually implemented in swiby. After a bit of digging around, it is relatively straight forward.

There is some code in swiby that looks kinda like this:

def component_factory(class_name)
  eval %{
    def #{class_name}(&block)
      x = #{class_name}.new
      x.instance_eval(&block)
      x
    end
  }
end

I’ve removed the error handling and some extra functionality, but the basic idea is the same. component_factory is called for each class that is required to be instantiated as a declared block. Here’s an example:

class Foo
end

class Bar
end

component_factory :Foo
component_factory :Bar

my_foo = Foo {
  @my_bar = Bar {
    @baz = 'baz'
  }
}

puts my_foo.inspect

Which outputs:
#<Foo:0x60544 @my_bar=#<Bar:0x604e0 @baz="baz">>

A Foo instance has been created containing a Bar instance, with a variable of baz. This sort of syntax could be useful for DSL implemented in Ruby.

One limitation I ran into, is that calling attr_accessor methods don’t get recognized (they just get defined as local variables that go out of scope at the end of the block) - but that may just be a limitation of useing instance_eval to set the newly created object as the object in scope as self.


Jan 25 2007

Java style stacktrace for JRuby code called from Java

Julian Doherty

As part of a project to write some nice Ruby APIs for creating Swing GUIs, I’ve been a little frustrated with the exceptions from JRuby in the Java code that calls it - particularly getting stack traces, and error messages.

Say you’ve got some code like this:

try {
  new ScriptEngineManager().
      getEngineByName("jruby").
      eval("raise 'foo is too bar'");
} catch (Exception e) {
  e.printStackTrace();
}

You’ll get output that looks something like:

javax.script.ScriptException: org.jruby.exceptions.RaiseException
  at com.sun.script.jruby.JRubyScriptEngine.evalNode(JRubyScriptEngine.java:376)
  at com.sun.script.jruby.JRubyScriptEngine.eval(JRubyScriptEngine.java:138)
  at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:245)
  at Borked.main(Borked.java:9)
Caused by: org.jruby.exceptions.RaiseException

Not very helpful. How are you supposed to know what went wrong where?

After a bit of poking round with the debugger, it turns out there is more information in the exception you get back. You just have to work to get it. The following code will grab the Ruby exception message and stacktrace and spit it back out in the same style as native Java exceptions are output.

try {
  new ScriptEngineManager().
      getEngineByName("jruby").
      eval("raise 'foo is too bar'");
} catch (Exception e) {
  RaiseException re = (RaiseException)e.getCause();
  System.out.println(re.getException().getMetaClass() + ":" + re.getException().toString());
  RubyArray rubyStackTrace = (RubyArray) re.getException().backtrace();
  for(Object stackLine : rubyStackTrace) {
    System.out.println("\tat " + stackLine);
  }
}

Here’s the output from some code I’ve been working on

ArgumentError:unknown key:foo
  at /development/wring/classes/wrung_test.rb:17:in `each'
  at /development/wring/classes/wrung.rb:43:in `populate_defaults'
  at /development/wring/classes/wrung_test.rb:17:in `add_comp'
  at /development/wring/classes/wrung_test.rb:17
  at :0:in `require'
  at :0

Which is pretty much exactly what a Java stack trace would look like - except its for the Ruby code running inside JRuby called by Java.