Ruby For Java Programmers

104
Ruby for Java Programmers Mike Bowler President, Gargoyle Software Inc.

description

One of the advantages of learning a new language is being exposed to new idioms and new approaches to solving old problems. In this talk, we will introduce the Ruby language with particular focus on the idioms and concepts that are different from what is found in Java. We will introduce concepts such as closures, continuations and meta programming. We will also examine powerful techniques that are practically impossible in Java due to its compile time binding of types. No experience with Ruby is assumed although an understanding of Java would be helpful. This talk was given at the Toronto Java Users Group in April 2008

Transcript of Ruby For Java Programmers

Page 1: Ruby For Java Programmers

Ruby for Java ProgrammersRuby for Java Programmers

Mike BowlerPresident, Gargoyle Software Inc.

Mike BowlerPresident, Gargoyle Software Inc.

Page 2: Ruby For Java Programmers

Why learn another language?

Page 3: Ruby For Java Programmers

Why Ruby?

Page 4: Ruby For Java Programmers

Timeline: 1993 to 2000Timeline: 1993 to 2000

Created in 1993 by Yukihiro "Matz" Matsumoto

Ruby was popular in Japan but unknown everywhere else

All documentation was written in Japanese

Page 5: Ruby For Java Programmers

Timeline: 2000-2004Timeline: 2000-2004

First English language book published in 2000

A lot of interest in the Agile development community but mostly unknown elsewhere

Page 6: Ruby For Java Programmers

Timeline: 2004-todayTimeline: 2004-today

The Ruby on Rails framework has pushed ruby into the spotlight

This is the “killer app”

Page 7: Ruby For Java Programmers

What influenced it?What influenced it?

Page 8: Ruby For Java Programmers

TerminologyTerminology

Early

Late

Static

Dynamic

Strong

Weak

Page 9: Ruby For Java Programmers

Defining strong/weak typing

Defining strong/weak typing

Strong typing

Objects are of a specific type and will not be converted automatically

Java: “4”/2 results in a compile error

Weak typing

Objects can be converted under the covers at any time

Perl: ‘4’/2 => 2

Ruby is strongly typed

Page 10: Ruby For Java Programmers

Early/late bindingEarly/late bindingEarly binding (aka static binding)

All method invocations must be defined at compile time

Java: foo.getUser()

Late binding (aka dynamic binding)

The runtime does not check that a given method exists until an attempt to invoke it

Smalltalk: foo user.

Ruby uses late binding

Page 11: Ruby For Java Programmers

SimilaritiesSimilarities

Like Java, Ruby...

runs on a virtual machine

is garbage collected

is object oriented (although to different degrees)

Page 12: Ruby For Java Programmers

DifferencesDifferences

Everything is an object

Javastring = String.valueOf(1);

Rubystring = 1.to_s()

Primitive

Object

Page 13: Ruby For Java Programmers

DifferencesDifferences

Many things that you would expect to be keywords are actually methods

throw new IllegalArgumentException("oops");

raise TypeError.new("oops")

Keywords

Methods

Page 14: Ruby For Java Programmers

DifferencesDifferencesSome syntax optional unless the result is ambiguous

These statements are equivalent

puts("foo");

puts "foo"Idiomatic (better) style

Page 15: Ruby For Java Programmers

DifferencesDifferencesClasses are real objects

They’re instances of Class

Class methods can be overridden

Page 16: Ruby For Java Programmers

class ZebraCage < Cage attr_accessor :capacity @@allCages = Array.new

def initialize maximumZebraCount @capacity = maximumZebraCount @@allCages << self end

private def clean_cage # do some stuff here endend

cage = ZebraCage.new 10puts cage.capacity

Page 17: Ruby For Java Programmers

Multiline ifMultiline ifif name.nil? do_somethingend

Page 18: Ruby For Java Programmers

Multiline ifMultiline ifif name.nil? do_somethingend

Notice thequestion mark

Page 19: Ruby For Java Programmers

With an elseWith an elseif name.nil? do_somethingelse something_elseend

Page 20: Ruby For Java Programmers

Single line ifSingle line ifif name.nil? do_somethingend

do_something if name.nil?

Page 21: Ruby For Java Programmers

