An Incredibly Brief Introduction to Ruby: Appendix A - Learning Rails

by Edd Dumbill, Simon St. Laurent

Fortunately, you don’t need to know a whole lot of Ruby to get real work done with Rails. The creators of Rails have used many of Ruby’s most advanced features to make Rails easy to work with, but fortunately you can enjoy the benefits without having to know the details. This Appendix explains the basics you’ll need to perform typical tasks in Rails and should help you get started. For a lot more detail on Ruby, try Learning Ruby (O’Reilly, 2007), The Ruby Programming Language (O’Reilly, 2008), the Ruby Pocket Reference (O’Reilly, 2007), or Programming Ruby (Pragmatic Programmers, 2004).

Learning Rails book cover

This excerpt is from Learning Rails . Most Rails books are written for programmers looking for information on data structures. Learning Rails targets web developers whose programming experience is tied directly to the Web. Rather than begin with the inner layers of a Rails web application -- the models and controllers -- this unique book approaches Rails development from the outer layer: the application interface. You can start from the foundations of web design you already know, and then move more deeply into Ruby, objects, and database structures.

buy button

Note

If you’ve never worked with a programming language before, this Appendix may go too fast for you. It’s hard to be incredibly brief and cover the basic basics at the same time. However, if you’ve worked with JavaScript before, you should be able to get started here.

Ruby is a beautiful but sometimes mystifying language, and probably a better choice as a second language to learn rather than a first language.

Because this is a Rails book, examples will work inside of the Rails framework, in a Rails view and controller, rather than from the command line. If you haven’t touched Rails before, it makes sense to read Chapter 1, Starting Up Ruby on Railsfirst and get Rails installed, and then come back here for more instruction.

How Ruby Works

Ruby is an object-oriented language. Although it’s often compared to Perl, because Ruby code often looks like Perl, Ruby’s object-orientation goes much deeper. Practically everything in Ruby is an object.

What does that mean?

Objects are combinations of logic and data that represent a (usually mostly) coherent set of tasks and tools for getting them accomplished. Programming objects aren’t quite as concrete as objects in the real world, often created and destroyed (or at least abandoned for cleanup later) in fractions of a second. Nonetheless, in those brief moments—or in the hours, days, or years they could also exist—they provide a practical means of getting things done.

In some sense, a program is a big toolchest filled with these objects, and programming is about assembling objects to put into the chest. Ruby provides some starter objects and a means of creating new objects and, of course, ways to start these objects interacting with each other so that the program actually runs.

There are a few other important things to know about Ruby. They’re probably most important if you’re coming to Ruby from other programming languages that have different expectations, but they all affect the way you’ll write Ruby programs:

  • Ruby is an interpreted language, meaning that Ruby reads through the code and decides how to execute it while it’s running, rather than reading it and turning it into a highly optimized set of instructions before it actually runs. (There are a few people working on ways to create a compiled Ruby, but that’s unusual.) While that slows things down, it also adds a lot of flexibility.

  • Ruby also has really flexible syntax expectations. Most of the time this makes things easier—you don’t need to type parentheses around method parameters most of the time. Other times, however, you’ll find yourself facing mysterious error messages because you didn’t include parentheses and the situation is slightly ambiguous. (This book tries to warn you about these kinds of situations when they appear in Rails.)

  • Ruby uses dynamic typing.[2] Some languages (notably Java, C, and C++) expect that the programmer will always know, and always specify, the kind of information they expect to store in a given information container, a variable. Locking that down in advance makes it easy to do some kinds of optimization. Ruby has taken another path, leaving variables open enough to contain any kind of information and be created at any time. Again, this allows for a much more flexible approach, in which operations can change what they do based on context. Sometimes, however, it means that things can go wrong in strange and unpredictable ways if something unexpected is in a variable.

  • Ruby supports blocks and closures. You don’t need to know how to create methods that work with blocks or closures in order to use Rails, but you definitely do need to know how to call methods that expect a block of code as an argument. At first, your best choice for dealing with these features will be to look at sample code and use it as a foundation rather than trying to step back and figure out how this should work in the abstract.

  • Ruby lets advanced developers perform metaprogramming, even creating Domain Specific Languages (DSLs), which are kind of like their own miniature programming language focused on a particular task. You don’t need to know how to do metaprogramming to use Rails, but you should be aware that Rails uses these techniques. Sometimes you’ll encounter something that claims to be Ruby but seems very strange and too specialized to be part of the Ruby language. Odds are good that metaprogramming is involved. As with blocks and closures, it’s often best to start out by emulating sample code to work toward figuring it out.

