Keeping it Small and Simple

2008.03.01

Testing JRuby 1.1 RC2

Filed under: JRuby, Ruby Programming — Tags: , , , — Lorenzo E. Danielsson @ 18:20

Of late my schedule has been a bit more hectic than I had hoped. So I haven’t had as much time I would have liked to play around with JRuby and Rubinius. But the other day I was finally able to find a little time. So I downloaded the latest JRuby 1.1 release candidate, which is RC2.

So far it has worked well. I hear that 1.1 is significantly faster than previous versions of JRuby. I cannot comment on that yet, because I haven’t begun playing around with the types of applications where speed would matter.

Installing it

I downloaded the .tar.gz package and installed it to /opt. Do this (as root):

# cd /opt
# tar zxf /home/lorenzod/dl/jruby-bin-1.1RC2.tar.gz

After that you will want to either (1) add /opt/jruby-1.1RC2/bin to PATH or (2) create a few symlinks. I chose the symlink option.

# JD=/opt/jruby-1.1RC2/bin
# ln -s $JD/jruby /usr/local/bin
# ln -s $JD/jrubyc /usr/local/bin
# ln -s $JD/jirb /usr/local/bin
# ln -s $JD/gem /usr/local/bin/jgem

You can of course add all the links that you need. These were enough for me to get started. Note that I named the link to jruby’s gem jgem.

Testing the compiler

I wanted to see how well the JRuby compiler works. So, I quickly put together the following little test program.


 1 #! /usr/bin/jruby
 2
 3 # Count the number of times the user clicks some buttons.
 4
 5 require java
 6
 7 include_class java.awt.GridLayout
 8 include_class java.awt.event.ActionListener
 9 include_class java.awt.event.WindowListener
10 include_class java.lang.System
11 include_class javax.swing.JButton
12 include_class javax.swing.JFrame
13
14 class ClickButton < JButton
15   include ActionListener
16
17   def initialize(text)
18     @count = 0
19     @text = text
20     super "#{@text} (0)"
21     add_action_listener self
22   end
23
24   def actionPerformed(event)
25     @count += 1
26     self.text = "#{@text} (#{@count})"
27   end
28 end
29
30 class MainWindow < JFrame
31   include WindowListener
32   include ActionListener
33
34   def initialize
35     super "Click Counter"
36     set_layout GridLayout.new(5, 1)
37     @total = 0
38
39     1.upto 5 do |n|
40       button = ClickButton.new "Button #{n}"
41       button.add_action_listener self
42       add button
43     end
44
45     add_window_listener self
46     pack
47   end
48
49   def actionPerformed(event)
50     @total += 1
51   end
52
53   # Bah, humbug!
54   def windowActivated(event); end
55   def windowClosed(event); end
56   def windowDeactivated(event); end
57   def windowDeiconified(event); end
58   def windowIconified(event); end
59   def windowOpened(event); end
60
61   def windowClosing(event)
62     puts "Total clicks: #{@total}"
63     System::exit 0
64   end
65 end
66
67 MainWindow.new.set_visible(true)

First I tried to run it normally.

% jruby cc.rb

That worked fine. After a few seconds a Swing application popped up on my screen. Next I tried compiling it.

% jrubyc cc.rb
Compiling cc.rb to class ruby/cc
% ls ruby/
cc.class

To run this program I did the following:

% java -cp /opt/jruby-1.1RC2/lib/jruby.jar:. ruby.cc

It worked. JVM takes a moment or two to fire up, then the application window pops up. Of course, I haven’t looked into the options to jrubyc yet, but at least this was enough to get me started.

Next, I’ll want to try JRuby out in Netbeans. I haven’t tried JRuby on Rails yet, so that is something I want to dedicate some time to. So expect more on JRuby from me in the near future.

2008.02.01

QOTD: 2008.02.01

Filed under: JRuby — Tags: , , , — Lorenzo E. Danielsson @ 18:58

I don’t normally do quotes, but this little gem from the Monkeybars project page put a smile on my face.

So, what do we mean by “easily”? Well, I’ve got a pretty low tolerance for pain in my code so ideally everything gets reduced down to a one liner.

2007.05.28

Yes, JRuby is Ruby

Filed under: JRuby, Ruby Programming — Lorenzo E. Danielsson @ 19:52

JRuby is an implementation of the Ruby language. Contrary to what some people seem to think, it is not a stripped-down version of Ruby (look elsewhere for that). And it is not a different language. (Somebody actually tried suggesting to me the other day that the relationship Ruby-JRuby is comparable to that of Java-JavaScript. That is utter nonsense.)