Both kinds of unlessBoth kinds of unlessif name.nil? do_somethingend

do_something if name.nil?

unless name.nil? do_somethingend

do_something unless name.nil?

Page 22: Ruby For Java Programmers

Dangerous methodsDangerous methods

name = " foo "

name.strip

name.strip!

Returns a new string.Doesn’t modify name.

Modifies name and returns that.

Dangerous!

Page 23: Ruby For Java Programmers

PhilosophyPhilosophyJava focuses on building blocks

You can build whatever you want with the pieces

Ruby focuses on solving problems

Things you do frequently should be concise

Page 24: Ruby For Java Programmers

Initializing arraysInitializing arraysList<String> list = new ArrayList<String>();list.add("foo");list.add("bar");

Page 25: Ruby For Java Programmers

Same only rubySame only rubyList<String> list = new ArrayList<String>();list.add("foo");list.add("bar");

list = Array.newlist << 'foo'list << 'bar'

Page 26: Ruby For Java Programmers

[][]List<String> list = new ArrayList<String>();list.add("foo");list.add("bar");

list = Array.newlist << 'foo'list << 'bar'

list = ['foo', 'bar']

Page 27: Ruby For Java Programmers

%w()%w()List<String> list = new ArrayList<String>();list.add("foo");list.add("bar");

list = Array.newlist << 'foo'list << 'bar'

list = ['foo', 'bar']

list = %w(foo bar)

Page 28: Ruby For Java Programmers

In fairness to java...In fairness to java...List<String> list = new ArrayList<String>();list.add("foo");list.add("bar");

List<String> list = Arrays.asList("foo", "bar");

list = Array.newlist << 'foo'list << 'bar'

list = ['foo', 'bar']

list = %w(foo bar)

Page 29: Ruby For Java Programmers

Same idea with hashes

Same idea with hashes

Map<String,String> map = new HashMap<String,String>();map.put("foo", "one");map.put("bar", "two");

map = {'foo' => 'one', 'bar' => 'two'}

Page 30: Ruby For Java Programmers

Special case for Hash

Special case for Hash

hash = {:a => 5, :b => 3}do_stuff 30, hash

do_stuff 100, :a => 5, :b => 3

Page 31: Ruby For Java Programmers

Regular ExpressionsRegular Expressions

Pattern pattern = Pattern.compile("^\\s*(.+)\\s*$");Matcher matcher = pattern.matcher(line);if( matcher.matches() ) { doSomething();}

Page 32: Ruby For Java Programmers

Regular ExpressionsRegular Expressions

Pattern pattern = Pattern.compile("^\\s*(.+)\\s*$");Matcher matcher = pattern.matcher(line);if( matcher.matches() ) { doSomething();}

do_something if line =~ /^\s*(.+)\s*$/

Page 33: Ruby For Java Programmers

Nil and NullNil and NullJava’s nullJava’s null Ruby’s nilRuby’s nil

Absence of an object An instance of NilClass

if( a != null ) {...} unless a.nil? {...}

null.toString() -> NPE nil.to_s -> “”

null.getUser() ->Exception in thread "main" java.lang.NullPointerException

nil.get_user -> NoMethodError: undefined method ‘get_user’ for nil:NilClass

Page 34: Ruby For Java Programmers

Implications of late binding

Implications of late binding

Method dispatch is quite different

Ruby makes a distinction between “messages” that are sent to an object and the “methods” that get dispatched

Page 35: Ruby For Java Programmers

Message != MethodMessage != Method

Page 36: Ruby For Java Programmers

What if there isn’t a method for the specified

message?

What if there isn’t a method for the specified

message?

Page 37: Ruby For Java Programmers

method_missing example from ActiveRecord

method_missing example from ActiveRecord

user = Users.find_by_name(name)

user = Users.find(:first, :conditions => [ "name = ?", name])

Page 38: Ruby For Java Programmers

Creating proxy objects

Creating proxy objects

Mock object for testing

Proxy object to allow distributed objects across machines

Wrapper to record usage of a given object

Page 39: Ruby For Java Programmers

Implementing a proxyImplementing a proxyclass Proxy def method_missing name, *args, &proc puts name,args endend

