Learn Ruby on Rails: the Ultimate Beginner’s Tutorial

Share this article

Running Ruby Files

For the simple Ruby basics that we’ve experimented with so far, the interactive Ruby shell (irb) has been our tool of choice. I’m sure you’ll agree that experimenting in a shell-like environment, where we can see immediate results, is a great way to learn the language.

However, we’re going to be talking about control structures next, and for tasks of such complexity, you’ll want to work in a text editor. This environment will allow you to run a chunk of code many times without having to retype it.

In general, Ruby scripts are simple text files containing Ruby code and have a .rb extension. These files are passed to the Ruby interpreter, which executes your code, like this:

$ ruby myscript.rb

To work with the examples that follow, I’d recommend that you open a new text file in your favorite text editor (which might be one of those I recommended back in Chapter 2, Getting Started) and type the code out as you go — this really is the best way to learn. However, I acknowledge that some people aren’t interested in typing everything out, and just want to cut to the chase. These more impatient readers can download the code archive for this book, which contains all of these examples. You can execute this code in the Ruby interpreter straight away.

As demonstrated above, to run the files from the command line, you simply need to type ruby, followed by the filename.

Control Structures

Ruby has a rich set of features for controlling the flow of your application. Conditionals are key words that are used to decide whether or not certain statements are executed based on the evaluation of one or more conditions; loops are constructs that execute statements more than once; blocks are a means of encapsulating functionality (for example, to be executed in a loop).

To demonstrate these control structures, let’s utilize some of the Car classes that we defined earlier. Type out the following class definition and save the file (or load it from the code archive); we’ll build on it in this section as we explore some control structures.

Example 3.1. 01-car-classes.rb

class Car
@@wheels = 4              # class variable
@@number_of_cars = 0      # class variable
def initialize
@@number_of_cars = @@number_of_cars + 1
end
def self.count
@@number_of_cars
end
def mileage=(x)           # mileage writer
@mileage = x
end
def mileage               # mileage reader
@mileage
end
end

class StretchLimo < Car
@@wheels = 6              # class variable
@@televisions = 1         # class variable
def turn_on_television
# Invoke code for switching on on-board TV here
end
end

class PontiacFirebird < Car
end

class VolksWagen < Car
end

Conditionals

There are two basic conditional constructs in Ruby: if and unless. Each of these constructs can be used to execute a group of statements on the basis of a given condition.

The if Construct

An if construct wraps statements that are to be executed only if a certain condition is met. The keyword end defines the end of the if construct. The statements contained between the condition and the end keyword are executed only if the condition is met.

Example 3.2. 02-if-construct.rb (excerpt)

if Car.count.zero?
puts "No cars have been produced yet."
end

You can provide a second condition by adding an else block: when the condition is met, the first block is executed; otherwise, the else block is executed. This kind of control flow will probably be familiar to you. Here it is in action:

Example 3.3. 03-if-else-construct.rb (excerpt)

if Car.count.zero?
puts "No cars have been produced yet."
else
puts "New cars can still be produced."
end

The most complicated example involves an alternative condition. If the first condition is not met, then a second condition is evaluated. If neither conditions are met, the else block is executed:

Example 3.4. 04-if-elsif-else.rb (excerpt)

if Car.count.zero?
puts "No cars have been produced yet."
elsif Car.count >= 10
puts "Production capacity has been reached."
else
puts "New cars can still be produced."
end

If the count method returned 5, the code above would produce the following output:

New cars can still be produced.

An alternative to the traditional if condition is the if statement modifier. A statement modifier does just that — it modifies the statement of which it is part. The if statement modifier works exactly like a regular if condition, but it sits at the end of the line that’s affected, rather than before a block of code:

Example 3.5. 05-if-statement-modifier.rb (excerpt)

puts "No cars have been produced yet." if Car.count.zero?

This version of the if condition is often used when the code that’s to be executed conditionally comprises just a single line. Having the ability to create conditions like this results in code that’s a lot more like English than other programming languages with more rigid structures.

The unless Construct

The unless condition is a negative version of the if condition. It’s useful for situations in which you want to execute a group of statements when a certain condition is not met.

Let’s create a few instances to work with (Aficionados of comics will notice that I’ve created the BatMobile as a Pontiac Firebird — in fact, the caped crusader’s choice of transport has varied over the years, taking in many of the automobile industry’s less common innovations, and including everything from a 1966 Lincoln Futura to an amphibious tank. But we’ll stick with a Pontiac for this example.):

Example 3.6. 06-unless-construct.rb (excerpt)

kitt = PontiacFirebird.new
kitt.mileage = 5667

herbie = VolksWagen.new
herbie.mileage = 33014

batmobile = PontiacFirebird.new
batmobile.mileage = 4623

larry = StretchLimo.new
larry.mileage = 20140

Now if we wanted to find out how many Knight Rider fans KITT could take for a joy-ride, we could check which class the kitt object was. As with the if expression, the end keyword defines the end of the statement.

Example 3.7. 06-unless-construct.rb (excerpt)

unless kitt.is_a?(StretchLimo)
puts "This car is only licensed to seat two people."
end

Like the if condition, the unless condition may have an optional else block of statements, which is executed when the condition is met:

Example 3.8. 07-unless-else.rb (excerpt)

unless kitt.is_a?(StretchLimo)
puts "This car only has room for two people."
else
puts "This car is licensed to carry up to 10 passengers."
end

Since KITT is definitely not a stretch limousine, this code would return:

This car only has room for two people.

Unlike if conditions, unless conditions do not support a second condition. However, like the if condition, the unless condition is also available as a statement modifier. The following code shows an example of this. Here, the message will not display if KITT’s mileage is less than 25000:

Example 3.9. 08-unless-statement-modifier.rb (excerpt)

