jRuby
The best of both worlds




                      Christopher Spring
@autonomous
Confessions of a... Rubyist
Brief history of
Yukihiro “Matz” Matsumoto
Yukihiro “Matz” Matsumoto




“I wanted a scripting language that was more powerful
  than Perl, and more object-oriented than Python...”
“[We] need to focus on humans, on how humans
care about doing programming... We are the masters.
               They are the slaves.”
“I hope to see Ruby help every programmer in
     the world be productive, and to enjoy
         programming, and to be happy.
This is the primary purpose of Ruby language”
A   primer
# booleans
truth = true
lies = false

# Strings
dude = 'Matz'
credo = "#{dude} is nice,
          so we are nice!"

# Numbers
42 ** 13
53.8 / 9
19 % 3
# Arrays
backpack = [6, 'apple', [true, some_object]]

# Hashes
hash = {
  true     => 'He loves me!',
  dude     => 'Must obey!',
  backpack => false
}

# symbols
cities = [:paris, :new_york, :johannesburg]
Wtf is a symbol?
coders = [
  { 'language' => 'ruby', 'location' => 'johannesburg' },
  { 'language' => 'java', 'location' => 'port elizabeth' },
  # ...
  { 'language' => 'ruby', 'location' => 'pretoria' }
]

                           # vs.

coders = [
  { :language => 'ruby', :location => 'johannesburg' },
  { :language => 'java', :location => 'port elizabeth' },
  # ...
  { :language => 'ruby', :location => 'pretoria' }
]
Cool, what about
    classes?
public Class Person {
  private String firstName, lastName;

    public Person(String firstName, String lastName){
      setFirstName(firstName);
      setLastName(lastName);
    }

    public String getFirstName(){
      return firstName;
    }

    public String getLastName(){
      return lastName;
    }

    public String setFirstName(String firstName){
      this.firstName = firstName;
    }

    public String setLastName(String lastName){
      this.lastName = lastName;
    }
}
class Person
  attr_accessor :first_name, :last_name

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
end
class Person
   attr_accessor :first_name, :last_name

   def initialize(first_name, last_name)
     @first_name = first_name
     @last_name = last_name
   end
 end



person            = Person.new( 'Nick', 'Cave')
person.first_name = 'Slick'
person.last_name = 'Nave'
puts "#{person.first_name} #{person.last_name}"
Btw, everything is an
      Object!
true.class() # => TrueClass
false.to_s() # => "false"

5.9.floor()   # => 5

3.times{ puts('Hello JUG!') }
# => Hello JUG!
# => Hello JUG!
# => Hello JUG!
module Earth




end
module Earth
  class Person
    attr_reader :first_name, :last_name

    def initialize(first_name, last_name)
      @first_name, @last_name = first_name, last_name
    end
  end
end
module Earth
  class Person
    attr_reader :first_name, :last_name

    def initialize(first_name, last_name)
      @first_name, @last_name = first_name, last_name
    end
  end
end




  chap = Earth::Person.new('Chandler', 'Bing')
  chap.first_name # => Chandler
module RepublicPersonnel
  def storm_trooper
    Earth::Person.new('Storm', 'Trooper')
  end
end

class CloningVat
  extend RepublicPersonnel
end
module RepublicPersonnel
  def storm_trooper
    Earth::Person.new('Storm', 'Trooper')
  end
end

class CloningVat
  extend RepublicPersonnel
end




#<Earth::Person @last_name="Trooper", @first_name="Storm">
CloningVat.storm_trooper
module TehForce
  def sense
    '... a disturbance in the force'
  end
end

module Earth
  class Person
    include TehForce
  end
end
module TehForce
  def sense
    '... a disturbance in the force'
  end
end

module Earth
  class Person
    include TehForce
  end
end



    farm_boy = Earth::Person.new('Luke', 'Skywalker')

    # => '... a disturbance in the force'
    farm_boy.sense
def some_days( something_happens )
  puts 'The sun rises'
  puts something_happens
  puts 'The sun sets'
end

def other_days( this_happens, and_that_happens)
  puts 'The sun rises'
  puts this_happens
  puts and_that_happens
  puts 'The sun sets'
end



    some_days 'I do some work'
    other_days 'I skip', 'I read a book'
def some_days( something_happens )
  puts 'The sun rises'
  puts something_happens
  puts 'The sun sets'