Page 40: Ruby For Java Programmers

Implementing a proxyImplementing a proxyclass Proxy def method_missing name, *args, &proc puts name,args endend

Proxy.new.foo_bar ‘a’Proxy.new.to_s

Dispatches to method_missing

Doesn’t go to method_missing

Page 41: Ruby For Java Programmers

Overriding to_sOverriding to_sclass Proxy def method_missing name, *args, &proc puts name,args end

def to_s method_missing :to_s, [] endend

Page 42: Ruby For Java Programmers

=•===•=~•__id__•_send__•class•clone•dclonedisplay•dup•enum_for•eql?•equal?•extend freeze

frozen?•hash•id•inspect•instance_eval instance_of?

instance_variable_defined•instance_variable_getinstance_variable_get•instance_variable_set

instance_variable_set•instance_variables•is_a?kind_of?•method•methods•new•nil?•object_id

private_methods•protected_methods•public_meth

odsremove_instance_variable•respond_to?•send

singleton_method_added•singleton_method_removed

singleton_method_undefined•singleton_methods•taint

tainted?•to_a•to_enum•to_s•to_yamlto_yaml_properties•to_yaml_style•type•untaint

Page 43: Ruby For Java Programmers

Implementing a proxyImplementing a proxy

class Proxy instance_methods.each do |method| undef_method method unless method =~ /^__/ end

def method_missing name, *args, &proc puts name,args endend

Proxy.new.to_s

Page 44: Ruby For Java Programmers

Unix was not designed to stop people from doing stupid things, because that would also stop them from doing clever things.

—Doug Gwyn

Page 45: Ruby For Java Programmers

Cultural differences about type

Cultural differences about type

Java is very focused on the types of objects

Is the object an instance of a specific class?

Or does it implement a specific interface?

Ruby is focused on the behaviour

Does the object respond to a given message?

Page 46: Ruby For Java Programmers

TypesTypes

public void foo( ArrayList list ) { list.add("foo");}

def foo list list << 'foo'end

What’s the type?

What’s the type?

Page 47: Ruby For Java Programmers

Duck typingDuck typingdef foo list list << 'foo'end

If list is a String=> ‘foo’

If list is an Array=> [‘foo’]

If list is an IO=> string will be written to stream

Page 48: Ruby For Java Programmers

Duck typingDuck typing

Duck typing implies that an object is interchangeable with any other object that implements the same interface, regardless of whether the objects have a related inheritance hierarchy. -- Wikipedia

"If it walks like a duck and quacks like a duck, it must be a duck." -- Pragmatic Dave Thomas

Page 49: Ruby For Java Programmers

How does this change how wethink of types?

How does this change how wethink of types?

Page 50: Ruby For Java Programmers

Overflow conditionsOverflow conditions

int a = Integer.MAX_VALUE;System.out.println(" a="+a);System.out.println("a+1="+(a+1));

a=2147483647a+1= ??

Page 51: Ruby For Java Programmers

Overflow conditionsOverflow conditions

int a = Integer.MAX_VALUE;System.out.println(" a="+a);System.out.println("a+1="+(a+1));

a=2147483647a+1=-2147483648

oops

Page 52: Ruby For Java Programmers

Overflow in ruby?Overflow in ruby?

number = 10001.upto(4) do puts "#{number.class} #{number}" number = number * numberend

Fixnum 1000Fixnum 1000000Bignum 1000000000000Bignum 1000000000000000000000000

Page 53: Ruby For Java Programmers

ClosuresClosures

A closure is a function that is evaluated in an environment containing one or more bound variables. When called, the function can access these variables. The explicit use of closures is associated with functional programming and with languages such as ML and Lisp. Constructs such as objects in other languages can also be modeled with closures. -- Wikipedia

Page 54: Ruby For Java Programmers

ClosuresClosures

A closure is a block of code that you can manipulate and query

In Ruby we call them blocks or Procs

A block is a pure closure

A Proc is a block wrapped as an object

We generally use the terms block and Proc interchangeably

Page 55: Ruby For Java Programmers

ClosuresClosures

multiplier = 5block = lambda {|number| puts number * multiplier }

A block

An instanceof Proc

