I had a good time tonight at the Gateway JUG attending a talk by Charles Nutter on JRuby, which is Ruby on the Java VM. Personally, I’m really excited to see Sun putting an investment in dynamic languages and considering changes to the JVM to better support them. Looking a few years down the line, Java (and the JVM) need to increase their support for dynamic and lighter weight languages to remain relevant.
I was particularly interested to hear that they are building support for scripting languages like JavaScript and Ruby into NetBeans and looking to bring the same level of refactoring support we see for Java into the IDE. This is a difficult thing to do but it’s a critical gap to close if dynamic languages are going to gain mainstream penetration.
Charles did a lot of interactive live examples with JRuby and I took a transcript of a lot of it, in case anyone from the meeting is interested. There were three code sessions focusing on Ruby, JRuby (Java integration), and Rails on JRuby. Here are transcripts from each.
Ruby:
x = ["foo", "bar", 1] # literal syntax for arrays (heterogenous) x = {:foo => "bar"} # literal syntax for maps # :foo is a symbol (a string, with uniqueness and identity guarantees, like interned string) # Define a method def hello(name) puts "hello' + name end # Print out, using variable in string puts "Hello #{name}" # times operator on integer 10.times { puts "Hello" } # block, for each x, print x over array ["foo", "bar"].each { |x| puts x } # yield in a method def greet yield "Greetings!" # yield to the block end # yield method with block greet { |x| puts x } # define class class MyClass # constructor def initialize(name) @name = name end # method def display puts "My name is #{@name}" end end mc = MyClass.new("Joe") mc.display # reopen class and add to it class MyClass def display puts "My name is REALLY #{@name}" end end # pre-existing instance has been changed as well mc.display # to define a mixin, use a module module Displayer def display_times # method doesn't exist in module, but does when mixed in 10.times { display } end end # open the class again and include a mixin class MyClass include Displayer end # use the mixed in method mc.display_times # metaprogramming - list methods on class and sort mc.methods.sort # list all class methods minus all super class Object methods mc.methods - Object.methods # add command completion to irb require 'irb/completion' # add a new method to the built-in Fixnum class class Fixnum def prime? return true end end # Remove a method from a class class Fixnum undef prime? end # Alias methods to new names # Inheritance class MyClass < OtherClass # exception handling in a method begin raise "Hello" # throw rescue Exception => e # catch ensure puts "x" # finally puts e.backtrace end # add method only to a particular instance (x in this case), not to a class x = 5 class << x def prime? true end end x.prime? # returns true 7.prime? # fails
JRuby and Java:
require 'java' java.lang.System.out.println("Hello") System = java.lang.System System.out.println("Yay!") f = javax.swing.JFrame.new("My Frame") f.set_size(200, 200) # ruby casing converted to java casing f.show # frame shows up f.set_size(300, 300) b = javax.swing.JButton.new("Press me!") f.content_pane.add(b) # getters/setters JavaBean become like attributes f.show class MyListener < java.awt.event.ActionListener def actionPerformed(event) event.source.text = "Please don't press me again!" end end b.add_action_listener(MyListener.new) include_class "java.util.ArrayList" # now available for use w/o namespace # also way to pull in all classes in a package (but brute force search) x = ArrayList.new # treat ArrayList as Ruby array x << "hello" x.each { |y| puts y } # also possible to add methods to ArrayList, but not available on Java side # Ruby to Java -> # bean scripting framework # implement interface # new Java scripting API
Rail on JRuby:
rails gateway_app cd gateway_app/ jruby script/server jruby script/generate controller Test hello # Test=controller, hello=action (http://localhost:3000/test/hello) test_controller.rb: class TestController < ApplicationController def hello @name = "Gateway" end end hello.html: <h1>Test#hello</h1> <h2>Hello, <%=@name %></h2> jruby script/generate migration create_person vi db/migrate/001_create_person.rb # 001 = version of the database class CreatePerson < ActiveRecord:Migration def self.up create_table :people do |table| table.column :firstname, :string table.column :lastname, :string table.column :age, :integer end end def self.down end end vi config/database.yml # edit the database config, whitespace is important adapter: jdbc url: jdbc:mysql://localhost/gateway_development username: gateway password: gateway driver: com.mysql.jdbc.Driver vi config/environment.rb require 'active_record/connection_adapters/jdbc_adapter' mysql create database gateway_development; grant all on gateway_development.* to 'gateway'@'localhost' identified by 'gateway'; rake db:migrate jruby script/generate scaffold person http://localhost:3000/people vi app/models/person.rb class Person < ActiveRecord::Base end