The Road to Ruby
an Developer eBook
contents
[ The Road to Ruby ]
2
While Java helps in fulfilling the promise of “write once, use anywhere”, there are practical concerns developers need to address in developing their code, whether its porting from another language, working in the best IDE or optimizing for today’s multi-core computing environments. This eBook will help you in understanding these issues and how you can get the most out of your Java code.
2 A Java Developer’s Guide to Ruby
Mark Watson
12 Ruby for C# Geeks Dave Dolan
21 The Road to Ruby from C++ By Michael Voss
31 Five Essentials For Your Ruby Toolbox
Peter Cooper
34 10 Minutes to Your First Ruby Application
James Britt
12
21
31
34
The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
1
[ The Road to Ruby ]
A Java Developer’s Guide to Ruby
By Mark Watson
As a Java developer, why should you learn Ruby? Because Ruby’s versatility and flexibility complement Java well, and you will be a more effective and effi- cient developer if you use both languages. In fact, I use Java, Ruby, and Common Lisp
for all my development and Ruby has become a core part of my work life. Specifically, the following reasons make Ruby compelling for Java develop- ers:
• As a scripting language, Ruby is an effective tool for small projects. When I need to write utilities for data conversion and text processing quickly, I almost always use Ruby.
• Ruby is a dynamic and
terse language.
• Using Ruby will often offer a different perspective on problem solving.
• JRuby is still a work-in-progress but I believe it eventually will provide an excellent Ruby deployment
platform using the Java VM. Currently, IntelliJ, NetBeans, and Eclipse all provide excellent Ruby support.
• As the cost of software maintenance is roughly pro-
Jupiterimages
portional to the number of lines of code, and Ruby pro- grams are short and concise, they tend to be easier to read, understand, and main- tain.
• The Ruby on Rails Web development framework is great for small and medium- sized database-backed web applications. You need to know Ruby if you want to use Ruby on Rails.
To demonstrate why Ruby is
a good fit for Java develop- ers, this article introduces the language features that
will make you more efficient (see Table 1. Ruby and Java Feature Comparison) and then shows short pro- gram examples in both languages.
“
Because Ruby’s versatility and flexibility complement Java well, and you will be a more effective and efficient developer if you use both languages.
2
”The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ] Table 1. Ruby and Java Feature Comparison
Language Features
Ruby
Java
Extending All Classes
Yes
Non Final Classes Only
Duck Typing
Yes
No
Code Blocks
Yes
No
Regular Expressions
Native
Standard Library Support
Supports Using External Programs
Yes
Yes, but not as easily as Ruby
Network Programming
Standard Library Support
Standard Library Support
Typing Dynamic
Static
Class Inheritance
Support mix-ins from multiple classes
Single
String Handling
Yes
Yes
What You Need
To follow along with the rest of the article, you need to install external Ruby libraries. The RubyGems library system makes this easy. Download it from RubyForge and follow the installation instructions for your operating system. (If you already have Ruby set up, you can verify that your setup includes RubyGems—many Ruby install packages do—by typing gem in a command shell to check for installation.) Having a central repository for libraries and a standard tool like RubyGems will save you a lot of time: no searching for the libraries you need, installing them, and using them in multiple projects.
Use the following commands to install the required gems:
gem query –remote # if you want to see all available remotely installable gems sudo gem install activerecord
sudo gem install mysql # if you want to use MySQL
sudo gem install postgres-pr # optional: install “pure ruby” PostgreSQL interface sudo gem install postgres # optional: install native PostgreSQL interface
sudo gem install ferret # a search library like Lucene (same API)
sudo gem install stemmer # a word stemming library for demonstrating extending a class
gem query # to show gems locally installed
gem specification activerecord # info on gem (ActiveRecord in this example)
Under Mac OS X and Linux, you will need to run the gem installs using sudo; if you are a Windows user, remove “sudo” from the previous commands.
This article also assumes that you will open a Ruby irb shell as follows and keep it open while you’re reading:
markw$ irb
>> s = “a b c” => “a b c”
>>
The example programs and code snippets are short enough to copy and paste into an irb interactive session.
3 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ] Ruby String Handling
The Ruby String class provides a large set of string-processing methods that are more flexible than Java’s string handling capabilities. This section shows a useful subset of Ruby’s string processing. This code snippet shows how to combine strings, take them apart with slices, and then search for substrings (the examples to follow use the # character to make the rest of a line a program comment):
require ‘pp’ # use the “pretty print” library. Defines the function ‘pp’ # define some strings to use in our examples:
s1 = “The dog chased the cat down the street” s2 = “quickly”
puts s1
puts s1[0..6] # a substring slice up to and including character at index==6 puts s1[0…6] # a substring slice up to (but not including) the character at index==6
puts “He is a #{s2} dog #{1 + 6} days a week.” # expressions inside #{} are inserted into a double quote string
puts ” test “.strip # create a copy of the string: the new copy has white space removed
puts s1 + ‘ ‘ + s2 # string literals can also be formed with single quotes puts s2 * 4
puts s1.index(“chased”) # find index (zero based) of a substring
s1[4..6] = ‘giant lizard’ # replace a substring (/dog/ -> /giant lizard/) puts s1
s2 = s2 << " now" # the << operator, which also works for arrays and other col- lections, copies to then end
puts s2
puts "All String class methods:"
pp s1.methods # the method "methods" returns all methods for any object
The output would be:
The dog chased the cat down the street The dog
The do
He is a quickly dog 7 days a week. test
The dog chased the cat down the street quickly quicklyquicklyquicklyquickly
8The giant lizard chased the cat down the street quickly now
All String class methods:
4 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
The Road to Ruby ]
# most methods not shown for brevity--try this in irb
The << operator in the above example is really a method call. When evaluating expressions, Ruby translates infix operators into method calls. For example, the << operator in the following code adds the value of the expression on its right side to the value on the left side:
>> 1 + 2 => 3
>> 1.+(2) => 3
[
[“send”,
“%”,
“index”, “collect”,
“[]=”,
“inspect”, ……]
>> “123” << "456" => “123456”
>> “123”.<<("456") => “123456”
In the above example, using the form “.<<" is a standard method call.
Many classes use the << operator to add objects to a class-specific collection. For example, you will later see how the Ferret search library (a Ruby gem you have installed) defines the << operator to add documents to an index.
Modifying an Existing Class
The key to Ruby's versatility is the ability to extend all its classes by adding methods and data. I frequently extend core Ruby classes in my application, not in the original class source code. This likely seems strange to Java or even C++ developers, but this technique lets you keep resources for a project in one place and enables many developers to add application-specific functionality without "bloating" the original class. As a Java programmer, think how the limitations of Java constrain you: if you want to add functionality and data to an existing class, you must subclass.
The following listing shows how to add the method stem to the String class:
begin
puts "The trips will be longer in the future".downcase.stem # stem is undefined
at this point
rescue
end puts 'Error:' + $!
require "rubygems" require_gem 'stemmer'
class String # you will extend the String class
end include Stemmable # add methods and data defined in module Stemmable
puts "The trips will be longer in the future".downcase.stem
You will also find it useful to add methods and perhaps new class instance variables to existing classes in your application.
5 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
The next section looks at "duck typing," another example of the extreme flexibility that Ruby offers.
Ruby Duck Typing
In Java, you can call a method only on an object that is defined (with public, package, etc. visibility) in the object's class hierarchy. Suppose that you have a collection of objects and you want to iterate over each element in the col- lection, calling one or more methods. In Java, the objects would need to be part of the same class hierarchy or implement interfaces defining the methods that you want to call.
As you have probably already guessed, Ruby is much more flexible. Specific data types and classes are not required in Ruby's runtime method-calling scheme. Suppose you call method foo on an object obj, and then call method bar on the resulting object of this first method call as follows (the example shows two equivalent calls; when there are no method arguments, you can leave off the ()):
obj.foo.bar
obj.foo().bar()
The result of calling obj.foo will be some object, and whatever the class of this new object is, you would attempt to call method bar on it.
As another example, suppose you want to call the method name on each object in a collection. One element in this collection happens to be of an instance of class MyClass2 that does not have a method name defined. You will get a runtime error when you first try applying method name to this object. You can fix this by dynamically adding the method as follows:
class MyClass2 def name
end end"MyClass2: #{this}"
Developers who are used to a strongly type checked language like Java likely will expect this "unsafe" flexibility to make their programs less reliable because the compiler or interpreter is not statically checking all type uses. However, any program bugs due to runtime type checking will be found quickly in testing, so there is no decrease in software reliability. Yet you get the benefits of a more flexible language: shorter programs and shorter develop- ment time.
Dealing with Missing Methods
Still skeptical about duck typing? Hang on, because now you are going to see another Ruby trick: how to handle missing methods for any Ruby class, starting with this simple example that applies two methods to a string object, one that is defined (length) and one that is undefined (foobar):
markw$ irb
>> s = “this is a string”
=> “this is a string”
>> s.length
=> 16
>> s.foobar
NoMethodError: undefined method `foobar’ for “this is a string”:String
from (irb):3
6
The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
You’ll see an error thrown for the undefined method. So “patch” the String class by writing your own method_missing method:
>>
>>
>>
>>
>>
=>
>> s.foobar Missing foobar () => nil
class String
def method_missing(method_name, *arguments)
end nil
puts “Missing #{method_name} (#{arguments.join(‘, ‘)})” end
>> s.foobar(1, “cat”) Missing foobar (1, cat) => nil
>>
Whenever the Ruby runtime system cannot find a method for an object, it calls the method method_missing that is initially inherited and simply raises a NoMethodError exception. This example overrode this inherited method with one that does not throw an error, and it prints out the name and arguments of the method call. Now, redefine this method again, this time checking to see if the method name (after converting it to a string with to_s) is equal to foobar:
>>
>>
>>
>>
>>
?>
>>
>>
>>
=>
>>
=>
>> s.foobar_it(1, “cat”)
NoMethodError: You need to define foobar_it
>>
If the method name is equal to foobar, this example calculates a return value. Otherwise, it throws an error.
Ruby Code Blocks
Ruby uses code blocks as an additional way to iterate over data. These blocks offer more flexibility and power than the limited iteration functionality built into the Java language. The previous example showing basic string function- ality used the stemmer gem to find the word stems of a string containing English words. The following example uses the String split method to tokenize a string using the space character as a word delimiter and then passes a code block defined using the { and } characters to mark the beginning and end of a code block (you also can use begin and end). Local variables in a block are listed between two | characters:
class String
def method_missing(method_name, *arguments)
if method_name.to_s==’foobar’ arguments.to_s.reverse # return a value
else
raise NoMethodError, “You need to define #{method_name}”
end end
end
nil
s.foobar(1, “cat”) “tac1”
from (irb):38:in `method_missing’ from (irb):43
from :0
7 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
puts “longs trips study studying banking”.split(‘ ‘)
puts “longs trips study studying banking”.split(‘ ‘).each {|token| puts “#{token} : #{token.stem}”
This code snippet produces the following:
longs
trips
study
studying
banking
longs : long trips : trip study : studi studying : studi banking : bank
You can see another good use of code blocks in the following example, which uses the Array collect method. The collect method processes each array element and then passes it to a code block:
require ‘pp’
pp [“the”, “cat”, “ran”, “away”].collect {|x| x.upcase}
pp [“the”, “cat”, “ran”, “away”].collect {|x| x.upcase}.join(‘ ‘)
In this example, the code block assumes that the elements are strings and calls the upcase method on each ele- ment. The collect method returns the collected results in a new array. It also uses the method join to combine all the resulting array elements into a string, separating the elements with the space character. This is the output:
[“THE”, “CAT”, “RAN”, “AWAY”] “THE CAT RAN AWAY”
Writing Methods That Use Code Blocks
You can use the yield method to call a code block passed to a method or function call. The following example uses the method block_given? to call yield conditionally if a code block is supplied. The method yield returns a value that is printed:
def cb_test name
puts “Code block test: argument: #{name}”
s = yield(name) if block_given?
puts “After executing an optional code block, =#{s}”
This example calls function cb_test, first without a code block and then with one:
>> puts cb_test(“Mark”)
Code block test: argument: Mark
After executing an optional code block, = nil
=> nil
>> puts cb_test(“Mark”) {|x| x + x}
end
8 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
Code block test: argument: Mark
After executing an optional code block, =MarkMark nil
=> nil
>>
The string value Mark is passed as an argument to yield, and inside the code block the local variable x is assigned the value Mark. The return value from the code block is MarkMark.
Ruby Regular Expressions
Ruby has built-in support for handling regular expressions using the class Regexp. Java’s java.util.regex APIs offer similar functionality but regular expression support in Ruby definitely has a more native feel to it. You can create a regular expression object by either directly using a method call like Regexp.new(“[a-e]og”) or enclosing a regular expression between slash characters like /[a-e]og/. You can find good tutorials on both regular expressions and on Ruby’s regular expression support on the web; this simple example shows only using the =~ operator:
=> 4
>> “the dog ran” =~ /[a-e]og/ => 4
>> “the zebra ran” =~ /[a-e]og/ => nil
Ruby Network Programming
Ruby has a great standard library for network programming as well. I frequently use Ruby for collecting data from the Internet, parsing it, and then storing it in XML or a database.
Ruby Document Indexing and Search Using the Ferret Library
By now, you have installed the Ruby gem called ferret. Ferret is the fastest indexing and search library based on Java Lucene (even faster than the Common Lisp version, Montezuma). One interesting fact about the Ferret library is that during development the author David Balmain eventually wrote most of it in C with a Ruby wrapper. The lesson is that if you start to use Ruby and have performance problems, you can always recode the time-critical parts in C or C++. Ferret defines a few classes that you will use in your own applications once you adopt Ruby:
• Document represents anything that you want to search for: a local file, a web URL, or (as you will see in the next section) text data in a relational database.
• Field represents data elements stored in a document. Fields can be indexed or non-indexed. Typically, I use a single indexed (and thereby searchable) text field and then several “meta data” fields that are not indexed. Original file paths, web URLs, etc. can be stored in non-indexed fields.
• Index represents the disk files that store an index.
• Query provides APIs for search.
Indexing and Searching Microsoft Word Documents
The following is the Ruby class I use for reading Microsoft Word documents and extracting the plain text, which is an example of using external programs in Ruby:
class ReadWordDoc attr_reader :text
def initialize file_path
9
The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
@text = `antiword #{file_path}` # back quotes to run external program end
The “trick” here is that I use the open source antiword utility to actually process Word document files. You can run any external program and capture its output to a string by wrapping the external command in back quotes. Try the following under Linux or OS X (for Windows try `dir`):
puts `ls -l`
This example prints the result of executing the external ls (Unix list directory) command.
The following Ruby script enters a Word document into an index (plain text files are easier—try that as an exer- cise):
require ‘rubygems’
require ‘ferret’
include Ferret
include Ferret::Document
require ‘read_word_doc’ # read_word_doc.rb defines class ReadWordDoc
index = Index::Index.new(:path => ‘./my_index_dir’) # any path to a directory
doc_path = ‘test.doc’ # path to a Microsoft Word
doc_text = ReadWord.new(doc_path).text # get the plain text from the Word file
doc = Document.new
doc << Field.new("doc_path", doc_path, Field::Store::YES, Field::Index::NO) doc << Field.new("text", doc_text, Field::Store::YES, Field::Index::TOKENIZED) index << doc
end
index.search_each('text:"Ruby"') do |doc, score| # a test search
puts "result: #{index[doc]['doc_path']} : #{score}" # print doc_path meta
data
puts "Original text: #{index[doc]['text']}" # print original text
end
index.close # close the index when you are done with it
Notice how short this example is. In 24 lines (including the class to use antiword for extracting text from Word doc- uments), you have seen an example that extracts text from Word, creates an index, performs a search, and then closes the index when you are done with it. Using Ruby enabled you to get complex tasks done with very few lines of code. Had you coded this example in Java using the very good Lucene library (which I've done!), the Java pro- gram would be much longer. Shorter programs are also easier and less expensive to maintain.
This example uses Word documents, but OpenOffice.org documents are simple enough to be read. With about 30 lines of pure Ruby code, you can unzip a document and extract the text from the content.xml element in the unzipped XML data stream. (XML processing is simple in Ruby, but it is beyond the scope of this article.)
10 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ] Ruby Complements Java
The cost of software development and maintenance is usually the largest expense for a company's IT budget— much more expensive than servers, Internet connectivity, etc. The use of Ruby can greatly reduce the cost of build- ing and maintaining systems, mostly because programs tend to be a lot shorter (For me, the time spent per line of code is similar for most programming languages I use).
OK, so when should you use Java? I have used the Java platform for building systems for my consulting customers for over 10 years, and I certainly will continue using Java. A well-built, Java-based web application will run forev- er—or at least until servers fail or have to be rebooted for hardware maintenance. My confidence comes from see- ing systems run unattended for months on end with no problems. My advice is to continue using Java on the serv- er side for large systems and to start using Ruby for small utility programs. For my work, I view Java and Ruby as complementary, and not as competitors. Use the best tool for each task.
11 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
Ruby for C# Geeks
By Dave Dolan
Attention C# developers: C# is good for a great many things, but it's not the best language for everything. In fact, there is no such thing as "the best language." It all depends on what you're trying to do, and your personal preference and familiarity with the languages you have avail- able. Although the Church-Turing Thesis suggests that any- thing you can do in one complete language, you can also do in another, the truth is not all languages are created equal. You can accomplish
simple tasks with complex code in some languages and complex tasks with simple code in others. C# sometimes falls into both categories, but more often than not, it requires a bit more than its fair share of effort. Strangely enough, a lan- guage that allows you to accomplish simple tasks with simple code is rare. Enter Ruby, an interpreted, dynami- cally typed language that enables just that.
Many would say that the main difference between Ruby and C# is that Ruby is a dynamic language whereas C# isn't. However, referring to C# as a static language really isn't right because you wouldn't apply that term to an entire language as you would to one of the dynamic variety. Ruby really differs from C# in that its code is not actually compiled into an interme- diate executable form before it is run. Instead, Ruby has at its heart a text-driven interpreter. This means
that the expressions and statements in a Ruby pro- gram are evaluated as the interpreter passes over them. In C#, you must first compile the code to an .exe or .dll file to be able to run it.
In requiring compilation, C# encapsulates an opportu- nity to check syntax and optimize the runtime efficiency of the code before it's ever run. On the other hand, all
Jupiterimages
of the declaration and speci- fication that goes into setting up your code with all of the necessary information to allow the compiler to per- form these tricks will slow you down when these fea- tures aren't necessary or desired. You might use a lan- guage like Ruby, with looser guidelines, to test your algo- rithmic theories or rapidly prototype an application. Sometimes you just need to format a couple of text files, and C# isn't exactly friendly
in cases where you just want to automate something as simple as a command line.
This article offers a brief introduction to the Ruby lan- guage from the perspective of a C# developer. You'll learn the differences between the languages' features through line-by-line examinations of identical programs built in each one.
12 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ] Same Results, Simpler Code
Every programming language tutorial I've ever read has what is (often sardonically) known as the "obligatory Hello World! example." I'm not very fond of that terminology, so I've spruced up the sample application for this article to the "slightly interesting Hello Foo! example." The snippets to follow show two programs (one in C#, the other in Ruby) that produce exactly the same output for my Hello Foo! example.
First, here's the C# version:
// Hello Foo! in C#
using System ; namespace TestCSharp
{
{
class MainClass
public static void Main(string[] args)
Console.WriteLine("Hello Foo!"); //say hello }
{
As you can see, C# requires you to specify a lot of structure and actually tell the compiler that you're going to write a class. Specifically, here you tell it you're going to write a class with a well-known method called Main that a console application will use as the first method to call (known as an entry point) when the program executes. The required verbosity is somewhat mitigated by Visual Studio or any other syntax-helping editor that allows for tem- plates and IntelliSense; however, the level of code complexity is still there regardless of whether or not you have to actually type every letter.
First, compile the C# program (If you're using Visual Studio or MonoDevelop, you can simply press F5.), and then you can run it:
# Hello Foo! in Ruby
puts "Hello Foo!" # say hello
Running this little snippet requires that you simply invoke the interpreter and then provide the name of the script file (hellofoo.rb):
C:\>ruby hellofoo.rb Hello Foo!
C:\>
Since the Ruby interpreter assumes all of the structural information is there, you don’t have to write it like you would in C#. The interpreter assumes that the first bunch of code you write without an enclosing class or module declaration is analogous to it appearing within the Main method of the entry point class. Very handy.
} }
13 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
Note the differences in syntax between the languages as well:
• Semi-colons aren’t used to delimit the end of a statement in Ruby.
• Ruby’s built-in puts method is actually like C#’s Console.Writeline in that it will display the result of the .to_s method of the parameter object, which in C# you would write as .ToString().
• Parentheses are optional for method calls in Ruby. Most of the time, you simply omit them—particularly when you don’t pass any parameters.
• The // style of commenting in C# is replaced by the # notation in Ruby. Authors Note: Because its syntax is regarded as self-explanatory, a common belief is that Ruby rarely requires comments. I’m a little skeptical of this idea, but I will admit that it’s often notably easier to understand Ruby code as a human reader than it is the C# equivalent.
Dynamically Typed Means Faster Coding
Not only is Ruby an interpreted (i.e., dynamically evaluated) language , it is also dynamically typed. So the vari- ables in Ruby do not require the specification of their types before you use them. The interpreter will infer variable types as they are assigned values. To add another twist, you don’t have to declare variables at all! This is a com- mon feature of many interpreted languages, and even a few compiled languages. To see what I’m talking about, consider the following Ruby snippet:
#Ruby
abc = 1 #abc is a Fixnum (integer) puts abc
abc = “Rubber Baby Buggy Bumpers” # now it’s a string! puts abc
You can see that you have to declare neither the variables nor their types in Ruby, and you can change the type by assigning it a different value—right in the middle of the running program. Very dynamic! If you tried to do some- thing like that in C#, the program wouldn’t even compile. C# requires statically defining types for variables, which allows the compiler to catch any errors that may arise when types don’t match the ways in which they are used. However, it’s faster and easier to throw together a script that doesn’t go to all of this trouble in Ruby. Both approaches may have a time and a place, but you can choose the one that best suits the problem you’re trying to solve.
The Ruby vs. C# Feature Rundown
The following is a simple example program in Ruby that demonstrates a variety of the features often seen in C# programs. (It’s another “classic” example program used in many programming tutorials.) It is chock full of new stuff I haven’t discussed yet, but I’ll explain the entire program snippet by snippet afterwards. If it’s still opaque after that, fear not because I provide a structurally similar C# version so you can compare lines for yourself:
#gessnum.rb class Game
def initialize(maxNum)
@num = rand(maxNum)
puts [“\n\nMagic Number Game!\n”,
“——————\n\n”,
“I’m thinking of a magic number between 0 and #{maxNum}\n”, “Would you like to guess my number? (y/n) [n]:”]
playNow = gets or “n” if playNow.chop == “y”
14
The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ] elseputs “OK, Bye!”
def play
loop do # infinite loop!
puts “\nNumber?!?” attempt = gets or break
play
end end
case attempt.to_i <=> @num when 1 # attempt > @num
puts “Guess Lower!” when -1 # attempt < @num
puts "Think Bigger, Mac."
else # only have == left... so...
puts "Spot on! That's it! Bye!" break # hop out of our loop!
end end end end Game.new(100)
Here's a sample run:
C:\proj\ruby>ruby sample.rb
Magic Number Game! ——————
I’m thinking of a magic number between 0 and 100 Would you like to guess my number? (y/n) [n]:
y
Number?!?
50
Guess Lower!
Number?!?
25
Think Bigger, Mac.
Number?!?
37
Spot on! That’s it! Bye!
15 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
At the very beginning of the code, I define a class. Notice how it’s just the word “class” followed by the class
name—no curly anything, and I haven’t set a namespace.
The Game class contains two methods: initialize, the constructor, and play, the main body of the game. Methods are designated simply by the def keyword, and are terminated by the end keyword. In Ruby, all constructors are called initialize, and they are called when an object is instantiated (more on this in a minute).
The following snippet designated a variable called @num in the constructor. The variable was set to a random number between 0 and maxNum:
@num = rand(maxNum)
Any variable inside a class that starts with a @ sign is known as an instance variable (like a field in C#). Just like in C#, the default for an instance variable is to be private, meaning that folks outside the class cannot see it or read its value. Why would I bother putting it in a field instead of a local variable? Well, for the same reasons that I’d do so in C#; I need to access it from other methods, namely the play method.
This command printed an array as a string:
puts [“\n\nMagic Number Game!\n”, “——————\n\n”,
“I’m thinking of a magic number between 0 and #{maxNum}\n”, “Would you like to guess my number? (y/n) [n]:”]
It’s much easier to just declare an array of strings and issue a single puts command for it to make a puts for each one.
The #{maxNum} portion is a Ruby trick known as string interpolation. The value of maxNum will be substituted for the occurrence of #{maxNum} in the string. This is roughly analogous to the String.Format() idiom in C#.
I set the value of a local variable ‘playNow’ to the result of the gets function, which reads a string from the input stream:
playNow = gets
if playNow.chop == “y”
play
elseputs “OK, Bye!” end
I had to compare playNow to “y” (for yes) to make sure the user actually wants to play the game. But wait, you say, what’s that .chop business? That extension will drop the last character from the value, which would be the newline character, because gets records the newline generated by the enter key when it reads the input stream. So, if the program gets a “y” it invokes the play method, otherwise it kindly says goodbye.
Normally, trying to run code from within the constructor may not be such a great idea or most objects, but for the sake of this paltry example game, it’s no big deal:
def play
loop do # infinite loop!
puts “\nNumber?!?”
16
The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ] attempt = gets or break
case attempt.to_i <=> @num when 1 # attempt > @num
puts “Guess Lower!” when -1 # attempt < @num
puts "Think Bigger, Mac."
else # only have == left... so...
puts "Spot on! That's it! Bye!" break # hop out of our loop!
end end end
The play method is an infinite loop, meaning that it will continue to execute until the process is terminated or something inside the loop issues a break statement. I prompt the user for a number and store it in the local vari- able attempt.
Listing 1. The C# Version of the Guess the Number Game
using System; namespace GuessMe {class Game
{int num;
public Game(int maxNum)
{Random r = new System.Random(); num = r.Next(maxNum);
Console.Out .WriteLine(
String.Join("\n", new string[] {
"\n\nMagic Number Game!",
"------------------\n",
String.Format("I'm thinking of a magic number between 0 and {0}", maxNum), "Would you like to guess my number? (y/n) [n]:"}
);
string playNow = Console.In.ReadLine(); if (playNow == "y")
{play();
}else
{Console.Out.WriteLine("OK, Bye!"); }
continued
17 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
The next bit is a little strange for a C# fan, but it actually is not all that different from a switch statement. The expression it is case-ing on (switching on) is attempt.to_i <=> @num. The first part, attempt.to_I, converts the string value attempt to an integer. (The Ruby class of objects holding small integer values is actually called Fixnum.) It’s a built-in method of the %(String) class, which itself is a built-in type. The <=> operator is analogous to the C# idiom of CompareTo(). If the values are equal, <=> returns the integer 0. If the left is less than the right, an integer value of -1 is returned, and a 1 is returned if the left side of the expression is greater than the right. Basically, this is a switch for the three possible values, but instead of the C# way (switch… case), it’s the Ruby way (case…when):
The very last line is the actual body of the main program:
Game.new(100)
The only thing that happens here is that an instance of the Game class is created by calling the .new method with the parameter of 100. In Ruby, .new is a special method that will invoke the initialize method, the constructor of the object. Notice the object isn’t assigned. Nobody needs to see the object, so it’s not stored.
Listing 1. The C# Version of the Guess the Number Game
}
void play()
{string attempt = null; while(true)
{
Console.Out.WriteLine(“\nNumber?!?”); attempt = Console.In .ReadLine ();
}
}class GuessGame
{public static void Main(string[] args) {new Game(100);
switch(Convert.ToInt32(attempt).CompareTo(num))
{case -1:
Console.Out.WriteLine(“Think Bigger, Mac.”);
break;
case 1:
Console.Out .WriteLine(“Guess Lower!”);
break;
default:
Console.Out .WriteLine(“Spot on! That’s it! Bye!”); return;
18 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ] The Ruby Mixin Feature
One trick I haven’t covered is the ability of Ruby to change an existing class that has already been defined. This is called a mixin because it allows you to mix in your code with code that already exists. You can even create mixins that alter the built-in types in Ruby, effectively altering the way the entire language operates. To add even more variability, mixins allow you to import classes, methods, or extend classes with features of a Module (like a static class in C# 2.0) by modifying either the classes themselves or just particular instances of the classes!
You also can re-open an existing class definition and inject your own methods in, or override ones that are already there. For example, I could redefine the .to_s method of the Fixnum class, which is the class that all integers take on by default, to return something like “I, for one, welcome our robot overlords” every time. (The wisdom of doing something like this is of course questionable.) For the sake of demonstrating how very open the entire struc- ture of Ruby is, here’s how you can do this:
Fixnum.class_eval do def to_s
“I, for one, welcome our robot overlords.” end
end
q = 123 puts q.to_s
Of course, don’t try this at home, or at least don’t do it in production code.
For Further Study
The Ruby language offers many more features and goodies than I could cover in one article, but absorbing them a few at a time from here on will be no harder than what this tutorial has shown. For a quick perusal of how Ruby generally compares to C# and Java, see Table 2.
Language Feature Ruby C# Java
Object Oriented
Yes
Yes
Yes
Compiled/Interpreted
Interpreted
Compiled
Both (usually compiled)
Variable Typing
Dynamic
Static
Static
Native Mixins
Yes
No
No (some add-on libraries offer limited support)
Closures
Yes
In version 2.0 +
No
Reflection
Yes
Yes
Yes
Multi-Threading
Yes
Yes
Yes
Regular Expressions
Native
Standard library support
Standard library support
Static and Instance Methods
Yes
Yes
Yes
Type Inference
Yes
No
No
Strong Typing
Yes*
Yes
Yes
* Ruby is still strongly typed, but the type can be inferred and easily changed at run-time. Note that this is not the same as statically typed.
19 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ]
One of the particularly nice things about Ruby is the voluminous mass of documentation generated by the ever- expanding Ruby user community. If you would like to learn some more about Ruby, you should check out the industry standard tongue-in-cheek guide known as Why’s (Poignant) Guide to Ruby. If humor isn’t your ideal way to learn a language, then you might want to see the more formal Programming Ruby, which is actually quite authori- tative on the subject. No matter what your preferences are, you can get wherever you want to go with Ruby from the official Ruby Language site (http://www.ruby-lang.org/).
20 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ] The Road to Ruby from C++
By Michael Voss
Get the code for this article at: http://assets.devx.com/sourcecode/19155.zip
If you’re a C++ developer who’s curious about all of the hype surrounding the Ruby programming language, this article is for you. It provides a high-level overview of the
key differences between C++ and Ruby, and then presents a small, complete example application implemented with each language.
Be forewarned, however: learn- ing Ruby can be a very frustrat- ing experience! Because once you become familiar with this powerfully concise language, you might find returning to C++ a bitter pill to swallow.
A High-Level Language Comparison and a Running Example
C++ is a statically typed, com-
piled language that has hybrid
object orientation. Its static typing means that the type of every expression and variable is known at compile- time, allowing significant correctness checking before the program executes. Its hybrid object orientation means that it defines non-object primitive types such as int and float, and functions can exist outside of objects. The Ruby programming language is designed to let you write code quickly and concisely. Unlike C++, it is a very dynamic, interpreted language that includes a powerful set of libraries. While it is often referred to as a scripting language, it is a pure objected-oriented lan-
guage that has sufficient expressiveness for general- purpose applications.
In Ruby, variables do not need to be declared and are free to change type from statement to statement. So the following code, where the variable x changes from a FixNum (an integer that fits within a native machine word) to a String to an Array, is a perfectly legal
Jupiterimages
sequence of Ruby code:
x = 10
x += 4
x = “My String”
x = [1, “My String”, Hash.new ]
A significant downside of Ruby’s dynamism is its use of an inter- preter. Ruby’s runtime perform- ance just can’t compare to a compiled language like C++. So even if you find yourself in love with the features of Ruby, you’re likely better off sticking with
C++ if you really need runtime efficiency.
Having digested some of the key differences between C++ and Ruby, you’re now ready to examine the small, complete example application implemented with each language. The application calculates the total number of occurrences for each word found in a set of files with a given directory, and generates an XML file that sum- marizes these occurrences as output. Listing 1 shows the C++ implementation, and Listing 2 shows the Ruby implementation.
21 The Road to Ruby, an Internet.com Developer eBook. Copyright 2008, Jupitermedia Corp.
[ The Road to Ruby ] The Basics of Classes and Variables
Both versions define the following three classes:
1. A class that represents the total number of occurrences of a word across all files
2. A class that is derived from this class and extended to also maintain the occurrences of each word by file
3. A counter class that reads and parses the files, creates and updates the word counts, and outputs the XML file
The first class is defined in Listing 1 at line 22 and in Listing 2 at line 4. Both implementations maintain a string word and a total_count variable. In Ruby, instance variables are preceded by a “@” symbol, and therefore Listing 2 has @word and a @total_count. Local variables have no prefix and global variables have a “$” symbol as a prefix.
The C++ code uses a struct to declare this class. Therefore, the variables word and a total_count are public by default. Ruby, however, does not allow access to instance variables from outside of the object; all instance vari- ables are private. You’ll learn more about access control later, but for now focus on adding the needed accessor methods. Luckily, as the statement at line 7 of Listing 1 demonstrates, adding these methods is no chore. You can automatically generate the needed get accessor methods by listing the variables after attr_reader.
Both implementations of this class also define a constructor that takes the word string, a method add that incre-
Listing 1. The C++ Implementation
This C++ application calculates the total number of occurrences for each word found in a set of files with a given directory, and generates an XML file that summarizes these occurrences as output.
// for directory manipulation #include
#include
// streams and strings #include
// STL collections #include