end
def some_days( something_happens )
  puts 'The sun rises'
  puts something_happens
  puts 'The sun sets'
end
def some_days( something_happens )
  puts 'The sun rises'
  puts something_happens
  puts 'The sun sets'
end



def some_days
  puts 'The sun rises'
  yield
  puts 'The sun sets'
end

some_days { puts 'I go to work' }

some_days do
  puts 'I skip'
  puts 'I read a book'
end
file = File.open('i <3 ruby.txt', 'w')
file.puts "... i really do!"
file.close
file = File.open('i <3 ruby.txt', 'w')
 file.puts "... i really do!"
 file.close




File.open('i <3 ruby.txt', 'w') do |file|
  file.puts "... i really do!"
end
Rubyists TATFT!
describe Calculator do
  before(:each) do
    @calc = Calculator.new
  end

  context 'Addition' do
    it 'returns 10 when adding 5 and 5'
      @calc.add(5,5).should == 10
    end

    it 'returns 0 when adding 5 and -5' do
      @calc.add(5,-5).should == 0
    end
  end
  # ...
end
Metaprogramming
class Person
  def initialize
    @height, @weight, @hair_color = 6.2, '70kg', 'brown'
  end

  %w(height weight hair).each do |property|
    define_method("#{property}") do
      i_var = self.instance_eval("@#{property}")
      i_var.to_s.upcase
    end
  end

  def method_missing(*args, &block)
    puts "I dont have #{args[0].to_s.gsub(/_/, ' ')}"
  end
end

p = Person.new
p.my_height       #   =>   6.2
p.my_weight       #   =>   70KG
p.my_hair_color   #   =>   BROWN
p.your_car_keys   #   =>   I dont have your car keys
A   primer
or....
Demo time!!
Interactive Ruby

 $ jirb
 jruby-1.6.7 :001 > require 'java'
  => true
 jruby-1.6.7 :001 > #...
Ackermann
class Ackermann
  def self.ack(m, n)
    return n + 1           if m.zero?
    return ack(m - 1, 1)   if n.zero?

    ack( m - 1, ack(m, n - 1) )
  end
end
include Rubeus::Swing

JFrame.new('Ackerizer') do |frame|
  frame.layout = java.awt.FlowLayout.new
  @m, @n = JTextField.new('3'),JTextField.new('10')

  JButton.new('->') do
    start = Time.now

    @result.text = Ackermann.ack(
      @m.text.to_i,
      @n.text.to_i
    ).to_s

    puts "#{Time.now - start}"
  end

  @result = JTextField.new 10

  frame.pack
  frame.show
end
class Ackermann
  def self.ack(m, n)
    return n + 1           if m.zero?
    return ack(m - 1, 1)   if n.zero?

    ack( m - 1, ack(m, n - 1) )
  end
end
class Ackermann
  def self.ack(m, n)
    return n + 1              if m.zero?
    return ack(m - 1, 1)      if n.zero?

    ack( m - 1, ack(m, n - 1) )
  end
end


public class Ackermann {
  public static int ack(int m, int n) {
    if (m == 0)
      return n + 1;

        if (n == 0)
          return ack(m - 1, 1);

        return ack(m - 1, ack(m, n - 1));
    }
}
include Rubeus::Swing

JFrame.new('Ackerizer') do |frame|
  frame.layout = java.awt.FlowLayout.new
  @m, @n = JTextField.new('3'),JTextField.new('10')

  JButton.new('->') do
    start = Time.now

    @result.text = Java::Ackermann.ack(
      @m.text.to_i,
      @n.text.to_i
    ).to_s

    puts "#{Time.now - start}"
  end

  @result = JTextField.new 10

  frame.pack
  frame.show
end
Method overloading
public class OverloadTypeChecker {
  public static String inspect(long value) {
    return "long";
  }

    public static String inspect(String value) {
      return "string";
    }

    public static String inspect(Object value) {
      return "object";
    }
}
require 'java'

java_import 'OverloadTypeChecker' do
  'OTC'
end

# jRuby happily handles the simple case
OTC.inspect(5)           # long
OTC.inspect('Helloooo!') # string
OTC.inspect([])          # object
public class BitLength {
  public int neededFor(int i) {
    return 32;
  }

