In 1993, Yukihiro “Matz” Matsumoto combined parts of his favorite languages (Perl, Smalltalk, Eiffel, Ada, and Lisp) to create his own ideal language, which he called Ruby.
Ruby is a dynamic, object-oriented programming language that also supports imperative and functional programming styles. It focuses on simplicity, productivity, and developer happiness. The Ruby website refers to it as “A Programmer’s Best Friend,” and developers with experience in other languages usually find Ruby easy to write and natural to read.
A solid foundation in Ruby is essential to understanding Ruby on Rails, so I’ll cover Ruby fundamentals in this chapter. As we progress through the language features, I’ll demonstrate common idioms used by experienced Ruby developers, so you can use them in your own programs later.
My favorite way to explore the Ruby language is through the Interactive Ruby interpreter (IRB). Most of the time, I develop applications in a text editor, but I still keep an IRB session open to test ideas quickly.
To start IRB, open a terminal (or command prompt on Windows), type irb
, and press ENTER. You should see a prompt similar to this:
irb(main):001:0>
If you see an error message after entering irb
, then you probably
don’t have it installed. Check out the Introduction, and follow the Ruby
installation instructions to get IRB set up.
IRB is a type of program called a read-eval-print loop (REPL).
IRB reads your input, evaluates it, and displays the result. It repeats this process until
you press CTRL-D or enter quit
or
exit
.
Try out IRB by typing a few words surrounded by quotation marks:
irb(main):001:0> "Hello, Ruby"
=> "Hello, Ruby"
Ruby evaluates the expression you entered and displays the result. A simple string
evaluates to itself, but this isn’t the same as printing the string. To output
something on the screen, use the Ruby method puts
, as shown
here:
irb(main):002:0> puts "Hello, Ruby"
Hello, Ruby
=> nil
Now Ruby outputs the string to the screen and displays nil
, which
is the result of evaluating the puts
method. In Ruby, every method
returns something. The puts
method doesn’t have anything useful
to return, so it returns nil
.
As you work through the rest of this chapter, you’ll find more examples that you can enter into IRB. I encourage you to try them out and explore what you can do with IRB and Ruby.
If IRB stops evaluating what you’re typing, you may have “confused” it by forgetting a closing quotation mark or some other syntax it was expecting. If this happens, press CTRL-C to cancel the current operation and return to a working prompt.
Now, let’s take a look at the data types available in Ruby.
Ruby has six main data types: number, string, symbol, array, hash, and Boolean. In this section, I’ll briefly discuss each of these data types and how to use them.
Ruby supports the math operations you learned in school, plus a few you may not have seen before. Type an expression into IRB and press ENTER to see the result:
irb(main):003:0> 1 + 1
=> 2
We asked Ruby to evaluate the expression 1 + 1, and it responded with the result, which is 2. Try out a few more math operations. Everything should work as expected, at least until you try division, as shown here:
irb(main):004:0> 7 / 3
=> 2
Ruby performs integer division by default. In other words, it
drops the remainder. You can find that remainder with the modulus operator
(%
). If you’d rather get a fractional answer, however, you
need to tell Ruby explicitly to use floating-point math by including a decimal point and
zero after at least one of the numbers. Here, you can see examples of both the modulus
operator and floating-point division in IRB:
irb(main):005:0> 7 % 3 => 1 irb(main):006:0> 7.0 / 3 => 2.3333333333333335
This concept is important to understand: although these appear to be simple math operators, they are actually methods in Ruby. You can even call methods on data types that other languages consider primitives.
irb(main):007:0> 1.odd?
=> true
Here, we ask the number 1 if it is odd and IRB responds with
true
.
You can create strings by surrounding characters with single or double quotes, as in this example:
irb(main):008:0> 'A String!'
=> "A String!"
You can also combine strings in Ruby to create larger ones. The language understands both adding strings and multiplying a string by a number. Let’s look at an example of each:
irb(main):009:0> "Hello" + "World" => "HelloWorld" irb(main):010:0> "Hi" * 3 => "HiHiHi"
Notice that Ruby doesn’t automatically put spaces between words when adding or multiplying. You are responsible for that detail.
Until now, I haven’t differentiated between single- and double-quoted strings,
but double-quoted strings actually allow you to combine strings in more complex ways.
For example, they support a feature called string interpolation, in
which Ruby evaluates an expression surrounded by #{
and
}
, converts the result to a string, and inserts it into the string
automatically, as shown here:
irb(main):011:0> x = 10 => 10 irb(main):012:0> "x is #{x}" => "x is 10"
In this case, #{x}
evaluates to 10, so Ruby converts the number
10 to a string and returns "x is 10"
.
Double-quoted strings also support special characters such as newlines and tabs.
These special characters consist of a backslash followed by a letter. Type
to create a newline (shown next) or
to create a tab. To add a
literal backslash in a double-quoted string, type two backslashes.
irb(main):013:0> puts "Line one
Line two"
Line one
Line two
=> nil
You’ve already seen a few string methods, but many others are handy, including
length
and empty?
. (Yes, methods in Ruby can end
with question marks and even exclamation marks.) Let’s look at those two methods
in action:
irb(main):014:0> "Hello".length => 5 irb(main):015:0> "Hello".empty? => false
The length
method returns the number of characters in a
string, whereas empty?
tells you whether a string contains any
characters.
Ruby has a data type not often seen in other programming languages, and that’s the symbol. Symbols are similar to strings in that they are made of characters, but instead of being surrounded by quotes, symbols are prefixed with a colon, like this:
irb(main):016:0> :name
=> :name
Symbols are typically used as identifiers. They are created only once and are
unique. This means they are easy for programmers to read as a string, but also memory
efficient. You can see this for yourself by creating a few strings and symbols and then
calling the object_id
method on them.
irb(main):017:0> "name".object_id => 70156617860420 irb(main):018:0> "name".object_id => 70156617844900 irb(main):019:0> :name.object_id => 67368 irb(main):020:0> :name.object_id => 67368
Notice that the two strings here have the same content, but different object ids. These are two different objects. The two symbols have the same content and the same object id.
When Ruby compares two strings for equality, it checks each individual character. Comparing two symbols for equality requires only a numeric comparison, which is much more efficient.
An array represents a list of objects in Ruby. You create an array by surrounding a list of objects with square brackets. For example, let’s make an array of numbers:
irb(main):021:0> list = [1, 2, 3]
=> [1, 2, 3]
Ruby arrays can contain any kind of object, even other arrays. You can
access individual elements of an array by passing a numeric index to the array’s
[]
method. The first element is at index zero. Try examining the
first element in the array just created:
irb(main):022:0> list[0]
=> 1
Entering list[0]
tells Ruby to fetch the first number in the
array, and the method returns 1
.
You can also pass two numbers to the []
method to create an array
slice, as shown next. The first number you provide specifies the
starting index, whereas the second tells it how many elements you want in your array
slice:
irb(main):023:0> list[0, 2]
=> [1, 2]
Here, the []
method starts at index zero and returns the first
two numbers in list
.
Like strings, you can also add arrays to create a new one using the
+
operator. If you just want to add elements to the end of an
existing array, you can use the <<
operator. You can see an
example of each operation here:
irb(main):024:0> list + [4, 5, 6] => [1, 2, 3, 4, 5, 6] irb(main):025:0> list << 4 => [1, 2, 3, 4]
Though the +
operator returns a new array, it doesn’t
modify the existing array. The <<
operator does modify the
existing array. You can also use an index to reassign an existing element or add a new
element to the array.
A hash is a set of key-value pairs. In Ruby, hashes are enclosed in curly braces.
Unlike an array index, a hash key can be of any data type. For example, symbols are
frequently used as hash keys. When you need to access a value in a hash, just pass the
corresponding key to the []
method, as shown next. Attempting to
access a key that does not exist returns nil
.
irb(main):026:0> some_guy = { :name => "Tony", :age => 21 } => {:name=>"Tony", :age=>21} irb(main):027:0> some_guy[:name] => "Tony"
The combination of an equal sign and a greater-than sign (=>
)
between the key and value is commonly referred to as a hash rocket.
Because symbols are used as hash keys so often, Ruby 1.9 added a shorthand syntax
specifically for them. You can take the colon from the front of the symbol, put it at
the end, and then leave out the hash rocket. Here’s an example:
irb(main):028:0> another_guy = { name: "Ben", age: 20 }
=> {:name=>"Ben", :age=>20}
Although you can create a hash with this shorthand, Ruby seems to be sentimental as it still uses the old syntax when displaying the hash.
You can also use the keys
method to get an array of all keys in a
hash. If you need an array of all the values in the hash, use the method
values
instead. The code here shows an example of each method,
using the same hash just created:
irb(main):029:0> another_guy.keys => [:name, :age] irb(main):030:0> another_guy.values => ["Ben", 20]
Hashes are frequently used to represent data structures, as in these examples. They are also sometimes used to pass named parameters to a method. If a hash is the last (or only) argument to a method call, you can even leave off the curly braces.
For example, the merge
method combines two hashes. The code here
merges the hash named another_guy
with a new hash containing
{ job: "none" }
.
irb(main):031:0> another_guy.merge job: "none"
=> {:name=>"Ben", :age=>20, :job=>"none"}
Because the only argument to this method call is the new hash, you can leave off the curly braces. Rails has many other examples of this type of method call.
A Boolean expression is anything that evaluates to true or false. These expressions
often involve a Boolean operator, and Ruby supports familiar operators including
less than (<
), greater
than (>
), equal
(==
), and not equal (!=
).
Try these Boolean expressions at the IRB prompt:
irb(main):032:0> 1 < 2 => true irb(main):033:0> 5 == 6 => false
Ruby also provides and (&&
) and
or (||
) operators for combining multiple
Boolean expressions, as shown next:
irb(main):034:0> 1 < 2 || 1 > 2 => true irb(main):035:0> 5 != 6 && 5 == 5 => true
Both of these operators short circuit. That is,
&&
is only true if the expressions on both sides evaluate
to true. If the first expression is false, then the second expression is not evaluated.
Likewise, ||
is true if either expression is true. If the first
expression is true, then the second expression is not evaluated.
The ||
operator is also sometimes used with assignment. You might
do this when you want to initialize a variable only if it is currently
nil
and keep the current value otherwise. Ruby provides the
||=
operator for this case. This is referred to as
conditional assignment, and you can see an example here:
irb(main):036:0> x = nil => nil irb(main):037:0> x ||= 6 => 6
If the variable x
had not been a false value, then the
conditional assignment would have returned the value of x
instead of
setting it to 6.
A constant gives a name to a value that doesn’t change. In Ruby, the name of a constant must begin with a capital letter. Constants are typically written in uppercase, like this one:
irb(main):038:0> PI = 3.14 => 3.14 irb(main):039:0> 2 * PI => 6.28
Ruby won’t actually stop you from assigning a new value to a constant, but it does display a warning if you do.
In Ruby, you don’t need to declare a variable in advance or specify a type. Just assign a value to a name as shown here:
irb(main):040:0> x = 10
=> 10
The variable x
now refers to the number 10. Variable names
are typically written in snake case, that is, all lowercase with
underscores between words.
irb(main):041:0> first_name = "Matthew"
=> "Matthew"
Variable names can include letters, numbers, and underscores, but they must start with either a letter or underscore.
The examples we’ve looked at so far have all been linear. Real programs usually include statements that only execute when a certain condition is met and statements that are repeated multiple times. In this section, I cover Ruby’s conditional statements and iteration.
Conditional statements let your program choose between one or
more branches of code to execute based on an expression you provide. As such, making a
decision in code is also called branching. For example, the
following conditional prints the word Child only if the expression
age < 13
evaluates to true.
irb(main):042:0> age = 21 => 21 irb(main):043:0> if age < 13 irb(main):044:1> puts "Child" irb(main):045:1> end => nil
The variable age
is set to 21, so age < 13
will evaluate to false
, and nothing will be printed.
You can also use elsif
and else
to make more
complicated conditionals. Let’s look at a code example that has to check multiple
conditions:
irb(main):046:0> if age < 13 irb(main):047:1> puts "Child" irb(main):048:1> elsif age < 18 irb(main):049:1> puts "Teen" irb(main):050:1> else irb(main):051:1> puts "Adult" irb(main):052:1> end Adult => nil
This code can take three different branches depending on the value of
age
. In our case, it should skip the code inside the
if
and elsif
statements and just print
Adult.
All of the previous conditional examples checked for true expressions, but
what if you want to execute a block of code when an expression is false instead? Like
other languages, Ruby has a logical not operator (either
not
or !
), which is useful here. The following
example will print the value of name
if it is not an empty
string.
irb(main):053:0> name = "Tony" => "Tony" irb(main):054:0> if !name.empty? irb(main):055:1> puts name irb(main):056:1> end => nil
When name.empty?
is false, the !
operator
should reverse the result to true so the code inside the if
statement
executes. A more natural way to say this conditional might be “unless
name
is empty, print its value.” Unlike an
if
statement, Ruby’s unless
statement
executes code when the expression evaluates to false.
irb(main):057:0> name = "Tony" => "" irb(main):058:0> unless name.empty? irb(main):059:1> puts name irb(main):060:1> end => nil
That still seems a little wordy to me. For one-line expressions such as this, Ruby lets you put the conditional at the end of the line:
irb(main):061:0> name = "Tony" => "" irb(main):062:0> puts name unless name.empty? => nil
This example is concise and readable. To me, this code says “print
name
unless it’s empty.” This code is also a great
example of Ruby’s flexibility. You can write conditional expressions using the
style that makes the most sense to you.
When you’re working with a collection of objects, such as an array or hash,
you’ll frequently want to perform operations on each item. In addition to the
for
loops seen in other languages, Ruby collections provide the
each
method.
The each
method accepts a block of code and executes it
for every element in the collection. A block in Ruby usually starts with the word
do
and ends with the word end
. A block can also
accept one or more parameters, which are listed inside a pair of pipe characters. The
each
method returns the value of the entire collection.
This next example iterates over each element in the array list
,
which we created earlier in this chapter as [1, 2, 3, 4]. It assigns the element to the
variable number
and then prints the value of
number
.
irb(main):063:0> list.each do |number| irb(main):064:1> puts number irb(main):065:1> end 1 2 3 4 => [1, 2, 3, 4]
Simple blocks like this are often written on one line in Ruby. Instead of writing
do
and end
to indicate a block, you can use
opening and closing curly braces, which are common in one-line blocks. Like the previous
example, this one iterates over the list and prints each element, but it does everything
in a single line of code.
irb(main):066:0> list.each { |n| puts n }
1
2
3
4
=> [1, 2, 3, 4]
You can also use the each
method to iterate over a hash. Because
a hash is a collection of key-value pairs, the block will take two parameters.
Let’s try using each
with one of our earlier hashes:
irb(main):067:0> some_guy.each do |key, value| irb(main):068:1> puts "The #{key} is #{value}." irb(main):069:1> end The name is Tony. The age is 21. => {:name=>"Tony", :age=>21}
Blocks are useful for more than just iteration. Any method can potentially accept a
block and use the code it contains. For example, you can pass a block to the
File.open
method. Ruby should pass the file handle as a variable to
the block, execute the code within the block, and then close the file
automatically.
A method is a named block of reusable code. Defining your
own methods in Ruby is simple. A method definition starts with the word
def
, followed by a name, and continues until end
.
This method will print “Hello, World!” each time it is called:
irb(main):070:0> def hello irb(main):071:1> puts "Hello, World!" irb(main):072:1> end => nil
As you can see in the example, a method definition should return
nil
.
Once you’ve defined a method, you can call it by entering its name at the IRB prompt:
irb(main):073:0> hello
Hello, World!
=> nil
Ruby methods always return the value of their last statement; in this case, the last
statement was puts
, which returns nil
. You can use
return
to return a value explicitly, or you can just add the value
you wish to return as the last line of the method.
For example, if you want the hello
method to return
true
, you can modify it like this:
irb(main):074:0> def hello irb(main):075:1> puts "Hello, World!" irb(main):076:1> true irb(main):077:1> end => nil
Now call the method as before:
irb(main):078:0> hello
Hello, World!
=> true
Because the last line of the method is the value true
, the method
returns true
when called.
In Ruby, you specify method parameters by adding them after the method name, optionally enclosed in parentheses, as shown in the next example. Parameters can also have default values.
irb(main):079:0> def hello(name = "World") irb(main):080:1> puts "Hello, #{name}!" irb(main):081:1> end => nil
This example redefines the hello
method to accept a
parameter called name
. This parameter has a default value of
"World"
. This method can be called as before to display “Hello,
World!”, or you can pass a value for the name
parameter to greet
someone else.
irb(main):082:0> hello Hello, World! => nil irb(main):083:0> hello "Tony" Hello, Tony! => nil
The parentheses around method arguments are also optional. Include them if the intention is not clear; otherwise, feel free to omit them.
In an object-oriented programming language such as Ruby, a class
represents the state and behavior of a distinct type of object. In Ruby, an object’s
state is stored in instance variables, and methods define its behavior. A Ruby class
definition starts with the word class
, followed by a capitalized name,
and continues to the matching end
.
Class definitions can include a special method called initialize
.
This method is called when a new instance of the class is created. It is typically used to
assign values to the instance variables needed by the class. In Ruby, instance variables
start with an @
, as shown in the following class definition:
irb(main):084:0> class Person irb(main):085:1> def initialize(name) irb(main):086:2> @name = name irb(main):087:2> end irb(main):088:1> def greet irb(main):089:2> puts "Hi, I'm #{@name}." irb(main):090:2> end irb(main):091:1> end => nil
This code defines a new class called Person
. The
initialize
method takes one parameter and assigns the value of that
parameter to the instance variable @name
. The greet
method prints a friendly greeting. Let’s write some code that uses this new
class.
irb(main):092:0> person = Person.new("Tony") => #<Person:0x007fc98418d710 @name="Tony"> irb(main):093:0> person.greet Hi, I'm Tony. => nil
You can create an instance of the Person
class by calling
Person.new
and passing the required parameters. The previous example
creates an instance of Person
with the name Tony.
The return value of Person.new
is a string representation of the
object. It consists of the class name followed by a reference to the object in memory and
a list of instance variables. Calling the greet
method should display
the friendly greeting we expect.
Instance variables, like @name
, are not accessible outside of the
class. Try to access person.name
from the IRB prompt, and you should
see an error.
irb(main):094:0> person.name
NoMethodError: undefined method 'name'
If you need to access or change @name
outside of the class, you
need to write a getter and a setter. These are
methods that get or set the value of an instance
variable. Fortunately, Ruby classes provide the method attr_accessor
,
which writes getters and setters for you.
You would normally include attr_accessor :name
in your definition
of the Person
class. Rather than retype the entire class definition,
you can reopen the class and add this line:
irb(main):095:0> class Person irb(main):096:1> attr_accessor :name irb(main):097:1> end => nil
This code adds the attr_accessor
call to the
Person
class and updates all objects of the class automatically. And
this is another example of the Ruby’s flexibility. You can reopen a class, even at
runtime, and add new methods as needed.
Now, if we want to change the name of our person, we can just set it equal to something else, as shown here:
irb(main):098:0> person.name => "Tony" irb(main):099:0> person.name = "Wyatt" => "Wyatt" irb(main):100:0> person.greet Hi, I'm Wyatt. => nil
The attr_accessor
method uses the symbol
:name
to define the getter name
and the setter
name=
. You can now get and set the value of the instance variable as
needed. If you only want a getter, include a call to attr_reader
instead of attr_accessor
. Doing this lets you read the value of
@name
, but not change it.
The attr_accessor
method is different from the methods I’ve
discussed so far. Note that attr_accessor
is called inside the body
of the class definition. The methods you’ve seen so far, such as the
greet
method, are called on an instance of a class.
In Ruby, methods called on an instance of a class are called instance
methods. Methods called on the class itself are called class
methods. Another example of a class method is new
. When
you typed Person.new("Tony")
before, you were calling the class
method new
of the class Person
.
In Ruby, you can define a new class that builds on the state and behavior of an
existing class, and the new class will inherit variables and methods from the existing
one. Inheritance defines an is-a relationship
between those two classes. For example, a student is a person. We can define the class
Student
like this:
irb(main):101:0> class Student < Person irb(main):102:1> def study irb(main):103:2> puts "ZzzzZzzz" irb(main):104:2> end irb(main):105:1> end => nil
The < Person
on the first line indicates that the
Student
class inherits from the Person
class.
The variables and methods defined by Person
are now available to
Student
:
irb(main):106:0> student = Student.new("Matt") #<Student:0x007fd7c3ac4d90 @name="Matt"> ➊ irb(main):107:0> student.greet Hi, I'm Matt. => nil irb(main):108:0> student.study ZzzzzZzzzz => nil
Because we created greet
on Person
earlier in
the chapter, we can have any Student
call this method ➊
without defining it in our new class.
Ruby only supports single inheritance, which means that one class can’t inherit from multiple classes at the same time. You can, however, work around this limitation by using modules. A module is a collection of methods and constants that cannot be instantiated but can be included in other classes to provide additional behavior. We discuss modules and other advanced features of Ruby in Chapter 7.
You are now well on your way to becoming a great Ruby on Rails programmer. The Ruby knowledge you gained in this chapter will make understanding the Rails framework much easier.
I recommend working with IRB as much as you need to feel comfortable with Ruby. When
you’re ready to start exploring Rails, enter exit
to leave IRB,
and continue on to Chapter 2.
3.137.174.23