lambda() is a method to convertblocks into Procs

Page 56: Ruby For Java Programmers

ClosuresClosures

multiplier = 5block = lambda {|number| puts number * multiplier }

Parameterto the block

Page 57: Ruby For Java Programmers

ClosuresClosures

multiplier = 5block = lambda {|number| puts number * multiplier }

Able to access variables from outside the block

Page 58: Ruby For Java Programmers

Proc’sProc’s

multiplier = 5block = lambda {|number| puts number * multiplier }block.call 2

block.arityprints 10

returns number of parametersthat the block takes. 1 in this case

Page 59: Ruby For Java Programmers

Blocks as parametersBlocks as

parametersmultiplier = 51.upto(3) {|number| puts number * multiplier }

=> 5=> 10=> 15

Same blockas before

Called once for each timethrough the loop

Page 60: Ruby For Java Programmers

Alternate syntaxAlternate syntax

multiplier = 51.upto(3) {|number| puts number * multiplier }

1.upto(3) do |number| puts number * multiplierend

Equivalent

Page 61: Ruby For Java Programmers

Why are closures significant?

Why are closures significant?

Presence of closures in a language completely changes the design of the libraries

Closure based libraries generally result in significantly less code

Page 62: Ruby For Java Programmers

// Read the lines and split them into columnsList<String[]> lines= new ArrayList<String[]>();BufferedReader reader = null;try { reader = new BufferedReader(new FileReader("people.txt")); String line = reader.readLine(); while( line != null ) { lines.add( line.split("\t") ); }}finally { if( reader != null ) { reader.close(); }}

// then sortCollections.sort(lines, new Comparator<String[]>() { public int compare(String[] one, String[] two) { return one[1].compareTo(two[1]); }});

// then write them back outBufferedWriter writer = null;try { writer = new BufferedWriter( new FileWriter("people.txt") ); for( String[] strings : lines ) { StringBuilder builder = new StringBuilder(); for( int i=0; i<strings.length; i++ ) { if( i != 0 ) { builder.append("\t"); } builder.append(strings[i]); } }}finally { if( writer != null ) { writer.close(); }}

# Load the datalines = Array.newIO.foreach('people.txt') do |line| lines << line.splitend

# Sort and write it back outFile.open('people.txt', 'w') do |file| lines.sort {|a,b| a[1] <=> b[1]}.each do |array| puts array.join("\t") endend

Page 63: Ruby For Java Programmers

Closure File ExampleClosure File Examplefile = File.new(fileName,'w')begin file.puts ‘some content’rescue file.closeend

Page 64: Ruby For Java Programmers

Closure File ExampleClosure File Examplefile = File.new(fileName,'w')begin file.puts ‘some content’rescue file.closeend Only one line of

business logic

Page 65: Ruby For Java Programmers

Closure File ExampleClosure File Examplefile = File.new(fileName,'w')begin file.puts ‘some content’rescue file.closeend

File.open(fileName,'w') do |file| file.puts ‘some content’end

Page 66: Ruby For Java Programmers

Ruby file IO sampleRuby file IO sample

# Load the datalines = Array.newIO.foreach('people.txt') do |line| lines << line.splitend

# Sort and write it back outFile.open('people.txt', 'w') do |file| lines.sort {|a,b| a[1] <=> b[1]}.each do |array| puts array.join("\t") endend

Page 67: Ruby For Java Programmers

Closure-like things in Java

Closure-like things in Java

final String name = getName();new Thread( new Runnable() { public void run() { doSomething(name); }}).start();

Only one line ofbusiness logic

Page 68: Ruby For Java Programmers

Closures for Java?Closures for Java?There are a couple of proposals being debated for Java7

Unclear whether any of them will be accepted

In the past, Sun’s position has been that closures didn’t make sense at this point in Java’s evolution

public static void main(String[] args) { int plus2(int x) { return x+2; } int(int) plus2b = plus2; System.out.println(plus2b(2)); }

Page 69: Ruby For Java Programmers

Inheriting behaviour from multiple placesInheriting behaviour from multiple places

C++ has multiple inheritance

Java has interfaces

Ruby has mixins

Page 70: Ruby For Java Programmers