Ruby is a very powerful language. It’s not hard to get started in Ruby, but you should at least be aware of these powerful techniques so you can recognize them when you encounter them. Knowing that these possibilities exist may help reduce your frustration when you encounter mysterious difficulties.

How Rails Works

Rails is a set of Ruby objects that together make up a framework. Installing Rails is a first step toward building an application, but while it gives you many useful objects that can run happily in a web environment, there’s a lot missing, a lot you have to provide.

You can buy a beehive—a set of boxes with frames that the bees will inhabit and fill with honey. It’ll have a top, a base, an entrance, a number of useful architectural features, and a nice coat of paint. It looks like a beehive when it’s set up. Unfortunately, setting up a beehive is just the first step. To make a beehive work, you have to add bees, who will finish building their home, collect useful nectar and pollen, and make the hive interact with the world.

Rails gives you an empty beehive. You don’t add bees, exactly, but you do populate it with your own logic. That logic turns Rails from an empty container into a dynamic application, connected to the outside world and performing the tasks you define.

The rest of this Chapter will teach Ruby within the Rails framework, explaining the language in the context you’ll likely be using it.

Note

If you haven’t installed Rails yet, take a look at Chapter 1, Starting Up Ruby on Rails. It might be easiest to use Heroku, a web-based implementation that will spare you having to really install Rails before getting started. On the other hand, if you want to stay at the command line, you can also run much of this code in irb, the Ruby command-line interface described in Chapter 11, Debugging.

Getting Started with Classes and Objects

Most of the Rails files you’ll work with and create define classes. (They do so even when they don’t have explicit class definitions, as Rails performs some of its magic in the background.) The clearest place to work with objects in Rails is in the controller classes. To get started, therefore, go to the command line and create a new application and a new controller:

rails testbed
...
cd testbed
...
ruby script/generate controller Testbed index

Note

If you’re using Heroku, instead of going to the command line, log in to Heroku and click on the Create A New Application button from the My Apps page. You can rename it “testbed” if you want, but the application name doesn’t matter much. What does matter is that when the application editing screen opens, you click on the gear menu near the bottom left, choose Generate, and enter controller Testbed index. That will set things up for the rest of these examples.

For the rest of this appendix, there are only two files that matter: app/views/testbed/index.html.erb and app/controllers/testbed_controller.rb. For right now, replace the contents of app/views/testbed/index.html.erb with:

<%= @result %>

That will make it easy to see the results of the code in the controller, which is a clearer place to explore Ruby. (@result is a variable whose value various examples will set.)

If you open app/controllers/testbed_controller.rb, you’ll see the code below. It doesn’t yet do anything, except tell the programmer what it is and what it derives from:

class TestbedController < ApplicationController
  def index
  end

end

The first line, class TestbedController < ApplicationController, tells you two important things. First, it tells you that this file contains a class definition, for a class named TestbedController. Second, it tells you—you can read < as “inherits from”—that this class is descended from ApplicationController. Even though this file is basically empty, it inherits a lot from ApplicationController. Well, actually, even though ApplicationController is almost as empty (see app/controllers/application.rb if you’re curious), it inherits from ActionController::Base, a key part of the Rails framework that provides a lot of functionality for connecting controllers with requests and data.

Note

Fortunately, one of the benefits of Rails is that you almost never need to worry what’s actually done in the superclasses, as these ancestors are called. It’s strange to say “don’t look” in a tutorial—but you really don’t have to look, and certainly not at first.

The next two lines define an empty method, index, which the next section will improve on. Finally, the closing end brings the definition of the TestbedController class to its conclusion.

So, this is a class. What’s an object?

An object is an instance of a class. This class defines what a TestbedController looks like. When Rails gets a request that it thinks requires a TestbedController, it reads the class definition and creates an object that will perform as that class specifies. If necessary, Rails will create places to store the object’s data as well as connections to call its methods. Rails may create many different TestbedController objects at the same time (one per request), but all will use the same definition. The process of creating an object from a class definition is called instantiation.

Comments

