This document provides an overview of the Ruby programming language for Perl programmers. Ruby is a dynamic, reflective, object-oriented scripting language that is similar to Perl in many ways but also differs in its stronger object-oriented focus. The document discusses Ruby's syntax, core classes, object model, modules, blocks and iterators, and other features.
1. Ruby for Perl programmers for Perl programmers Ruby All material Copyright Hal E. Fulton, 2002. Use freely but acknowledge the source.
2. What is Ruby? A scripting language An object-oriented language A dynamic language A Very High Level Language (VHLL) A human-oriented language An open-source project
3. Ruby is General-Purpose One-liners at the command line “ Quick and dirty” scripts System administration tasks CGI scripts GUI apps Networking and distributed apps
4. A Brief History of Ruby… 1993 Conceived by Yukihiro Matsumoto (“Matz”) 1995 First release (Japan) 1999 Version 1.4 released 2000 Programming Ruby published; comp.lang.ruby created 2001 Version 1.6 released; Ruby in a Nutshell published 2001 The Ruby Way published 2002 The Ruby Developer’s Guide published; Teach Yourself Ruby in 21 Days published
5. Where does Ruby get its ideas? Perl (regular expressions, some syntax) SmallTalk (dynamic features, OOP) CLU (iterators) LISP (dynamic features) C (operators, printf/sprintf, etc.) Others
6. Ruby’s Design Principles Principle of Least Surprise (POLS), aka Law of Least Astonishment (LOLA) Human-oriented design Orthogonality, “naturalness,” consistency Flexibility and dynamism TMTOWTDI – “There’s More Than One Way To Do It” TABWTDI – “There’s A Better Way To Do It”
8. How is Ruby like Perl? Scripting language Interpreted, not compiled Minimalist philosophy Good at text processing Understands regular expressions Powerful and versatile Many features borrowed directly from Perl
9. How is Ruby different from Perl? Radically object-oriented from the start Extremely dynamic Built-in threads (non-native) True closures (?) Exception handling Significant syntax differences
10. Some Specific Similarities… Most operators and precedence Most regular expression features Many special variables: $! $& $+ $` $’ $0-$9 $/ $ $, $. $$ $? $_ Flexible quoting of strings Multiple assignment __END__, taint, and other features
11. Some Specific Differences… OOP by design: No bless , tie , tied , untie No scope declarations ( my , local ) Prefixes indicate scope, not type True/false testing is different Missing special variables: $| %SIG @_ $# $= $~ $% $: $; $[ No autovivification Better #{} support Better support for dynamic features No DESTROY, __PACKAGE__, __DATA__
12. Enough! Let’s see some code. The Obligatory Hello, world! Program: puts “Hello, world!” or print “Hello, world!”
13. Some Basic Syntax # This is a comment. str1 = “Some string.” str2 = ‘Some other string.’ # Multiple assignment: a, b, c = 3, 5, 7 # Interpolating values in strings: puts “The sum is #{a+b+c}”
14. Some More Syntax var = 123 # Local variable $var = 234 # Global variable PI = 3.14159 # Constant # Note that variables don’t have types! var = “xyz” # redefined # Note that constants really aren’t. PI = 3.1416 # Gives a warning
15. Loops for index in 1..9 do puts “Iteration #{index}” end 1.upto(9) {|index| puts index } index = 1 while index < 9 do puts index index += 1 end loop { puts “Infinite loop.” }
16. Conditions if x < 5 puts “yes” else puts “no” end unless x < 5 puts “no” else puts “yes” end if x < 5 then puts “yes” else puts “no” end unless x < 5 then puts “no” else puts “yes” end y = if x < 5 then 23 else 45 end y = unless x < 5 then 45 else 23 end y = x < 5 ? 23 : 45
17. Loop and condition modifier forms puts “Error!” if x < 5 puts “Error!” unless x >= 5 do_something while notFinished do_something until finished
18. Syntax Sugar and More for loop calls default iterator each x += y is shorthand for x = x + y Most operators are methods Aliases or synonyms are allowed Flexible quoting and array literals Method suffixes ( ? and ! )
19. OOP in Ruby Everything is an object – no wrappers as in Java Standalone functions are really methods of Object Code can be stored as objects Singletons are permitted Metaclasses Data hiding: public , private , protected Etc. …
20. A Simple Class class Person def initialize(name, number) @name, @phone = name, number end def inspect “ Name=#{@name} Phone=#{@phone}” end end # Note that “new” invokes “initialize” a1 = Person.new(“Bill Gates”, “1-800-666-0666”) a2 = Person.new(“Jenny”, “867-5309”) # p is like print or puts but invokes inspect p a2 # Prints “Name=Jenny Phone=867-5309”
21. Defining attributes # Adding to previous example… class Person attr_reader :name # Defines a “name” method attr_accessor :phone # Defines “phone” and # “phone=“ methods end person1 = a2.name # “Jenny” phone_num = a2.phone # “867-5309” a2.phone = “512 867-5309” # Value replaced… p a2 # Prints “Name=Jenny Phone=512 867-5309”
22. Class-level entities class Foobar @@count = 0 # Class variable def initialize(str) @data = str @@count += 1 end def Foobar.population # Class method @@count end end a = Foobar.new(“lions”) b = Foobar.new(“tigers”) c = Foobar.new(“bears”) puts Foobar.population # Prints 3
23. Inheritance class Student < Person def initialize(name, number, id, major) @name, @phone = name, number @id, @major = id, major end def inspect super + “ID=#@id Major=#@major” end end x = Student.new(“Mike Nicholas”, “555-1234”, “ 000-13-5031”, “physics”) puts “yes” if x.is_a? Student # yes puts “yes” if x.is_a? Person # yes
24. Singleton objects # Assume a “Celebrity” class newsguy = Celebrity.new(“Dan Rather”) popstar = Celebrity.new(“Britney Spears”) superman = Celebrity.new(“Superman”) class << superman def fly puts “Look, I’m flying! Woo-hoo!” end end superman.fly # Look, I’m flying! Woo-hoo! newsguy.fly # Error!
25. Garbage Collection No need for destructors No memory deallocation, etc. Currently “mark and sweep” technique Plans for generational GC
26. Using method_missing class OSwrapper def method_missing(method, *args) system(method.to_s, *args) end end sys = OSwrapper.new sys.date # Sat Apr 13 16:32:00… sys.du “-s”, “/tmp” # 166749 /tmp
27. Modules in Ruby Used as mixins (substitute for multiple inheritance); features are “mixed in” to an existing class Used for interface polymorphism; existing class defines method(s) that will be called by the module methods (and vice versa) Used for namespace management
28. Modules as mixins An example of a “pure” mixin is the special Kernel module Because Kernel is mixed into Object , its methods are universally available (without receivers) In most cases, module methods call methods of the class into which they are mixed (interface polymorphism)
29. Modules for Interface Polymorphism Example is Enumerable , which implements methods such as min , max , find , and select These methods depend on the existence of the default iterator each and a method called <=> (used for comparing).
30. Modules for Namespace Management An example is the Math module It has many useful mathematical functions, but these are basically independent of any class or object Therefore Math is really never mixed into a class A similar example is the Process module
31. Module example 1 class MyCollection include Enumerable # interface polymorphism #… def <=>(other) # Compare self to other somehow… # Return –1, 0, or 1 end def each # Iterate through the collection and do # a yield on each one… end end
32. Module example 2 # Namespace management def sin puts “Pride, gluttony, bad commenting…” end x = Math.sin(theta) # unrelated to “our” sin User = Process.uid # uid of this process
33. Module example 3 # A user-defined module module FlyingThing def fly puts “Look, I’m flying!” end end class Bat < Mammal include FlyingThing # A substitute for MI? #… end x = Bat.new x.is_a? Bat # true x.is_a? Mammal # true x.is_a? FlyingThing # true
34. Programming Paradigms Functional Programming (FP): This is possible in a limited way in Ruby (for Haskell fans, etc.) Aspect-Oriented Programming (AOP): A library (AspectR) exists Design-by-Contract (DBC) as in Eiffel: An add-on library exists Design Patterns: The Singleton , Observer , Delegator , and others are already implemented; new ones are relatively easy to implement Extreme Programming (XP): Ruby’s flexible and dynamic nature makes it a natural for XP practices
35. Cool Features in Ruby Iterators (as in CLU) A rich class set Open classes Exceptions Easy extension in Ruby Operator overloading Reflection or dynamic features Threads The Bignum class Continuations Easy extension in C
36. A Rich Set of Classes Array Exception File Hash IO Proc Range Regexp String Struct And others…
37. A Closer Look at Some Classes… Array instance methods: & * + - << <=> == === [ ] [ ]= | assoc at clear collect collect! compact compact! concat delete delete_at delete_if each each_index empty? eql? fill first flatten flatten! include? index indexes indices join last length map! nitems pack pop push rassoc reject! replace reverse reverse! reverse_each rindex shift size slice slice! sort sort! to_a to_ary to_s uniq uniq! unshift IO class and instance methods foreach new pipe popen readlines select << binmode clone close close_read close_write closed? each each_byte each_line eof eof? fcntl fileno flush getc gets ioctl isatty lineno lineno= pid pos pos= print printf putc puts read readchar readline readlines reopen rewind seek stat sync sync= sysread syswrite tell to_i to_io tty? ungetc write String instance methods % * + << <=> == === =~ [ ] [ ]= ~ capitalize capitalize! center chomp chomp! chop chop! concat count crypt delete delete! downcase downcase! dump each each_byte each_line empty? gsub gsub! hash hex include? index intern length ljust next next! oct replace reverse reverse! rindex rjust scan size slice slice! split squeeze squeeze! strip strip! sub sub! succ succ! sum swapcase swapcase! to_f to_i to_s to_str tr tr! tr_s tr_s! unpack upcase upcase! upto
38. Iterators An iterator is a method that can take a code block as a parameter; control is passed back and forth as though they were coroutines. The “standard” iterator is called each ; this can also be called implicitly through a for statement. Example: list = [1, 2, 3, 4, 5] list.each do {|x| puts x if x > 3 } # Equivalently… for x in list puts x if x > 3 end Many classes have other predefined iterators such as foreach , each_byte , reverse_each , and so on.
39. Iterators That Don’t Iterate Sometimes the term “iterator” is not truly accurate; it may serve to enclose a block of code and isolate its context.The canonical example: f = File.open(“somefile”) # Requires a close # But… File.open(“somefile”) do |f| # Process the file as needed; it will be # closed automatically at end of block end Other examples: mutex.synchronize do # Perform thread-sensitive operation, then # release the mutex end Dir.chdir(“/tmp”) do # Temporarily change current directory end
40. Defining Your Own Iterators class Array # Iterate only over an array’s even- # numbered indices… def every_other if block_given? self.each_with_index do |x,i| yield(x) if i % 2 == 0 end else raise “No block specified!” end end arr = [11,23,35,47,59,61,73] arr.every_other {|obj| puts obj } # Prints 11 35 59 73 arr2 = [] arr.every_other {|obj| arr2 << obj } # arr2 is now [11,35,59,73]
41. Interesting Example #1 In this code fragment source.each { |line| process(line) } What is source ? We can’t tell! It could be anything that has an iterator each and returns a line at a time. It could be a string with embedded newlines. It could be a file or other IO object. In a sense, we care less about its real “type” or “class” than we care about the methods it implements.
42. Interesting Example #2 The POP3 email library defines a POPMail class that has a method called all (to process the entire contents of the message). This method acts as an iterator if it is used with a block: msg.all {|line| puts line } Otherwise it uses the append operator ( << ) on whatever object was passed to it. Thus the object can be any object that responds to the << message: arr = [] str = “” out = $stdout msg.all(arr) # Produce an array of lines msg.all(str) # or a string with embedded newlines msg.all(out) # Write each line to stdout
43. Open Classes This means that predefined classes can be added to or modified at will. Example: class String def rot13 self.tr(“A-Ma-mN-Zn-z”,”N-Zn-zA-Ma-m”) end end text = “Elvis is dead.” secret = text.rot13 # Ryivf vf qrnq.
44. Exceptions Help obviate the need for return codes Help eliminate spaghetti “if” logic Example forms: raise raise “Any error message.” raise ArgumentError raise ArgumentError, “Bad data.” raise ArgumentError.new(“Bad data.”) raise ArgumentError, “Bad data”, caller[0]
45. Catching Exceptions, Part 1 begin x = Math.sqrt(y/z) # … rescue ArgumentError puts “Error taking square root.” rescue ZeroDivisionError puts “Tried to divide by zero.” end
46. Catching Exceptions, Part 2: The General Form begin # … rescue SomeExceptionType # Can attempt recovery with “retry” rescue SomeOtherType # Same thing, different exception… else # All other exception types ensure # Code here is ALWAYS executed end
47. Other Forms of rescue Inside a method definition: def mymethod # Code… rescue # Handle any exceptions… end Capturing in a variable begin #… rescue SomeType => exc puts exc.message end As a one-liner (modifier form) x = y/z rescue puts “Division by zero!”
48. Easy Extension (in Ruby) Often we can “play” with new features before adding them to the core language. Matz doesn’t have to change the interpreter. We don’t have to write a C extension.
49. Example: Smalltalk-like inject # A Smalltalk-like “inject” method for arrays class Array def inject(initial) result = initial self.each {|x| result = yield(x, result) } result end end nums = [1,2,3,4] sum = arr.inject(0) {|x,acc| acc+x } prod = arr.inject(1) {|x,acc| acc*x } words = [“red”, “green”, “blue”] list = words.inject(“Words:”) {|x,acc| acc+“ ”+x } # list is now: “Words: red green blue”
50. Example: Invert Array to Form Hash class Array def invert h={} self.each_with_index{|x,i| h[x]=i} h end end a = %w[red green blue] h = a.invert # {“blue”=>2, “green”=>1, “red”=>0}
51. Example: Sorting by an Arbitrary Key # Assume class Person with name, age, and height class Array def sort_by(sym) self.sort {|x,y| x.send(sym) <=> y.send(sym)} end end list = [Person.new("Hansel", 35, 69), Person.new("Gretel", 32, 64), Person.new("Ted", 36, 68), Person.new("Alice", 33, 63)] # Sorted lists… s1 = people.sort_by(:name) s2 = people.sort_by(:age) s3 = people.sort_by(:height) # s1 is [Alice 33 63, Gretel 32 64, Hansel 35 69, Ted 36 68] # s2 is [Gretel 32 64, Alice 33 63, Hansel 35 69, Ted 36 68] # s3 is [Alice 33 63, Gretel 32 64, Ted 36 68, Hansel 35 69]
52. Example: Existential Quantifiers module Quantifier def any? self.each { |x| return true if yield x } false end def all? self.each { |x| return false if not yield x } true end end class Array include Quantifier end list = [1, 2, 3, 4, 5] flag1 = list.any? {|x| x > 5 } # false flag2 = list.any? {|x| x >= 5 } # true flag3 = list.all? {|x| x <= 10 } # true flag4 = list.all? {|x| x % 2 == 0 } # false
53. Example: Selective File Deletion def delete_if(dir) Dir.chdir(dir) do Dir.foreach(“.”) do |entry| next if File.directory?(entry) # Skip dirs File.unlink(entry) if yield(entry) end end end # Delete files over 5K in size delete_if(“/tmp”) {|f| File.size(f) > 5000 } # Delete log and tmp files delete_if(“/tmp”) {|f| f =~ /(log|tmp)$/i } # Delete files over 24 hours old delete_if(“/home/bill”) {|f| (Time.now-File.mtime(f))>86400 }
54. Other Possible Examples (of Extending Ruby in Ruby) Autovivification of hashes and arrays One-based arrays Better multidimensional array syntax Hash-like constructs that allow duplicates Design-by-contract features AOP features … and much more
55. Dynamic Features of Ruby Dynamic code evaluation ( eval , instance_eval , class_eval , and module_eval ) Queries or reflection (finding a class’s methods and so on) Hooks (modifying “behind the scenes” behavior) Callbacks (finding out when something happens, e.g., when a variable is modified)
56. Operator Overloading Most operators can be redefined Example: # Assume a class Length with feet and inches class Length #… def +(other) f = self.feet + other.feet i = self.inches + other.inches if i > 12 then i -= 12; f += 1 end Length.new(f,i) end end board1 = Length.new(5,10) board2 = Length.new(8,9) total = board1 + board2 p total # 14’ 7”
57. Operator Overloading, ex. 2 class File def <<(args) self.print(*args) self # Return the file object! end end f = $stdout f << “The time is “ << Time.now << “ currently.”
58. The Bignum class A Fixnum will transparently “roll over” into a Bignum – an arbitrary-precision integer Example: a, b, c, d, e, f = 237, 365, 451, 666, 2001, 24601 product = a*b*c*d*e*f puts product # Output: 1279062690897238830 square = product**2 # Output: 1636001367245285523749542918059768900 cube = product**3 # Output: # 2092548311100316744450709557388954690847073917906387000
59. Threads in Ruby Ruby threads allow platform-independent multithreading of applications As such, they are non-native (not pthreads, for example) They do not take advantage of multiple processors They can be started, stopped, synchronized, and killed by means of a number of predefined methods For more sophisticated synchronization, there are add-on libraries available such as monitor.rb
60. Thread Example 1 # Thinking ahead during chess… responses = {} # move-response hash humans_turn = true thinking_ahead = Thread.new(board) do predictMove do |m| responses[m] = myResponse(board,m) Thread.exit if humans_turn == false end end human_move = getHumanMove(board) humans_turn = false # Stop the thread gracefully # Now we can access “responses” which may contain # the move the person just made...
61. Thread Example 2 # A simple threaded server… require "socket" PORT = 9999 server = TCPServer.new(PORT) while (session = server.accept) Thread.new(session) do |my_session| #… my_session.close end end
62. Continuations Continuations are similar to setjmp and longjmp in C; we can do a non-local jump to another context. Contrived example: def mymethod(cont) puts "Line 2" cont.call # "long jump" puts "Line 3" end callcc do |cc| # a Kernel method puts "Line 1" mymethod(cc) puts "Line 4" end puts “Line 5” # Here's the return point # Output: # Line 1 # Line 2 # Line 5
63. Extending Ruby in C Every Ruby object is accessed as a VALUE (either an immediate value or a pointer) The only header file needed is ruby.h Various rb_* functions correspond to Ruby operations ( rb_ary_push , rb_define_var , and so on) C datatype wrapping is accomplished with Data_Wrap_Struct , Data_Make_Struct , and Data_Make_Struct Rumor has it, it is much easier to extend Ruby than Perl
64. Ruby’s Weaknesses “ Now, the bad news…” Some external add-ons (libraries, tools, utilities) of the language are immature, incomplete, or missing Many things are still documented only in Japanese There are some “issues” with Windows platforms The Ruby Application Archive (RAA) is not nearly so comprehensive as the CPAN as yet User base is limited and expertise is rare Industry acceptance is limited as yet “ And now, back to our regularly scheduled program…”
65. Libraries and Utilities The “one true repository” is the Ruby Application Archive (RAA) accessible from www.ruby-lang.org ; this includes… HTTP, CGI, XML, and related libraries Network and distributed app libraries Development tools (editors, browsers, simple IDEs, syntax highlighting files, debuggers, etc.) Database apps and interfaces GUI, graphics, sound, multimedia in general MS Windows-related libraries Numerical and scientific libraries Documentation
66. Ruby and MS Windows The WIN32API library gives access to the entire Windows API (should you be so bold) The WIN32OLE library provides a Ruby interface for OLE automation ActiveScriptRuby allows Ruby to interface (for example) with the WSH RubyCOM is like a Ruby-to-COM bridge, allowing Ruby to reference VB objects and vice versa
67. Who’s Into Ruby… Dave Thomas and Andy Hunt (the “Pragmatic Programmers”); authors of The Pragmatic Programmer and Programming Ruby Ron Jeffries and Chet Hendrickson , XP gurus and co-authors of Extreme Programming Installed Dan Sugalski , developer for Parrot (the upcoming Perl/Python/Ruby runtime environment) … and a growing user community on comp.lang.ruby!
68. Web Resources www.ruby-lang.org The master site for all things Ruby-related, including the RAA www.rubycentral.com Dave and Andy’s site; very useful info and links www.rubygarden.org A Ruby wiki and a wealth of information www.rubyhacker.com My personal site, still under development
69. Print Resources Programming Ruby , Dave Thomas and Andy Hunt, Addison-Wesley, 2000. Ruby in a Nutshell , Yukihiro Matsumoto, O’Reilly, 2001. The Ruby Way , Sams Publishing, Hal Fulton, 2001. The Ruby Developer’s Guide , Syngress, Michael Neumann et al., 2002. Teach Yourself Ruby in 21 Days , Sams Publishing, Mark Slagell, 2002.
70. The Ruby Way Table of Contents 1. Ruby in Review 2. Simple Data Tasks 3. Manipulating Structured Data 4. External Data Manipulation 5. OOP and Dynamicity in Ruby 6. Graphical Interfaces for Ruby 7. Ruby Threads 8. Scripting and System Administration 9. Network and Web Programming A. From Perl to Ruby B. From Python to Ruby C. Tools and Utilities D. Resources on the Web (and Elsewhere) E. What’s New in Ruby 1.8 More than 300 sections More than 500 code fragments and full listings More than 10,000 lines of code All significant code fragments available in an archive