C++ : multiple inheritance

C++ : multiple inheritance

Page 71: Ruby For Java Programmers

Java : inheritanceJava : inheritance

Page 72: Ruby For Java Programmers

Ruby : mixinsRuby : mixins

Page 73: Ruby For Java Programmers

MixinsMixins

Cannot be instantiatedCan be mixed in

Page 74: Ruby For Java Programmers

EnumerableEnumerable

class Foo include Enumerable

def each &block block.call 1 block.call 2 block.call 3 endend

module Enumerable def collect array = [] each do |a| array << yield(a) end array endend

Page 75: Ruby For Java Programmers

EnumerableEnumerable

class Foo include Enumerable

def each &block block.call 1 block.call 2 block.call 3 endend

module Enumerable def collect array = [] each do |a| array << yield(a) end array endend

Page 76: Ruby For Java Programmers

EnumerableEnumerableRequires that the class implement each()

For max, min and sort the <=> operator is also needed

Adds many methods for modifying, searching, sorting the items

all?, any?, collect, detect, each_cons, each_slice, each_with_index, entries, enum_cons, enum_slice, enum_with_index, find, find_all, grep, include?, inject, map, max, member?, min, partition, reject, select, sort, sort_by, to_a, to_set, zip

Page 77: Ruby For Java Programmers

Reopening classesReopening classesclass Foo def one puts 'one' endend

Page 78: Ruby For Java Programmers

Reopening classesReopening classesclass Foo def one puts 'one' endend

class Foo def two puts 'two' endend

Reopening the same class

Page 79: Ruby For Java Programmers

Reopening classesReopening classesclass Foo def one puts 'one' endend

class Foo def one puts '1' endend

Replacing, notadding a method

Page 80: Ruby For Java Programmers

Reopening core classesReopening core classesclass String def one puts 'one' endend

We reopened a CORE class

and modified it

Page 81: Ruby For Java Programmers

MetaprogrammingMetaprogramming

Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data. In many cases, this allows programmers to get more done in the same amount of time as they would take to write all the code manually.

-- Wikipedia

Page 82: Ruby For Java Programmers

What changes can we make at

runtime?

What changes can we make at

runtime?

Anything we can hand code, we can programmatically do

Because of late binding, EVERYTHING happens at runtime

Page 83: Ruby For Java Programmers

attr_accessorattr_accessor

class Foo attr_accessor :barend

class Foo def bar @bar end def bar=(newBar) @bar = newBar endend

Getter

Setter

Page 84: Ruby For Java Programmers

class Foo def self.attr_accessor name module_eval <<-DONE def #{name}() @#{name} end def #{name}=(newValue) @#{name} = newValue end DONE end my_attr_accessor :barend

Possible implementation of attr_accessor

Possible implementation of attr_accessor

Page 85: Ruby For Java Programmers

class Foo def self.attr_accessor name module_eval <<-DONE def #{name}() @#{name} end def #{name}=(newValue) @#{name} = newValue end DONE end my_attr_accessor :barend

Possible implementation of attr_accessor

Possible implementation of attr_accessor

“Here Doc”Evaluates to

String

Page 86: Ruby For Java Programmers

class Foo def self.attr_accessor name module_eval <<-DONE def #{name}() @#{name} end def #{name}=(newValue) @#{name} = newValue end DONE end my_attr_accessor :barend

Possible implementation of attr_accessor

Possible implementation of attr_accessor

Stringsubstitution

Page 87: Ruby For Java Programmers

class Foo def self.attr_accessor name module_eval <<-DONE def #{name}() @#{name} end def #{name}=(newValue) @#{name} = newValue end DONE end my_attr_accessor :barend

Possible implementation of attr_accessor

Possible implementation of attr_accessor

Executes the stringin the context of

the class

Page 88: Ruby For Java Programmers

ResultResult

class Foo def bar @bar end def bar=(newBar) @bar = newBar endend

Page 89: Ruby For Java Programmers

ActiveRecordActiveRecord

class ListItem < ActiveRecord::Base belongs_to :amazon_item acts_as_taggable acts_as_list :scope => :userend

Page 90: Ruby For Java Programmers

Date :: onceDate :: once