    public int neededFor(long l) {
      return 64;
    }
}
# jRuby can be forced to use a specific overload
require 'java'
java_import 'BitLength'

bits = BitLength.new
bits.needed_for(1_000_000) # 64
# jRuby can be forced to use a specific overload
require 'java'
java_import 'BitLength'

bits = BitLength.new
bits.needed_for(1_000_000) # 64

# java_send
bits.java_send :neededFor, [Java::int], 1_000_000 # 32
# jRuby can be forced to use a specific overload
require 'java'
java_import 'BitLength'

bits = BitLength.new
bits.needed_for(1_000_000) # 64

# java_send
bits.java_send :neededFor, [Java::int], 1_000_000 # 32

# java_alias
class BitLength
  java_alias :needed_for_int, :neededFor, [Java::int]
end

bits.needed_for_int 1_000_000 # 32
# jRuby can be forced to use a specific overload
require 'java'
java_import 'BitLength'

bits = BitLength.new
bits.needed_for(1_000_000) # 64

# java_send
bits.java_send :neededFor, [Java::int], 1_000_000 # 32

# java_alias
class BitLength
  java_alias :needed_for_int, :neededFor, [Java::int]
end

bits.needed_for_int 1_000_000 # 32

# java_method
needed = bits.java_method :neededFor, [Java::int]
needed.call 1_000_000 # 32
... and more about
        types
public class TypeChecker{
  public static String check(Object o){
    return o.getClass().getName();
  }
}




require 'java'

ruby_array = [1, 2, 'Mushrooms', :sample]
java_array = ruby_array.to_java

# org.jruby.RubyArray
Java::TypeChecker.check( ruby_array )

# [Ljava.lang.Object;
Java::TypeChecker.check( java_array )

# org.jruby.RubyArray
Java::TypeChecker.check( java_array.to_a )
Sooo, what about those
   C extensions... ?
Awesome, but why?
• Cross platform GUI dev
• Performance
• Ruby interface to legacy code
• Use a ruby test framework
  (Seriously, they’re amazing!)
• Utilize java libraries
• Sneak ruby into a java shop
• Threading
import org.jruby.embed.InvokeFailedException;
import org.jruby.embed.ScriptingContainer;

public class RubyFromJava {
  public static void main(String[] args) {
    ScriptingContainer container = new ScriptingContainer();

        container.runScriptlet("puts 'Java touched my Ruby'");
    }
}




                   $ java -cp jruby-complete.jar:. RubyFromJava
                   Java touched my Ruby
More Demos!!
http://pragprog.com/book/jruby/using-jruby
http://www.meetup.com/Code-Coffee-JHB/
http://www.meetup.com/RubyOnBeer/
Questions?