So JRuby is Ruby, with the added bonus of giving you access to the Java class library. And no, that does not mean that you have to write Ruby code that looks like it was Java. Let’s take a look.

Looking inwards

You can learn a lot about JRuby classes by reflection. Here, for example, I create an instance of a javax::swing::JLabel and then list all the available methods.

1 #! /usr/local/bin/jruby -w
2 
3 require 'java'
4 
5 label = javax::swing::JLabel.new "Test"
6 label.methods.each { |method|
7   puts method
8 }

If you run this program it will list all the methods available on javax::swing::JLabel instances, and there are many (./listmethods.rb | wc -l tells me 917 lines). You can always use grep to find specific methods. For instance, I wanted to find only methods that contain the word visible:


% ./listmethods.rb | egrep '[vV]isible'
compute_visible_rect
scrollRectToVisible
visible_rect
visible=
set_visible
setVisible
visibleRect
get_visible_rect
computeVisibleRect
scroll_rect_to_visible
getVisibleRect
visible
visible?
is_visible
isVisible

If you study this output you will notice that the JRuby “java classes” come packed with goodies to make things feel “right” for a Ruby programmer. For instance, instead of:


javax::swing::JFrame.new.setVisible(true)

you can, if you so wish, do:


javax::swing::JFrame.new.visible = true

Let’s look at an example where we create a JFrame holding a JLabel. But let’s make it look like Ruby, not Java (not that I’m suggesting there is anything wrong with Java).

 1 #! /usr/local/bin/jruby -w
 2 
 3 require 'java'
 4 
 5 include_class 'javax.swing.JLabel'
 6 include_class 'javax.swing.JFrame'
 7 
 8 class Frame < JFrame
 9   def initialize
10     super "JRuby/Swing Demo"
11     default_close_operation = JFrame::EXIT_ON_CLOSE
12     label = JLabel.new ""
13     label.text = "JRuby is Ruby!"
14     add label
15     pack
16   end
17 end
18 
19 frame = Frame.new
20 unless frame.visible?
21   puts "The frame exists but is not yet visible."
22 end
23 frame.visible = true
24 

If you look at lines 11, 13 and 23 you see that I am using assignment instead of setter methods. (Incidentally, it seems that default_close_operation is broken, at least for me. Then again, I am using a release candidate..) Also on line 20 I am using frame.visible? rather than frame.isVisible(). So if you already are a Ruby programmer, there is no need to learn “the Java way” (although that also works).

I’ll give you one final example. How would you create a singleton class in JRuby? The same you would do it in Ruby, of course. Look at the following:

 1 #! /usr/local/bin/jruby -w
 2 
 3 require 'java'
 4 require 'singleton'
 5 
 6 include_class 'javax.swing.JLabel'
 7 include_class 'javax.swing.JFrame'
 8 
 9 class SingletonFrame < JFrame
10   include Singleton
11 
12   def initialize
13     super "Singleton Frame"
14     setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
15     label = JLabel.new "JRuby/Swing"
16     add label
17     pack
18   end
19 end
20 
21 frame1 = SingletonFrame.instance
22 frame2 = SingletonFrame.instance
23 
24 puts "frame2 visibility: #{frame2.isVisible}"
25 frame1.setVisible true
26 puts "frame2 visibility: #{frame2.isVisible}"
27 

Note that frame1 and frame2 are references to the same object. Also, you cannot call new on SingletonFrame (go ahead and try it). This works just like any Ruby programmer would expect.

I have just scraped the surface of what is possible with JRuby (after all this is not a tutorial). Hopefully I may have tickled somebody’s curiosity out there. And hopefully I’ve been able to show with these few examples that by using JRuby you are not “losing” anything as compared to Ruby (and then I mean C-Ruby or whatever you want to call it).

2007.05.24

Dealing with Java exceptions in JRuby

Filed under: Java Programming, JRuby, Ruby Programming — Lorenzo E. Danielsson @ 16:32

As you may know already, JRuby gives you access to the Java class library, so that you can do things like write Swing applications in Ruby. Of course, that also means that your JRuby code needs to deal with the exceptions that various Java classes can throw.

Exception handling is similar in Ruby and Java, but there are a few small differences. If you know both Java and Ruby you already know that in Java we use try and catch whereas in Ruby we use begin and rescue. But the principle is the same. Any code that could throw and excpetion goes into a try (or begin) block. If an exception is thrown, execution moves to the corresponding catch (rescue) block.

But Java has two slightly different types of exceptions: “normal” exceptions and runtime exceptions. The difference is that if you call anything that can throw java.lang.Exception or one of its subclasses, then you must enclose that code in a try-catch block. So, for instance, the following code will not compile:

1 public class ForNameTest {
2     public static void main(String[] args) {
3         Class.forName("java.lang.String");
4     }
5 }

Trying to compile we get:

% javac ForNameTest.java
ForNameTest.java:3: unreported exception java.lang.ClassNotFoundException; must be caught or declared to be thrown
        Class.forName("java.lang.String");
                     ^
1 error

In Ruby, however, it is not required to put any code within a begin-rescue block (in other words, Ruby treats all exceptions as they were Java runtime exceptions). Here is the equivalent JRuby code:

1 #! /usr/local/bin/jruby -w
2 
3 require 'java'
4 
5 java::lang::Class.forName("java.lang.String")

This will run without any problems. Notice another thing. I have specified the fully qualified name of the Java Class class. The reason is simple. Both Ruby and Java contain a class called Class. If I don’t qualify it, JRuby will assume that I want to use the Ruby class, which does not contain a forName method. Keep that in mind when there is both a Ruby class and a Java class with the same name.

Also note that just because we don’t have to use a begin-rescue in Ruby doesn’t mean that it is not a good idea. On the contrary, you should use exception handling to trap errors and do something useful when they occur.

Apart from that, dealing with Java exceptions in JRuby is very straightforward. Look at the following example:

 1 #! /usr/local/bin/jruby -w
 2 
 3 require 'java'
 4 
 5 include_class 'java.lang.ClassNotFoundException'
 6 
 7 begin
 8   # This will work.
 9   java::lang::Class.forName("java.lang.String")
10 
11   # This will not.
12   java::lang::Class.forName("foo.Bar")
13 rescue ClassNotFoundException => e
14   $stderr.print "Java told me: #{e}n"
15 end

Simple, wasn’t it? The first call to forName is not problematic because java.lang.String is a valid class name. The second call will fail, because under normal circumstances we don’t have a foo.Bar class (unless you have added one).

2007.05.17

Creating a simple Swing application in JRuby

Filed under: Java Programming, JRuby, Ruby Programming — Lorenzo E. Danielsson @ 09:27

First of all, for those who may not know, JRuby is an implementation of the Ruby programming language in Java. In addition to being a Ruby interpreter, JRuby also integrates with the Java platform, so you can interact with your existing Java classes from JRuby.

Being implemented in Java, JRuby of course has full support for Unicode. It also supports the Bean Scripting Framework. Supposedly you can run Ruby on Rails with JRuby, although I have not yet tried doing so myself. For more information about JRuby, go to their web site.

For those who’d like to try it, I’ll give you a very simple Swing application. If you are a bit familiar with both Ruby and Java, then you’ll get into JRuby in no time.

 1 #! /usr/local/bin/jruby -w
 2 
 3 require 'java'
 4 
 5 include_class 'java.awt.event.ActionListener'
 6 include_class 'javax.swing.JButton'
 7 include_class 'javax.swing.JFrame'
 8 
 9 class ClickAction < ActionListener
10   def actionPerformed(event)
11     puts "Button got clicked."
12   end
13 end
14 
15 class MainWindow < JFrame
16   def initialize
17     super "JRuby/Swing Demo"
18     setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
19 
20     button = JButton.new "Click me!"
21     button.addActionListener ClickAction.new
22     add button
23     pack
24   end
25 end
26 
27 MainWindow.new.setVisible true

There that should be it. Remember, this is Ruby, so there’s no need to call the file anything special, and no “only one public class per file” restriction.

So how do you run this program? First of all, you need to have JRuby installed, which I assume you have. I installed my copy into /usr/local/jruby. I then created a symlink from /usr/local/jruby/bin/jruby to /usr/local/bin/jruby. That way the JRuby interpreter behaves just like the “normal” Ruby interpreter.

Assuming you have saved your program as swingtest.rb, installed JRuby and created your symlink, you should just have to do the following:


% chmod +x swingtest.rb
% ./swingtest.rb

Simple, wasn’t it?

Update

As I was writing this post, I downloaded the latest version of JRuby and the code above no longer runs. It turns out that you can no longer extend an interface. You are affected if you get this error:


% jruby button.rb
button.rb:[13,19]:[326,432]: superclass must be a Class (#) given (TypeError)

Fortunately, it’s very simple to fix. Simply change the ClickAction class to look as follows:

class ClickAction
  include ActionListener

  def actionPerformed(event)
    puts "Button got clicked."
  end
end

Now it should run fine. Still simple.


Update 2007.05.17: fixed the code to work with JRuby-1.0RC2.

Create a free website or blog at WordPress.com.