While they don’t actually do anything in a Ruby program, comments are critical for making code readable, especially complicated code. Ruby comments start with a # character and continue to the end of that line. If a line starts with #, then the entire line is a comment. If a line starts with code and then includes a # (outside of a quoted string or regular expression), then everything to the right of the # is considered a comment and ignored. For example:

# This whole line is a comment
x = 2  # x is assigned the value 2, and the comment is ignored.

Comments are useful for humans, especially when you read someone else’s code or return to a project after a long while away, but Ruby will just ignore them.

Variables, Methods, and Attributes

TestbedController is a pretty dull class so far. If you start Rails (with ruby script/server, the >> button in Heroku, or whatever does it in the environment you’ve installed), and visit http://localhost:3000/testbed/, you’ll get a mostly blank response. There’s nothing in @result, because TestbedController’s index method doesn’t actually do anything.

That’s easily fixed. Change the definition of index to:

def index
   @result = 'hello'
end

Now, when you load the page, you’ll see “hello” as the result. (This is not exciting enough to deserve a screenshot.)

Variables

@result is a variable, a container for information. Because the name of the variable started with @, it is an instance variable, connected to the object in such a way that other objects can see it. That let the view retrieve it to shows its value. The new line of code assigned (=) an expression to the @result variable, the string hello.