jRuby: The best of both worlds

  • 1.
    jRuby The best ofboth worlds Christopher Spring
  • 2.
  • 3.
  • 5.
  • 6.
  • 7.
    Yukihiro “Matz” Matsumoto “Iwanted a scripting language that was more powerful than Perl, and more object-oriented than Python...”
  • 8.
    “[We] need tofocus on humans, on how humans care about doing programming... We are the masters. They are the slaves.”
  • 10.
    “I hope tosee Ruby help every programmer in the world be productive, and to enjoy programming, and to be happy. This is the primary purpose of Ruby language”
  • 11.
    A primer
  • 12.
    # booleans truth =true lies = false # Strings dude = 'Matz' credo = "#{dude} is nice, so we are nice!" # Numbers 42 ** 13 53.8 / 9 19 % 3
  • 13.
    # Arrays backpack =[6, 'apple', [true, some_object]] # Hashes hash = { true => 'He loves me!', dude => 'Must obey!', backpack => false } # symbols cities = [:paris, :new_york, :johannesburg]
  • 14.
    Wtf is asymbol?
  • 15.
    coders = [ { 'language' => 'ruby', 'location' => 'johannesburg' }, { 'language' => 'java', 'location' => 'port elizabeth' }, # ... { 'language' => 'ruby', 'location' => 'pretoria' } ] # vs. coders = [ { :language => 'ruby', :location => 'johannesburg' }, { :language => 'java', :location => 'port elizabeth' }, # ... { :language => 'ruby', :location => 'pretoria' } ]
  • 16.
  • 17.
    public Class Person{ private String firstName, lastName; public Person(String firstName, String lastName){ setFirstName(firstName); setLastName(lastName); } public String getFirstName(){ return firstName; } public String getLastName(){ return lastName; } public String setFirstName(String firstName){ this.firstName = firstName; } public String setLastName(String lastName){ this.lastName = lastName; } }
  • 18.
    class Person attr_accessor :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end
  • 19.
    class Person attr_accessor :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end person = Person.new( 'Nick', 'Cave') person.first_name = 'Slick' person.last_name = 'Nave' puts "#{person.first_name} #{person.last_name}"
  • 20.
  • 21.
    true.class() # =>TrueClass false.to_s() # => "false" 5.9.floor() # => 5 3.times{ puts('Hello JUG!') } # => Hello JUG! # => Hello JUG! # => Hello JUG!
  • 22.
  • 23.
    module Earth class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name, @last_name = first_name, last_name end end end
  • 24.
    module Earth class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name, @last_name = first_name, last_name end end end chap = Earth::Person.new('Chandler', 'Bing') chap.first_name # => Chandler
  • 25.
    module RepublicPersonnel def storm_trooper Earth::Person.new('Storm', 'Trooper') end end class CloningVat extend RepublicPersonnel end
  • 26.
    module RepublicPersonnel def storm_trooper Earth::Person.new('Storm', 'Trooper') end end class CloningVat extend RepublicPersonnel end #<Earth::Person @last_name="Trooper", @first_name="Storm"> CloningVat.storm_trooper
  • 27.
    module TehForce def sense '... a disturbance in the force' end end module Earth class Person include TehForce end end
  • 28.
    module TehForce def sense '... a disturbance in the force' end end module Earth class Person include TehForce end end farm_boy = Earth::Person.new('Luke', 'Skywalker') # => '... a disturbance in the force' farm_boy.sense
  • 29.
    def some_days( something_happens) puts 'The sun rises' puts something_happens puts 'The sun sets' end def other_days( this_happens, and_that_happens) puts 'The sun rises' puts this_happens puts and_that_happens puts 'The sun sets' end some_days 'I do some work' other_days 'I skip', 'I read a book'
  • 30.
    def some_days( something_happens) puts 'The sun rises' puts something_happens puts 'The sun sets' end
  • 31.
    def some_days( something_happens) puts 'The sun rises' puts something_happens puts 'The sun sets' end
  • 32.
    def some_days( something_happens) puts 'The sun rises' puts something_happens puts 'The sun sets' end def some_days puts 'The sun rises' yield puts 'The sun sets' end some_days { puts 'I go to work' } some_days do puts 'I skip' puts 'I read a book' end
  • 33.
    file = File.open('i<3 ruby.txt', 'w') file.puts "... i really do!" file.close
  • 34.
    file = File.open('i<3 ruby.txt', 'w') file.puts "... i really do!" file.close File.open('i <3 ruby.txt', 'w') do |file| file.puts "... i really do!" end
  • 35.
    Rubyists TATFT! describe Calculatordo before(:each) do @calc = Calculator.new end context 'Addition' do it 'returns 10 when adding 5 and 5' @calc.add(5,5).should == 10 end it 'returns 0 when adding 5 and -5' do @calc.add(5,-5).should == 0 end end # ... end
  • 36.
  • 37.
    class Person def initialize @height, @weight, @hair_color = 6.2, '70kg', 'brown' end %w(height weight hair).each do |property| define_method("#{property}") do i_var = self.instance_eval("@#{property}") i_var.to_s.upcase end end def method_missing(*args, &block) puts "I dont have #{args[0].to_s.gsub(/_/, ' ')}" end end p = Person.new p.my_height # => 6.2 p.my_weight # => 70KG p.my_hair_color # => BROWN p.your_car_keys # => I dont have your car keys
  • 38.
    A primer
  • 41.
  • 42.
  • 43.
    Interactive Ruby $jirb jruby-1.6.7 :001 > require 'java' => true jruby-1.6.7 :001 > #...
  • 44.
  • 45.
    class Ackermann def self.ack(m, n) return n + 1 if m.zero? return ack(m - 1, 1) if n.zero? ack( m - 1, ack(m, n - 1) ) end end
  • 46.
    include Rubeus::Swing JFrame.new('Ackerizer') do|frame| frame.layout = java.awt.FlowLayout.new @m, @n = JTextField.new('3'),JTextField.new('10') JButton.new('->') do start = Time.now @result.text = Ackermann.ack( @m.text.to_i, @n.text.to_i ).to_s puts "#{Time.now - start}" end @result = JTextField.new 10 frame.pack frame.show end
  • 47.
    class Ackermann def self.ack(m, n) return n + 1 if m.zero? return ack(m - 1, 1) if n.zero? ack( m - 1, ack(m, n - 1) ) end end
  • 48.
    class Ackermann def self.ack(m, n) return n + 1 if m.zero? return ack(m - 1, 1) if n.zero? ack( m - 1, ack(m, n - 1) ) end end public class Ackermann { public static int ack(int m, int n) { if (m == 0) return n + 1; if (n == 0) return ack(m - 1, 1); return ack(m - 1, ack(m, n - 1)); } }
  • 49.
    include Rubeus::Swing JFrame.new('Ackerizer') do|frame| frame.layout = java.awt.FlowLayout.new @m, @n = JTextField.new('3'),JTextField.new('10') JButton.new('->') do start = Time.now @result.text = Java::Ackermann.ack( @m.text.to_i, @n.text.to_i ).to_s puts "#{Time.now - start}" end @result = JTextField.new 10 frame.pack frame.show end
  • 50.
    Method overloading public classOverloadTypeChecker { public static String inspect(long value) { return "long"; } public static String inspect(String value) { return "string"; } public static String inspect(Object value) { return "object"; } }
  • 51.
    require 'java' java_import 'OverloadTypeChecker'do 'OTC' end # jRuby happily handles the simple case OTC.inspect(5) # long OTC.inspect('Helloooo!') # string OTC.inspect([]) # object
  • 52.
    public class BitLength{ public int neededFor(int i) { return 32; } public int neededFor(long l) { return 64; } }
  • 53.
    # jRuby canbe forced to use a specific overload require 'java' java_import 'BitLength' bits = BitLength.new bits.needed_for(1_000_000) # 64
  • 54.
    # jRuby canbe forced to use a specific overload require 'java' java_import 'BitLength' bits = BitLength.new bits.needed_for(1_000_000) # 64 # java_send bits.java_send :neededFor, [Java::int], 1_000_000 # 32
  • 55.
    # jRuby canbe forced to use a specific overload require 'java' java_import 'BitLength' bits = BitLength.new bits.needed_for(1_000_000) # 64 # java_send bits.java_send :neededFor, [Java::int], 1_000_000 # 32 # java_alias class BitLength java_alias :needed_for_int, :neededFor, [Java::int] end bits.needed_for_int 1_000_000 # 32
  • 56.
    # jRuby canbe forced to use a specific overload require 'java' java_import 'BitLength' bits = BitLength.new bits.needed_for(1_000_000) # 64 # java_send bits.java_send :neededFor, [Java::int], 1_000_000 # 32 # java_alias class BitLength java_alias :needed_for_int, :neededFor, [Java::int] end bits.needed_for_int 1_000_000 # 32 # java_method needed = bits.java_method :neededFor, [Java::int] needed.call 1_000_000 # 32
  • 57.
    ... and moreabout types
  • 58.
    public class TypeChecker{ public static String check(Object o){ return o.getClass().getName(); } } require 'java' ruby_array = [1, 2, 'Mushrooms', :sample] java_array = ruby_array.to_java # org.jruby.RubyArray Java::TypeChecker.check( ruby_array ) # [Ljava.lang.Object; Java::TypeChecker.check( java_array ) # org.jruby.RubyArray Java::TypeChecker.check( java_array.to_a )
  • 59.
    Sooo, what aboutthose C extensions... ?
  • 60.
    Awesome, but why? •Cross platform GUI dev • Performance • Ruby interface to legacy code • Use a ruby test framework (Seriously, they’re amazing!) • Utilize java libraries • Sneak ruby into a java shop • Threading
  • 62.
    import org.jruby.embed.InvokeFailedException; import org.jruby.embed.ScriptingContainer; publicclass RubyFromJava { public static void main(String[] args) { ScriptingContainer container = new ScriptingContainer(); container.runScriptlet("puts 'Java touched my Ruby'"); } } $ java -cp jruby-complete.jar:. RubyFromJava Java touched my Ruby
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.

Editor's Notes