def once(*ids) # :nodoc: for id in ids module_eval <<-"end;", __FILE__, __LINE__ alias_method :__#{id.to_i}__, :#{id.to_s} private :__#{id.to_i}__ def #{id.to_s}(*args, &block) if defined? @__#{id.to_i}__ @__#{id.to_i}__ elsif ! self.frozen? @__#{id.to_i}__ ||= __#{id.to_i}__(*args, &block) else __#{id.to_i}__(*args, &block) end end end; endend

Page 91: Ruby For Java Programmers

ObjectSpaceObjectSpaceObjectSpace.each_object do |o| puts o end

ObjectSpace.each_object(String) do |o| puts o end

Page 92: Ruby For Java Programmers

ObjectSpaceObjectSpaceObjectSpace.each_object do |o| puts o end

ObjectSpace.each_object(String) do |o| puts o end

All objects

Only Strings

Page 93: Ruby For Java Programmers

ContinuationsContinuations

A snapshot of the call stack that the application can revert to at some point in the future

Page 94: Ruby For Java Programmers

Why continuations?Why continuations?

To save the state of the application across reboots of the VM

To save the state of the application across requests to a web server

Seaside (smalltalk) does this today

Page 95: Ruby For Java Programmers

DownsidesDownsidesOnly supported in one implementation of Ruby

Will be removed from the language in Ruby 2.0

Page 96: Ruby For Java Programmers

ImplementationsImplementationsRuby 1.8.x - “reference implementation” in C

Ruby 1.9 - Next version of C interpreter

Rubinius - Ruby in Ruby (sort-of)

Cardinal - Ruby on Parrot

Iron Ruby - Ruby on the DLR (Microsoft)

Ruby.NET - Ruby on the CLR

JRuby - Ruby on the JVM (Sun)

Page 97: Ruby For Java Programmers

ImplementationsImplementationsRuby 1.8.x - “reference implementation” in C

Ruby 1.9 - Next version of C interpreter

Rubinius - Ruby in Ruby (sort-of)

Cardinal - Ruby on Parrot

Iron Ruby - Ruby on the DLR (Microsoft)

Ruby.NET - Ruby on the CLR

JRuby - Ruby on the JVM (Sun)

Page 98: Ruby For Java Programmers

JRubyJRubyRuns on the Java Virtual Machine (JVM)

Full implementation of the Ruby language

Supported by Sun

Runs many benchmarks faster than the 1.8 reference implementation (written in C)

Able to easily call out to Java code

Page 99: Ruby For Java Programmers

Ruby on RailsRuby on Rails

Web application framework

Sweet spot - web application talking to a single relational database

Allows very rapid development of web apps

Page 100: Ruby For Java Programmers

Who’s using rails?Who’s using rails?

Amazon • BBC • Cap Gemini Chicago Tribune • Barclays • BPN • Cisco

CNET Electronic Arts • IBM • John Deere JP Morgan Chase • LA Times • Limewire Linked In • NASA • NBC • New York Times

Oakley • Oracle • Orbitz • Turner Media twitter.com • Siemens • ThoughtWorks

Yahoo!

Page 101: Ruby For Java Programmers

JRuby on Rails?JRuby on Rails?Yes! You can run a rails application on JRuby in a servlet container

Goldspike is the servlet that dispatches to rails

Tested on WebLogic, WebSphere, GlassFish, Jetty, Tomcat

Warbler is the packaging tool that makes the WAR

Supported on: WebLogic, WebSphere, GlassFish, Jetty, Tomcat

Page 102: Ruby For Java Programmers

RecapRecap

Learning a new language will make you better with all the languages you know

Ruby has a much more concise syntax which means that it takes much less code to solve the same problems

Ruby is able to run on the JVM which makes it an option for shops with heavy investments in J2EE infrastructure

Page 103: Ruby For Java Programmers

RecapRecapEverything is an object

The language is extremely malleable

New classes/methods can be created on the fly

Existing classes can be modified at any time

Page 104: Ruby For Java Programmers

Contacting meContacting me

Mike [email protected] (company)www.SphericalImprovement.com (blog)

Interested in learning more about how Ruby and Java can coexist in your company? Just ask me.