The string was surrounded with single quotes (') to indicate that it was a string, a series of characters, rather than another variable name or something else entirely. If you need to include an apostrophe or single quote inside of a single-quoted string, just put a backslash (\) in front of the quote, as in 'Hello! Ain\'t it a pretty day?'. This is called escaping the quote, hiding it from normal processing.

Note

Ruby also lets you surround strings with double quotes. Double-quoted strings offer a lot more escaping functionality, but single-quoted strings are simpler and faster to work with. If you’re used to putting double quotes around strings, that will still work, but you may want to explore the documentation to learn what you’re getting yourself into.

@result could take a variety of different kinds of values; Ruby isn’t picky about what goes into its variable containers. You can assign it numbers, objects, boolean values—pretty much anything that comes to mind in Ruby work. Ruby will do its best to figure out what to do with the values you assign to your variables. For example, you could write:

def index
  one = 1
  two = 2
  @result = one + two
end

The value of @result would be 3, what you get for evaluating the expression one + two, which leads to adding 1 and 2. (Note that one and two are local variables—they don’t have an @ in front of their names, and are only available within the index method.) If, however, you’d written:

def index
  one = 'one'
  two = 'two'
  @result = one + two
end

The value of @result would be onetwo, because the plus operator (+) combines strings sequentially (also called concatenating them) instead of adding their numeric values. When Ruby runs that line of code, it checks to see what types are in the values before deciding how the operator will behave.

Warning

Ruby isn’t as flexible as some other dynamically typed languages. If you set one to 'one' and two to 2, you’d get the error message “can't convert Fixnum into String.” Ruby may not keep close track of what types your variables have, but effectively it’s your responsibility to do so.

While programmers often think of their code as determining the main flow of logic through an application, from a user’s point of view most of what’s interesting is what happens to the variables. Does data go to the right place? Is it stored properly? What are the results of calculations on that data?

Variables are the places you store that data as they follow these paths through your applications. You can assign values to variables and change those values. You can perform operations on those values (like +, , *, /, and much, much more), and pass variables to methods as arguments.

Arrays and hashes

Sometimes a variable should hold more than just one value. It needs to contain a list, a list of lists, or even a collection where values are connected to names. Ruby supports these needs with arrays, which are simple lists, and hashes, which are collections of named data.

Arrays start out simple. While you can create arrays more programmatically with the Array object, it’s easiest to create an array by surrounding a comma-separated list of values with square brackets:

myArray = [1, 2, 'tweet']

The values can be any Ruby expression. This one happens to mix two numbers and a string. You can reference specific items by number. For example, you might redefine the index method to look like:

def index
  myArray = [1, 2, 'tweet']
  @result = myArray[2]
end

If you’ve done a lot of programming, you might not be surprised that the @result variable ends up containing tweet. Why? Because Ruby counts arrays from zero, not from one. myArray[0] is 1, myArray[1] is 2, and, of course, myArray[2] is tweet.

Sometimes you’ll want to have lists containing lists. Ruby supports this by letting you put arrays inside of arrays:

myNestedArray= [ [1, 2, 'tweet'], [3, 4, 'woof'], [5, 6, 'meow'] ]

If you wanted to reach the meow, you’d go to item 2 of the overall array, and then item 2 of the array inside of item 2, as in:

def index
  myNestedArray= [ [1, 2, 'tweet'], [3, 4, 'woof'], [5, 6, 'meow'] ]
  @result = myNestedArray[2][2]
end

Note

You can mix arrays of any size you’d like inside of another array, or even mix in ordinary values. There’s no requirement that the array structure must be consistent.

Hashes are just a little more complicated. Hashes, also called maps or associative arrays, contain keys and values. Keys are effectively names that correspond to values. Within a given hash, all of the keys have to be unique. (Values can duplicate as necessary, though.) The easiest way to create a hash is with a hash literal:

myHash={ 'one' => 1, 'two' => 2, 'three' => 'tweet' }

To retrieve items from the hash, just call for them by name, as in:

def index
 myHash={ 'one' => 1, 'two' => 2, 'three' => 'tweet' }
    @result = myHash['two']
  end

In this case, @result will contain 2, as that corresponds to the name two. As with arrays, you can also create hashes through the Hash object and its methods.

Both the key and the value can have any type: you can use numbers, or strings, or, as Rails often does, especially in method calls, symbols.

Symbols

Rails uses symbols—names preceded by a colon, like :courses or :students—practically everywhere. They get used like variables, to refer to models. They get used as labels for options in method calls. When you’re first starting out in Rails, your best option is to study the examples and see where symbols are used and where other kinds of variables are used. Then, just follow the established pattern.

Why does Rails use symbols? The short answer is efficiency. Ruby handles symbols with less processing than strings. The long answer is a lot more complicated than that, involving the metaprogramming glue that holds the framework together. When you’re ready to extend the Rails framework yourself, you’ll need to learn the details. Until then, you don’t need a deep understanding.

Methods

So far, all of the action in these examples have taken place in one method: index. You may have the occasional controller with just one method, but most classes contain more than one method. Methods can call each other, passing each other data, establishing program logic through these many interconnections. A simple demonstration in the same testbed controller can show how this works:

class TestbedController < ApplicationController
  def index
    @result = addThem (1, 2)
  end

  def addThem (firstNumber, secondNumber)
    firstNumber + secondNumber
  end

end

When index is called, is sets a value for @result. The expression it uses, however, is a call to another method, addThem, which is given two arguments, 1 and 2.

Note

The arguments are shown here in parentheses because most other languages use them, and it’s a little easier to imagine what happens. However, the parentheses are optional in Ruby and often omitted.

The addThem method specifies that it takes two parameters, named firstNumber and secondNumber. The expression on the second line, firstNumber + secondNumber, will be evaluated, yielding 3. Ruby methods return the last value they produced, so addThem will tell index that its answer is 3. @result will be set to 3, which will be presented through the view.

Note

If you prefer, you could write return firstNumber + secondNumber, making it explicit that the value is the return value for the method. However, you won’t see this done frequently in other people’s Ruby code.

Privacy, please

Because of the way Rails routing works, the addThem method is currently exposed to the public—though there isn’t a view for it, it won’t get useful arguments, and so on. Fortunately, Ruby offers a way to hide such methods from public view while keeping them accessible to other methods in the same class. Just add the keyword private before addThem is defined:

class TestbedController < ApplicationController
  def index
    @result = addThem (1, 2)
 end

 private

 def addThem (firstNumber, secondNumber)
  return firstNumber + secondNumber
 end
end

Methods that follow the private are still available to the other methods in the class, but can no longer be called from outside of it.

Note

Ruby also offers public and protected keywords for specifying access to methods, but they aren’t frequently needed in Rails programming.

super

The methods explicitly listed in the TestbedController class are only a subset of the methods the class actually contains, because of the opening declaration:

class TestbedController < ApplicationController

All of the methods that are defined in ApplicationController will also be available in TestbedController. If you want some different behavior in TestbedController, you can override methods—defining new methods with the same name and arguments.

Chapter 8, Improving Formsshows how overriding methods can work, but there’s frequently one small problem. As often as not, the new method wants to do what the old method did, plus something additional. For example, this was a method overriding the text_field method from ActionView::Helpers::FormBuilder:

class TidyFormBuilder < ActionView::Helpers::FormBuilder
....
def text_field(method, options={})
    label_for(method, options) + super(method, options)
end

The text_field method here wants to create a label, and then call the original method that it was overriding. The call to super isn’t to a method called super—it’s to the text_field method specified in the ActionView::Helpers::FormBuilder class. This is a common technique when you need to tweak the functionality the framework provides.

Calling methods: advanced options

While you probably won’t be writing methods as sophisticated as the ones in the Rails framework itself for a little while, there are a few techniques you should understand for calling those methods.

The first, simpler one, is Rails’ frequent use of methods that take an options hash as an argument. While reading the Rails API documentation, you might encounter something like:

text_field_tag(name, value = nil, options = {})

The method name is text_field_tag, and it takes a name argument and a value argument which has a default value of nil. But what is options = {}, especially since most calls to text_field_tag don’t even use { and }?

options = {} provides a way for methods to accept named parameters, taking hash with named values specified elsewhere in the documentation. In a more formal world, the named parameters would form a hash literal inside of { and }, but Ruby doesn’t require that level of formality. You could write:

text_field_tag 'Name', 'Jim', {:maxlength => 15, :disabled => true}

But more typically you’ll see:

text_field_tag 'Name', 'Jim', :maxlength => 15, :disabled => true

In general, named parameters go at the end of the method call, and the curly braces are optional. There are times, however, when the braces are necessary, as noted in the section called “Creating Checkboxes” ” section in Chapter 6, Presenting Models with Forms.

The second, harder one, is Rails’ use of methods that take an unnamed block of code as an argument. This happens frequently with the helper methods listed in Appendix D, A Catalog of Helper Methods, as well as in the migration code explored in Chapter 10, Managing Databases with Migrations, but it’s a pattern that can appear anywhere. Sometimes, as in the layout issues discussed in Chapter 2, Rails on the Web, the block-passing is just a quiet part of the framework, and you only notice it because of a yield call.

The key to recognizing a method that takes a block as an argument is the &proc or pair of curly braces at the end of the list of arguments, and examples that show the method wrapping around other code, usually with do. The typical form looks pretty similar, whether in straight Ruby code or in ERb view markup. For example, create_table in a migration looks like:

create_table :awards do |t|
    t.string :name
    t.integer :year
    t.integer :student_id
end

A form_for call, meanwhile, looks like:

<% form_for([@student, @award]) do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :year %><br />
    <%= f.text_field :year %>
  </p>
  <p><%= f.submit "Create" %></p>
<% end %>

Each of these calls does something when it is first called. create_table orders the creation of a database table, while form_for creates an HTML form element. They don’t just complete and disappear, however—they create a context, using do, that applies until the end statement. The t variable and the f variable provide information that makes it possible for the calls inside of the do to be much shorter (and much less repetitive) than would otherwise be necessary.

Note

When you’re working in Ruby code you’ll often use { and } in place of do and end. It’s easier to read do and end amidst the < and > of the HTML markup, though.

Rails uses blocks for other purposes as well. Chapter 2, Rails on the Web explains how the yield statement lets a method execute code passed to it as a block when it seems convenient, and Appendix D, A Catalog of Helper Methods lists some helper methods (notably benchmark and cache) that use blocks for their own purposes.

Note

If you want to become a Ruby pro, studying techniques for using blocks as arguments is a good way to familiarize yourself with ways that Ruby makes amazing things happen in a very compact amount of code.

Attributes

Ruby attributes lie somewhere between methods and variables. Well, actually, attributes are methods, but when used, they feel like variables. Attributes are methods that end in =, and they get called whenever you assign a value to the property with that name. Chapter 8, Improving Forms used a photo= method to capture incoming data when the photo field arrived from a form. You may find use for them eventually in your Rails development, but at the beginning, it’s mostly useful to know the technique exists.

Logic and Conditionals

Classes, variables, and simple methods may carry some basic applications a surprisingly long way, but most applications need more logic. This quick tour through Ruby’s control structures will give you more tools for building your applications.

Operators

Your program logic will depend on combining variables with operators into expressions. Those expressions then get resolved into results, which may be used to assign values to variables, or to give an answer about whether a test passed or failed. Most Ruby operators should look familiar if you’ve ever used a programming language before. The following table shows an abbreviated list of operators you’re likely to encounter in your first forays into Rails.

Operator

Use(s)

+

Addition, concatenation, making numbers positive

Subtraction, removing from collections, making numbers negative

*

Multiplication

/

Division

%

Modulo (remainder from integer division)

!

Not

**

Exponentiation (2**3 is 8, 10**4 is 1000)

<<

Shift bits left, or add to a collection

<

Less than

<=

Less than or equal to

>=

Greater than or equal to

>

Greater than

<=>

General comparison—less then yields –1, equal returns 0, greater than 1, and not comparable nil

==

Equal to (note that a single = is just assignment and always returns true)

===

Tests to see whether objects are of same class

!=

Not equal to

=~

Tests a regular expression pattern for a match (see Appendix C, An Incredibly Brief Guide to Regular Expressions)

!~

Tests a regular expression pattern for no match

&&

Boolean AND (use to combine test expressions)

||

Boolean OR

and

Boolean AND (lower precedence)

or

Boolean OR (lower precedence)

not

Not (lower precedence)

..

Range creator, including end value

...

Range creator, excluding end value

defined?

Tests variable definition, returns details

Nearly all of these can take on other meanings, as Ruby lets developers redefine them. Usually they’ll behave as you expect, but if they don’t, you may need to examine the context you’re programming in.

if, else, unless, and elsif

The if statement is pretty much at the heart of all computer programming. Though it might be very painful, nearly all code could be rewritten as if statements. The basic approach looks like:

if expression
  thingsToDo
end

To create a simple example again, return to the TestbedController:

class TestbedController < ApplicationController
  def index
    @result = 'First is greater than or equal to last.'
    first=20
    last=25
    if first < last
      @result = 'First is smaller than last.'
    end
  end
end

Because the value of first is less than the value of last, the first < last expression will evaluate to true, and @result will be set to First is smaller than last. For evaluation purposes, anything except for false or 0 or nil will evaluate to true. Definitely try changing the values of first and last and reloading.

The if statement has a simple opposite: unless. It performs its tasks if the expression returns false. While you don’t really need it, it can make some code more readable:

def index
  @result = 'First is smaller than last.'
  first=20
  last=25
  unless first < last
    @result = 'First is greater than or equal to last.'
  end
end

The unless first < last statement means exactly the same as if !(first < last).

Sometimes you want to do something more when your first test fails. This calls for the else statement, which lets you do things instead of what you had planned if your if or unless succeeded. You could rewrite these two little methods as:

def index
  first=20
  last=25
  if first < last
    @result = 'First is smaller than last.'
  else
    @result = 'First is greater than or equal to last.'
  end
end

and:

def index
  first=20
  last=25
  unless first < last
    @result = 'First is greater than or equal to last.'
  else
    @result = 'First is smaller than last.'
  end
end

Using an else can both make your code’s results more explicit for later developers who have to maintain it, and support your efforts to do different things based on a single test.

There’s one last option in regular if statements: elsif, which combines an else and an if. You can only use it with if, not with unless, but you can have as many elsifs as you want. A simple example that extends the logic of the previous code is:

def index
    first=20
    last=25
    if first < last
      @result = 'First is smaller than last.'
    elsif first == last
      @result = 'First is equal to last.'
    else
      @result = 'First is greater than last.'
    end
  end

Note that it’s elsif, not elseif, and that the double equals sign (==) tests for equality rather than assigning a value. Using a single equals sign in a comparison is a common mistake for new arrivals from other languages. Not only does it assign the value, it always returns true, satisfying the conditional test.

There is still one other variation on if that you might encounter. Instead of:

if expression
  thingsToDo
end

it looks like:

somethingToDo if expression

It’s more concise and sometimes more readable, but it can certainly confuse you if you’re looking for neatly indented logical statements. If you want, though, you can write:

@result = 'First is greater then last' if first > last

?:

The ?: operator isn’t precisely a statement, but it works like an abbreviated if/else statement. It’s mostly used in cases where you need to return a slightly different result for one of two cases. It starts with a test expression, then has a question mark (?), then the value returned if the test expression is true, then a colon (:), and then the value returned if the test expression is false. You could rewrite the earlier if/else example as:

def index
  first=20
  last=25
  @result = (first < last ? 'First is smaller than last.' : 'First is greater than
or equal to last.')
end

Again, the message reported would be that First is smaller than last., but you can try changing the values to see what happens.

case and when

If your if statements start sprouting elsifs everywhere, it may be time to switch to case and when statements. These let you specify an expression in the case, and then test it against various conditions. You could rewrite the earlier test as:

def index
  first=20
  last=25
  case
    when first < last
       @result ='First is smaller than last.'
    when first == last
       @result ='First is equal to last.'
    when first > last
       @result ='First is greater than last.'
  end
end

There are actually many ways to write case statements. If you want to reduce repetition, you might try:

def index
  first=20
  last=25
  @result =case
    when first < last
       'First is smaller than last.'
    when first == last
       'First is equal to last.'
    when first > last
       'First is greater than last.'
  end
end

This works because case returns a value, and the when clauses just set that value. You can also add an else clause to the end of your case statement, to catch the situation where none of your when clauses matched.

Loops

Evaluations are useful, but sometimes you want to just go around and around until you’ve tested something a set number of times, a particular condition is met, or you just plain run out of additional data to process. Ruby offers all kinds of ways to go around and around.

while and until

The while and until methods let you create loops that run until the specified condition is true (while) or false (until). Both of these take a do...end block that will be run until the loop decides to stop. A simple example that demonstrates this is counting. With while, counting from 1 to 10 might look like:

def index
  count=1
  @result =' '
  while count <= 10 do
    @result = @result + count.to_s + " "
    count= count + 1
  end
end

The first time through the loop, count starts out with a value of one, and the condition count <= 10 evaluates to true, so Ruby proceeds into the loop. The string value of count gets tacked onto the end of @result, with a space for clarity, and then the value of count is increased by one. When the end corresponding to the do is reached, the loop goes back to its start at while and evaluates the condition. If the condition is still true, it goes through the loop again; if not, it ends the loop and goes forward. In this case it hits the end at the end of the index method, and we’re done. The view reports @result, which is “1 2 3 4 5 6 7 8 9 10.”

Note

The to_s method on count converts its numeric value to a string. The to_s method is a general facility for turning Ruby objects into strings. You may want to support this in your own programming, as it is often easier to see the state of something when it can be expressed as a string.

You could write the same thing with until, except that the condition would be reversed:

def index
  count=1
  @result =' '
  until count > 10 do
    @result = @result + count.to_s + " "
    count= count + 1
  end
end

You will doubtless have more exciting conditions than incrementing variables, but remember: Rails can do many things for you, but it won’t protect you from an infinite loop. If your conditions aren’t met (or refused for until), your code will go on and on until you halt it or it runs out of resources. Always make sure that the loop will come to a halt by itself, no matter what you feed it.

Just Counting

If you know how many times you want something to go around in a loop, you can use the times method on any numeric variable. times takes a block, marked with {}, which it will run that many times. For example:

def index
  count=3
  @result =''
  count.times {
    @result = @result + "count "
  }
end

will produce "count count count" as the loop goes around three times.

for

A for loop takes a variable and a collection. In its simplest counting approach, the collection is a range, specified with a starting value, then two periods (..), and then an end value. The variable will be set to a value from the range as the loop proceeds, and will advance one step every time the loop hits end until it’s done:

def index
  count=13
  @result =' '
  for i in 1..count
    @result = @result + i.to_s + " "
  end
end

Of course, like most things Ruby, the for loop has greater powers than just this. You can use it to iterate over an array:

def index
my_array= [5, 4, 3, 2, 1]
@result =' '
  for i in myArray
    @result = @result + i.to_s + " "
  end
end

The loop will go through the array to produce “5 4 3 2 1.” You can do even fancier things with hashes, extracting both the key and the value:

def index
my_hash= { 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4 }
@result =' '
  for key,value in my_hash
   @result = @result + "key: " + key + " - value: " + value.to_s + "<br />"
  end
end

As always, don’t expect the hash to be reported in any given order. Ruby reserves the right to present hashes however it wants. You’ll see a result something like:

key: three - value: 3
key: two - value: 2
key: one - value: 1
key: four - value: 4

These are a few of the simpler ways to use loops in Ruby. There’s much more to explore.

Many More Possibilities

Ruby offers, and Rails can use, a variety of other structures for passing control through a program:

  • return, break, next, and redo statements for moving through or from loops

  • throw and catch statements for breaking out of code

  • Iterators that go beyond loops

  • raise, rescue, retry, and ensure statements for exceptions

Rails doesn’t allow the use of Ruby’s BEGIN and END statements, however, or its support for threads.



[2] Sometimes this is called “duck typing” because when Ruby processes information, “if it looks like a duck and quacks like a duck, it’s a duck.”

If you enjoyed this excerpt, buy a copy of Learning Rails .