6.1. About Ruby
Ruby is a dynamic, fully object-oriented language that's usually grouped with
scripting languages. The scripting term, for languages like Ruby, Smalltalk, and
Python, is a little too limited, so I'll use the term applications language . If you've
used nothing but compiled languages like Java and C, get ready to have some fun.
Ruby will turn you loose. I suggest that you install it (just go to http://ruby-
lang.org ), and type along. It comes with a primitive IDE, but the command line
works well. Fire up a Ruby shell by typing irb. You'll get a shell prompt:
irb(main):001:0>
6.1.1. Ruby Is Fully OO
From here, you can evaluate Ruby statements. You'll frequently use irb
to answer
those tiny questions that come up often in programming. In Ruby, everything is an
object, and if you type one alone, Ruby will return that object. Type 4 and press
Enter:
irb(main):001:0> 4
=> 4
Unlike Java, numbers are objects , not primitives. For example, you can do this:
irb(main):008:0> 4.4765.round
=> 4
Even nil is a class, standing for nothing:
irb(main):009:0> nil.class
=> NilClass
You don't have to worry about primitives or wrappers at all. More importantly,
you
don't have to deal with those cases in an API. Ruby's reflection, persistence
engines, and XML frameworks are all much simpler, because you don't have to
with the oversimplified definition for this chapter.
irb(main):016:0> n.size
=> 4
How do you know what methods a string supports? Just ask one:
irb(main):017:0>
n.methods
=> ["send", "%", "rindex", "between?", "reject", "[ ]=", "split", "<<",
"object_id", "strip", "size", "singleton_methods", "downcase", "gsub!",
and so on
So, String supports a whole bunch of methods. Try to count them with the
size method. If you've always used statically typed languages, you will probably
underestimate the benefits. You've read that dynamic typing lets you focus on the
right part of the problem at the right time. It eases your refactoring burden, and
reduces the amount of code that you have to write and maintain.
6.1.3. Conditionals
Ruby's conditionals will remind you more of C than Java. In Ruby, nil and
false evaluate to false, and everything else (including TRue) means true. Read
that sentence again. Unlike C, 0 is true. You should also notice that false and
"false" are different. One is the Boolean constant for false, and one is a string.
For example, puts "It's false." unless "false" returns nil, but
puts "It's false." unless false will print It's false.
Ruby also has a few more conventions that you should know about. ? and ! are
both valid in method names. By convention, methods ending in ? are tests. For
example, nil? would test to see if a value is Nil. Methods ending in ! are
potentially dangerous, because they have side effects. For example, a method
called replace(in_string, sub_string, replacement) might
return a string with the substring replaced, while replace!(in_string,
sub_string, replacement) would modify the input string.
Like Java, Ruby has an if statement. Ruby also supports an unless statement
you might do this:
irb(main):010:0> puts line while line=gets
one
one
two
two
^Z
=> nil
The loop continued until I entered the end-of-file character. Of course, you can
also direct the input stream to a file. Plus you can use while at the beginning of a
line, as long as you terminate it with an end:
irb(main):013:0> while line=gets
irb(main):014:1> puts line
irb(main):015:1> end
You've already seen Until, the other looping construct. It works in exactly the
same way, but it will fire the loop while the expression is false. You'll also see a
for loop later, but that's just syntactic sugar.
6.1.5. Ranges
Java programmers typically will specify a range using an arithmetic expression,
like this:
class Range {
public static void main (String args[ ]) {
int i = 4;
if (2 < i && i < 8) System.out.println("true");
}
}
You can do something similar in Ruby, but you've got another alternative. Ruby
supports first-class range support. x y represents values from x to y, inclusive.
For example, 1 3 represents 1, 2, 3. You can include the 3 with a third
period. As you can imagine, ranges in Ruby are objects:
to refactor. Still, they can be much more useful than similar code, handwritten to
recognize certain patterns.
In Ruby, you'll define a regular expression between slashes. You'll match regular
expressions like this:
irb(main):027:0> regex = /better/
=> /better/
irb(main):028:0> regex.class
=> Regexp
irb(main):029:0> "Mine is bigger" =~ regex
=> nil
irb(main):030:0> "Mine is better" =~ regex
=> 8
Ruby returns the index of the character at the match. Ruby regular expressions are
much more powerful than I can show you here. I'll just say that Java developers
spend at least half of their time dealing with strings. When you think about it,
servlets, XML strings, configuration files, deployment descriptors, and application
data can all be strings. To parse them effectively, you need first-class pattern
matching, such as regular expressions and ranges. Java 1.5 closes the gap some,
but not completely.
6.1.7. Containers
Ruby containers are like Java's collections. You just saw an array. Like Java,
arrays are objects: [1,2,3].class returns Array. Unlike Java, everything in
an array is also an object. Ruby also has a Hash. Like Java's HashMaps, a Ruby
Hash is an object. Unlike Java's HashMap, a Ruby Hash also has some syntactic
sugar. You use braces instead of brackets, and you use key=>value to define
one key-value pair, like this:
irb(main):011:0> numbers={0=>"zero", 1=>"one", 2=>"two", 3=>"three"}
=> {0=>"zero", 1=>"one", 2=>"two", 3=>"three"}
irb(main):012:0> 4.times {|i| puts numbers[i]}
zero
irb(main):003:0> stack.pop
=> "cat"
irb(main):004:0> stack
=> [1, 2, 3]
Similarly, you can use Hash whenever you need a set, dictionary, or any type of
unordered collection. You'll find yourself doing more with collections, and less
customized iteration.
6.1.8. Files
Iterating through a file works much like iterating through a collection. You'll create
a new file and pass it a code block. For example, here's a simple GREP:
File.open(ARGV[0]) do |file|
rx = Regexp.new(ARGV[1])
while line=file.gets
puts line if line =~ rx
end
end
To use it, type it into a file called grep.rb. Then, you can call it (outside of irb)
like this:
ruby grep.rb filename regex
Notice what you don't see. You don't have to close the file or manage exceptions.
This implementation makes sure the file will be closed if an exception occurs.
You're effectively using a library that specifies everything on the outside of a
control loop that iterates through a file. Ruby does the repetitive dirty work for
you, and you customize the inside of the control loop with a code block.
6.1.9. Why Should You Care?
By now, you should be getting a feel for the power and simplicity of Ruby. You
can probably see how the lines of code go down and the abstraction goes up. You
might think it doesn't make any difference. You could lean ever harder on your
development environments and on code generation tools like XDoclet , and shield
yourself from some of the problem, but let me tell you: lines of code matter!
a method. Iterating through a JDBC loop, processing a file, and iterating through a
collection are only a few examples of this strategy. Some Java developers call this
strategy inversion of control .
Ruby lets you program with both styles, as shown in Figure 6-1. Code written with
that strategy is a joy to maintain, and it hides repetition from you. To be fair, some
Java frameworks, like Spring, do some of this for you as well, but it's not as easy in
Java, and this style of programming is not nearly as common, since you have to use
the heavyweight anonymous inner class to do so. In dynamic languages like Ruby
and Smalltalk, this programming strategy gives you tremendous intellectual
freedom, both in the frameworks that you use and in the frameworks that you
build.