puts "Service due!" unless kitt.mileage < 25000

Loops

Ruby provides the while and for constructs for looping through code (i.e. executing a group of statements a specified number of times, or until a certain condition is met). Also, a number of instance methods are available for looping over the elements of an Array or Hash; we’ll cover these in the next section.

while and until Loops

A while loop executes the statements it encloses repeatedly, as long as the specified condition is met.

Example 3.10. 09-while-loop.rb (excerpt)

while Car.count < 10
Car.new
puts "A new car instance was created."
end

This simple while loop executes the Car.new statement repeatedly, as long as the total number of cars is below ten. It exits the loop when the number reaches ten.

Like the relationship between if and unless, the while loop also has a complement: the until construct. If we use until, the code within the loop is executed until the condition is met. We could rewrite the loop above using until like so:

Example 3.11. 10-until-loop.rb (excerpt)

until Car.count == 10
Car.new
puts "A new car instance was created."
end

The Difference Between = and ==

It’s important to note the difference between the assignment operator (a single equal sign) and the equation operator (a double equal sign) when using them within a condition.

If you’re comparing two values, use the equation operator:

if Car.count == 10
...
end

If you’re assigning a value to a variable, use the assignment operator:

my_new_car = Car.new

If you confuse the two, you might modify a value that you were hoping only to inspect, with potentially disastrous consequences!

for Loops
for loops allow us to iterate over the elements of a collection — such as an Array — and execute a group of statements once for each element. Here’s an example:

Example 3.12. 11-for-loop.rb (excerpt)

for car in [ kitt, herbie, batmobile, larry ]
puts car.mileage
end

The code above would produce the following output:

5667
33014
4623
20140

This simple for loop iterates over an Array of Car objects and outputs the mileage for each car. For each iteration, the car variable is set to the current element of the Array. The first iteration has car set to the equivalent of lincoln_towncar, the second iteration has it set to chrysler_voyager, and so forth.

In practice, the traditional while and for loops covered here are little used. Instead, most people tend to use the instance methods provided by the Array and Hash classes, which we’ll cover next.

Blocks

Blocks are probably the single most attractive feature of Ruby. However, they’re also one of those things that take a while to “click” for Ruby newcomers. Before we dig deeper into creating blocks, let’s take a look at some of the core features of Ruby that use blocks.

We looked at some loop constructs in the previous section, and this was a useful way to explore the tools that are available to us. However, you’ll probably never actually come across many of these constructs in your work with other Ruby scripts, simply because it’s almost always much easier to use a block to perform the same task. A block, in conjunction with the each method that is provided by the Array and Hash classes, is a very powerful way to loop through your data.

Let me illustrate this point with an example. Consider the for loop we used a moment ago. We could rewrite that code to use the each method, which is an instance method of the Array class, like so:

Example 3.13. 12-simple-block.rb (excerpt)

[ kitt, herbie, batmobile, larry ].each do |car_name|
puts car_name.mileage
end

Let’s analyze this: the block comprises the code between the do and end keywords. A block is able to receive parameters, which are placed between vertical bars (|) at the beginning of the block. Multiple parameters are separated by commas. Therefore, this code performs an identical operation to the for loop we saw before, but in a much more succinct manner.

Let’s take another example. To loop through the elements of a Hash, we use the each method, and pass two parameters to the block — the key (car_name) and the value (color) — like this:

Example 3.14. 13-block-with-params.rb (excerpt)

car_colors = {
'kitt' => 'black',
'herbie' => 'white',
'batmobile' => 'black',
'larry' => 'green'
}
car_colors.each do |car_name, color|
puts "#{car_name} is #{color}"
end

This code produces the following output:

kitt is black
herbie is white
batmobile is black
larry is green

The Integer class also sports a number of methods that use blocks. The times method of an Integer object, for example, executes a block exactly n times, where n is the value of the object.

Example 3.15. 14-block-integer.rb (excerpt)

10.times { Car.new }
puts "#{Car.count} cars have been produced."

The code above produces this output:

10 cars have been produced.

One final point to note here is the alternate block syntax of curly braces. Instead of the do...end keywords that we used in previous examples, curly braces are the preferred syntax for blocks that are very short, as in the previous example.

Here’s another method of the Integer class — in the spirit of times, the upto method counts from the value of the object up to the argument passed to the method.

Example 3.16. 15-block-upto.rb

5.upto(7) { |i| puts i }

This code produces the output shown here:

5
6
7

In Ruby parlance, the object i is a parameter of the block. Parameters for blocks are enclosed in vertical bars, and are usually available only from within the block. If we have more than one parameter, we separate them using commas, like so: |parameter1, parameter2|. In the example above, we would no longer have access to i once the block had finished executing.

As we work through this book, we’ll explore many more uses of blocks in combination with the Rails core classes.

Summary

Wow, we covered a lot in this chapter! First, we swept through a stack of object oriented programming theory — probably the equivalent of an introductory computer science course! This gave us a good grounding for exploring the basics of the Ruby programming language, and the Interactive Ruby Shell (irb) was a fun way to do this exploration.

We also investigated many of the Ruby core classes, such as String, Array, and Hash, from within the Ruby shell. We then moved from the shell to create and save proper Ruby files, and using these files, we experimented with control structures such as conditionals, loops, and blocks.

In the next chapter, we’ll look at the major cornerstones that make up the Rails framework — the integrated testing facilities — as well as the roles that the development, testing, and production environments play.

Go to page: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
Patrick LenzPatrick Lenz
View Author

Patrick has been developing web applications for ten years. Founder and lead developer of the freshmeat.net software portal, he and his Rails consultancy and application development company, limited overload were responsible for a major relaunch of Germany's biggest infotainment community. Patrick lives in Wiesbaden, Germany. His weblog can be found at poocs.net.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week