So recently I was looking at Monkeybars. Monkeybars is not a UI as such, but a framework for using Swing inside an IDE. It attempts to hide all the underlying Java, so you just design your interface through your IDE, and the rest is Ruby (specially, JRuby of course, for Swing).
There is a problem with Monkeybars; it does not work with recent versions of JRuby. I found an older version in an example file, and that works old (I have reported this as a bug here).
Installing is simple (this will also install the "rawr" gem):
gem install monkeybars
Create a project
To create a new project:
You than need to set it up in your IDE. Monkeybars was designed with NetBeans in mind, and that is the IDE I use too (it obviously needs both Ruby and Java included). It needs to be a Java project, as you are actually running a Java application that uses Ruby, so your new NetBeans project will be a "Java Project with Existing Sources". You need to point NetBeans to the source packages in the "my_project/src" folder. Once the project is created, right click on "Libraries" in the project browser, select "Add JAR/Folder" and select the .jar files in "my_project/lib/java".
When you first run your project, NetBeans will ask for the main file; select "org.rubyforge.rawr.Main"
Create your model-view-controller
Just like Rails, you can use a rake task to generate these (in ths example, called "main"):
rake generate ALL=src/main
This will generate the files:
Create the UI
You actually need a fourth file, which is the UI itself. In the IDE, right click on the folder (in this case "main") in the project browser, and select New - JFrame Form, and give it a suitable name (I chose MainJFrame). You can now add components graphically, using your IDE. The important point to remember is that any component you want your Ruby code to interact with should have a Ruby-friendly name. A "Quit" option on the file menu might be "quit_menu_item", but the menu itself you can leave to the default.
To keep it simple, drag a label on to the dialog box, and set the variable name (found under the code tab) to "message".
Edit the view
The main_view.rb file is the glue between the Java UI and your Ruby code. The first thing it needs is the name of your Java UI. Then it needs to know how the components on the UI map (or more accurately, properties of the components) to the model. In this example, then we need only two lines.
class MainView < ApplicationView
map :model => :message, :view => "message.text"
You can have as many map statements as you need, one for each component that displays data (not required for buttons, etc. that generate events only). The :view part means that one end of the link is to the text property of the JLabel that we called "message". The other end of the link is the message property in the model.
You can set a mapping to be one way, by adding a :using value. In our example, the label cannot be edited directly by the user, so we only want the data to go from the model to the UI component, not the other way around (you can flip "nil" and ":default" to have the data go the other way only, but your model needs to provide read and write access to the property even so, because of the way Monkeybars creates a new model with the view data, then transfers data from that to the real model).
map :model => :message, :view => "message.text", :using => [:default, nil]
You can also use the ":using" value to specify a method to convert the data; give the method name instead of :default.
Edit the controller
The controller needs to be told the name of the model, and the name of the view. You can also set an action for when the dialog close is clicked. All of this is done for you, and is all we need for this simple application.
class MainController < ApplicationController
However, if you have any way for the user to interact with your dialog box, that gets captured here. Let us suppose you have a "Quit" option on yor file menu, and you have set the variable name to "quit_menu_item". This method will respond to that menu item being selected, and ask for confirmation.
r = javax.swing.JOptionPane.showConfirmDialog(nil,
"Do you really want to quit?",
exit if r == javax.swing.JOptionPane::YES_OPTION
Here is another example for a button; the user presses the button and the text in two JTextAreas is used to update the model (the first JTextArea is called "text_area_1", but mapped to "text1" in the model, using
map :model => :text1, :view => "text_area_1.text"in the view). The update_model method is used to transfer data to the model. Then the update method in the model is called (this is a method I have written, to do what I need in my model). Finally, update_view is called so the UI is updated to reflect the new state of the model.
model.text1 = view_state.model.text1
model.text2 = view_state.model.text2
What happens is that calling view_state creates a new instance of the model, and this is populated with the values from the UI. You can then copy across the values you want into the real model. A convenience method, update_model, can be used instead.
update_model(view_state.model, :text1, text2)
This is how the controller will handle most events, first transfer the data from the UI to the model, then call a method in the model, then update the view, so you could have one method to create a whole set.
%w(up down edit cut paste insert).each do |action|
%w(button menu_item).each do |type|
model.text = view_state.model.text
You can set the value for set_close_action to :nothing, :exit, :close, :dispose or :hide. If you want other functionality, override one of those methods in the controller (I found I could override close, but not exit).
Edit the model
The model needs to include accessor methods for all the properties you mapped to in the view, and methods for all the actions. In our simple example, like this:
You can set properties through properly defined methods, but that is something of a minefield, I have found, and better avoided.
There is no more to say about the model; Monkeybars makes no assumptions about it, and it has no parent class to inherit from (other than Object). This is where you do the real work.
Struggling with Ruby: Contents Page