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
16. 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 end end cage = ZebraCage. new 10 puts cage.capacity
18. Multiline if if name. nil ? do_something end Notice the question mark
19. With an else if name. nil ? do_something else something_else end
20. Single line if if name. nil ? do_something end do_something if name. nil ?
21. Both kinds of unless if name. nil ? do_something end do_something if name. nil ? unless name. nil ? do_something end do_something unless name. nil ?
22. Dangerous methods name = " foo " name.strip name.strip! Returns a new string. Doesn’t modify name. Modifies name and returns that. Dangerous!
25. Same only ruby List<String> list = new ArrayList<String>(); list.add( "foo" ); list.add( "bar" ); list = Array.new list << 'foo' list << 'bar'
26. [] List<String> list = new ArrayList<String>(); list.add( "foo" ); list.add( "bar" ); list = Array.new list << 'foo' list << 'bar' list = [ 'foo' , 'bar' ]
27. %w() List<String> list = new ArrayList<String>(); list.add( "foo" ); list.add( "bar" ); list = Array.new list << 'foo' list << 'bar' list = [ 'foo' , 'bar' ] list = %w(foo bar)
28. 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.new list << 'foo' list << 'bar' list = [ 'foo' , 'bar' ] list = %w(foo bar)
29. 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' }
30. Special case for Hash hash = { :a => 5 , :b => 3 } do_stuff 30 , hash do_stuff 100 , :a => 5 , :b => 3
36. What if there isn’t a method for the specified message?
37. method_missing example from ActiveRecord user = Users.find_by_name(name) user = Users.find( :first , :conditions => [ "name = ?" , name])
38.
39. Implementing a proxy class Proxy def method_missing name, *args, &proc puts name,args end end
40. Implementing a proxy class Proxy def method_missing name, *args, &proc puts name,args end end Proxy. new .foo_bar ‘a’ Proxy. new .to_s Dispatches to method_missing Doesn’t go to method_missing
41. Overriding to_s class Proxy def method_missing name, *args, &proc puts name,args end def to_s method_missing :to_s, [] end end
43. Implementing a proxy class Proxy instance_methods.each do |method| undef_method method unless method =~ /^__/ end def method_missing name, *args, &proc puts name,args end end Proxy. new .to_s
44. Unix was not designed to stop people from doing stupid things, because that would also stop them from doing clever things. — Doug Gwyn
45.
46. Types public void foo( ArrayList list ) { list.add( "foo" ); } def foo list list << 'foo' end What’s the type? What’s the type?
47. Duck typing def 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
48.
49. How does this change how we think of types? think of types? think of types?
50. Overflow conditions int a = Integer. MAX_VALUE ; System. out .println( " a=" +a); System. out .println( "a+1=" +(a+1)); a=2147483647 a+1= ??
51. Overflow conditions int a = Integer. MAX_VALUE ; System. out .println( " a=" +a); System. out .println( "a+1=" +(a+1)); a=2147483647 a+1=-2147483648 oops
52. Overflow in ruby? number = 1000 1 .upto( 4 ) do puts "#{number.class} #{number}" number = number * number end Fixnum 1000 Fixnum 1000000 Bignum 1000000000000 Bignum 1000000000000000000000000
53.
54.
55. Closures multiplier = 5 block = lambda {|number| puts number * multiplier } A block An instance of Proc lambda() is a method to convert blocks into Procs
56. Closures multiplier = 5 block = lambda {|number| puts number * multiplier } Parameter to the block
57. Closures multiplier = 5 block = lambda {|number| puts number * multiplier } Able to access variables from outside the block
58. Proc’s multiplier = 5 block = lambda {|number| puts number * multiplier } block.call 2 block.arity prints 10 returns number of parameters that the block takes. 1 in this case
59. Blocks as parameters multiplier = 5 1.upto(3) {|number| puts number * multiplier } => 5 => 10 => 15 Same block as before Called once for each time through the loop
60. Alternate syntax multiplier = 5 1.upto(3) {|number| puts number * multiplier } 1.upto(3) do |number| puts number * multiplier end Equivalent
61.
62. // Read the lines and split them into columns List<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( "" ) ); } } finally { if ( reader != null ) { reader.close(); } } // then sort Collections.sort(lines, new Comparator<String[]>() { public int compare(String[] one, String[] two) { return one[1].compareTo(two[1]); } }); // then write them back out BufferedWriter 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( "" ); } builder.append(strings[i]); } } } finally { if ( writer != null ) { writer.close(); } } # Load the data lines = Array. new IO.foreach( 'people.txt' ) do |line| lines << line.split end # Sort and write it back out File.open( 'people.txt' , 'w' ) do |file| lines.sort {|a,b| a[ 1 ] <=> b[ 1 ]}. each do |array| puts array.join( "" ) end end
63. Closure File Example file = File. new (fileName, 'w' ) begin file.puts ‘some content’ rescue file.close end
64. Closure File Example file = File. new (fileName, 'w' ) begin file.puts ‘some content’ rescue file.close end Only one line of business logic
65. Closure File Example file = File. new (fileName, 'w' ) begin file.puts ‘some content’ rescue file.close end File.open(fileName, 'w' ) do |file| file.puts ‘some content’ end
66. Ruby file IO sample # Load the data lines = Array. new IO.foreach( 'people.txt' ) do |line| lines << line.split end # Sort and write it back out File.open( 'people.txt' , 'w' ) do |file| lines.sort {|a,b| a[ 1 ] <=> b[ 1 ]}. each do |array| puts array.join( "" ) end end
67. Closure-like things in Java final String name = getName(); new Thread( new Runnable() { public void run() { doSomething(name); } }).start(); Only one line of business logic
74. Enumerable class Foo include Enumerable def each &block block.call 1 block.call 2 block.call 3 end end module Enumerable def collect array = [] each do |a| array << yield (a) end array end end
75. Enumerable class Foo include Enumerable def each &block block.call 1 block.call 2 block.call 3 end end module Enumerable def collect array = [] each do |a| array << yield (a) end array end end
78. Reopening classes class Foo def one puts 'one' end end class Foo def two puts 'two' end end Reopening the same class
79. Reopening classes class Foo def one puts 'one' end end class Foo def one puts '1' end end Replacing, not adding a method
80. Reopening core classes class String def one puts 'one' end end We reopened a CORE class and modified it
81.
82.
83. attr_accessor class Foo attr_accessor :bar end class Foo def bar @bar end def bar=(newBar) @bar = newBar end end Getter Setter
84. Possible implementation of attr_accessor 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 :bar end
85. Possible implementation of attr_accessor 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 :bar end “ Here Doc” Evaluates to String
86. Possible implementation of attr_accessor 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 :bar end String substitution
87. Possible implementation of attr_accessor 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 :bar end Executes the string in the context of the class
88. Result class Foo def bar @bar end def bar=(newBar) @bar = newBar end end
89. ActiveRecord class ListItem < ActiveRecord::Base belongs_to :amazon_item acts_as_taggable acts_as_list :scope => :user end
90. Date :: 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 ; end end