© Brady Somerville, Adam Gamble, Cloves Carneiro Jr and Rida Al Barazi 2020
B. Somerville et al.Beginning Rails 6https://doi.org/10.1007/978-1-4842-5716-6_4

4. Introduction to the Ruby Language

Brady Somerville1 , Adam Gamble2, Cloves Carneiro Jr.3 and Rida Al Barazi4
(1)
Bowling Green, KY, USA
(2)
Gardendale, AL, USA
(3)
Hollywood, FL, USA
(4)
FONTHILL, ON, Canada
 

Rails is a great framework for the development of web-based applications. One of its greatest advantages over other web frameworks is that it’s written in Ruby, a very consistent and elegant object-oriented programming language. In order to increase your productivity as a Rails developer, it’s important that you master Ruby. If you’re new to programming, don’t worry: we explain the concepts in a way you can understand.

Ruby was made to make developers happy. This should be exciting to you because you’re a developer, and you want to be happy! Some languages feel like the creator was in a bad mood and hated you. Ruby tries its best to make you feel at ease and in control. As you grow as a developer, you’ll understand the importance of this fact more and more, especially if you do this for a living.

This chapter gives you an overview of the features of the Ruby language. It explains how the language is organized and presents its fundamentals. After reading this chapter, you should better understand how the Ruby language that Rails is built on works, and you should be able to create classes and methods and use control-flow statements in your code. The best way to learn is to explore the language using this chapter as a guide. It’s important that you run the examples given yourself and also to try things on your own.

Ruby has far more features than we can mention in this short introduction. We encourage you to investigate more of the complex features of Ruby as you continue using Rails.

Instant Interaction

A lot of languages require that you write some code, compile, and then run the program to see the results. However, Ruby is dynamic, which means you can work with the language live. You will get instant feedback from your commands.

Ruby comes with a great little tool: an interactive interpreter called irb (for Interactive Ruby). You can start up an irb session whenever you want by typing irb at the command prompt. Using irb, you can play around with code and make sure it works as you expect before you write it into your programs.

You can execute any arbitrary Ruby code in irb and do anything you would otherwise do inside your Ruby programs: set variables, evaluate conditions, and inspect objects. The only essential difference between an interactive session and a regular old Ruby program is that irb echoes the return value of everything it executes. This saves you from having to explicitly print the results of an evaluation. Just run the code, and irb prints the result.

You can tell when you’re in an irb session by looking for the irb prompt, which looks like irb(main):001:0>, and the arrow symbol (=>), which indicates the response.

To start an irb session, go to the command prompt and type irb. You should see the irb prompt waiting for your input:
$ irb
irb(main):001:0>

Look at that. You’re inside Ruby! If you press Enter, Ruby ignores the line and gives you another prompt, but it ends with an asterisk instead of the greater-than sign to indicate that Ruby is expecting something from you to execute. It can only get more exciting from here.

Note

Your irb prompt might look slightly different depending on your version of Ruby and your computer environment. This is perfectly okay.

When learning a new programming language, traditionally, the first thing you do is make the language print the string “Hello, World!” Let’s go ahead and do that. Type the following after the irb prompt:
irb(main):001:0>  "Hello, World!"
=> "Hello, World!"

Excellent. You just wrote your first bit of Ruby! Some languages require many more lines of code to write the Hello, World! application, but in Ruby it only took one. One of the ways Ruby makes developers happy is by being concise. One line is certainly concise, wouldn’t you say?

So what exactly happened here? Well, first, you created a string with the content “Hello, World!” The irb command always outputs the value of the last command to the screen; thus, you have “Hello, World!” written to the screen. You will notice as you type valid Ruby commands and press Enter that irb will continue to output the value of those commands. Try adding two numbers together:
irb(main):001:0>  1 + 1
=> 2
Now let’s try something a little more difficult. Let’s ask Ruby for the current time:
irb(main):001:0>  Time.now
=> 2020-01-20 19:38:17 -0600
So Ruby dutifully reported the current time to us, including the date no less. What if you just wanted the current year?
irb(main):001:0>  Time.now.year
=> 2020

You can see how easy and concise Ruby is. The code is simple and almost reads like an English sentence. If you’re wanting a description of exactly what you did in the last two examples, here it is: You called a method (now) on a class (Time). In the second example, you chained another method call onto the previous one. We’ll cover this in depth later, but first let’s talk about data types.

Ruby Data Types

A data type is a constraint placed on the interpretation of data. Numbers and strings are just two of the data types the Ruby interpreter distinguishes among, and the way Ruby adds numbers is different from the way in which it adds strings. For example, 2 + 3 evaluates to 5, but “2” + “3” evaluates to “23”. The second example may seem surprising at first, but it’s simple: anything, including numbers, surrounded by quotes is interpreted as a string. Read on to find out more.

Strings

A string is a sequence of characters that usually represents a word or some other form of text. In Ruby, you can create String objects by putting the characters inside single or double quotation marks:
irb(main):001:0>  'Ruby is a great language'
=> "Ruby is a great language"
irb(main):002:0>  "Rails is a great framework"
=> "Rails is a great framework"
The main difference between strings delimited by single and double quotes is that the latter are subject to substitutions. Those substitutions are identified by Ruby code inside the #{} construct, which is evaluated and replaced by its result in the final String object. The technical term for this technique is string interpolation:
irb(main):003:0>  "Now is #{Time.now}"
=> Now is 2020-01-20 19:39:45 -0600"
irb(main):004:0>  'Now is #{Time.now}'
=> "Now is \#{Time.now}"
Note

In general, most developers only use double quotes when using string interpolation or if the actual string includes single quotes. This is technically faster, if only slightly.

When you use the hash symbol (#) with the curly braces, Ruby notices and tries to evaluate whatever is between the braces. To evaluate means to process it like any other code. So, inside the braces, you say Time.now, which returns the current time. However, when you use single quotes, Ruby doesn’t check the string for substitutions before sending it through.

The String class has a large number of methods you need when doing string manipulation, like concatenation and case-changing operations. The following examples list a few of those methods:
irb(main):005:0>  "Toronto - Canada".downcase
=> "toronto - canada"
irb(main):006:0>  "New York, USA".upcase
=> "NEW YORK, USA"
irb(main):007:0>  "a " + "few " + "strings " + "together"
=> "a few strings together"
irb(main):008:0>  "HELLO".capitalize
=> "Hello"
Tip

To get a list of methods available for any object, call the "methods" method using an instance of the object you want to inspect. Type "a string".methods in irb to see all the methods you can call on the String object. If you want to find a certain method, try using grep on that method too. For example, typing "a string".methods.grep /case/ shows all string methods containing the word case. Other examples would be

4.methods

["some", "array", "elements"].methods

Numbers

Ruby has a couple of classes to represent numbers: Integer and Float. Integer represents whole numbers, while Float objects represent real numbers, meaning numbers with a fractional part. As in most programming languages, you can perform basic arithmetic operations in Ruby as you would using a calculator:
irb(main):001:0>  1 + 2
=> 3
irb(main):002:0>  2323 + 34545
=> 36868
irb(main):003:0>  9093 - 23236
=> -14143
irb(main):004:0>  343 / 4564
=> 0
irb(main):005:0>  3434 / 53
=> 64
irb(main):006:0>  99 * 345
=> 34155
irb(main):007:0>  34545.6 / 3434.1
=> 10.059578928977025
Note

Notice that when whole numbers are divided, the result is always a whole number even if there is a remainder. If one of the numbers is a decimal, then a decimal will always be returned:

irb(main):001:0> 6/4
=> 1
irb(main):002:0> 6/4.0
=> 1.5

Symbols

Symbols aren’t a common feature in most languages. However, as you’ll learn when reading this book, they’re extremely useful. Symbol is a data type that starts with a colon, like :controller. Symbols are objects that work just like any other object in Ruby. They’re used to point to some data that isn’t a traditional String object, in a human-readable format. In fact, they’re almost like strings, except you can’t modify them:
irb(main):001:0>  :my_symbol
=> :my_symbol
irb(main):002:0>  :my_symbol + :second
Traceback (most recent call last):
      ...
      1: from (irb):22
NoMethodError (undefined method `+' for :my_symbol:Symbol)
irb(main):003:0>  "my_string" + "second"
=> "my_stringsecond"

Fancy computer science types refer to this condition as being immutable, which really just means you can’t modify something. Use symbols when you want to name something nicely and you don’t want it changed at all—for example, by having something appended to the end of it. There are also memory advantages to using symbols, but that is out of the scope of this book. The importance of symbols will become clear as you use Ruby more.

Arrays and Hashes

Sometimes you have a lot of data that you need to keep track of—maybe a list of students, users, or anything that you may keep in a collection. Ruby has two different types of container objects for storing collections: arrays and hashes.

Arrays are part of almost every modern language. They keep information in order. You can ask for the first item or the last item or put items in a certain order. You can think of an Array object as a long series of boxes in which you can put things. You define arrays by using the [] notation. Note that in most programming languages, including Ruby, arrays are 0 indexed. This means you always refer to the first element in an array as 0. Read carefully what happens here:
irb(main):001:0>  city_array = ['Toronto', 'Miami', 'Paris']
=> ["Toronto", "Miami", "Paris"]
irb(main):002:0>  city_array[0]
=> "Toronto"
irb(main):003:0>  city_array[1] = 'New York'
=> "New York"
irb(main):004:0>  city_array << 'London'
=> ["Toronto", "New York", "Paris", "London"]
irb(main):004:0>  city_array + ["Los Angeles"]
=> ["Toronto", "New York", "Paris", "London", "Los Angeles"]

In the first example, we created the array of cities and assigned it to the variable named city_array. In the second example, we referenced the city array and asked for the object at the index position 0 (remember, with arrays, 0 is the first index). “Toronto” is returned. In the third example, we are replacing the object at index 1 with the string “New York.” Notice in the next example when the array is printed to the screen, Miami is no longer in the list but has been replaced. The fourth example uses what is commonly called the shovel operator. Simply put, this just adds the object to the end of the array. So we added the string “London” to the end of our array. Finally, in the last array, we added the array that contains “Los Angeles” to our previous array. This returns a new single dimensional array with the contents of both arrays. Arrays are extremely common and useful in Ruby.

The Hash object offers another way to keep a collection. Hashes are different from arrays, because they store items using a key. Hash objects preserve order, just like arrays, which enables you to call certain methods on them—for example, hash.first to get the first key-value pair. In Ruby, you often use symbols for hash keys, but in reality, any object can function as a key.

You define hashes with curly braces, {}. You can create a Hash object by defining it with {key: "value", other_key: "other value" }. Then, you can pull out data by using square brackets on the end of the list. For instance, you retrieve a value by typing my_hash[:key] from the my_hash variable. Here are some examples:
irb(main):005:0> my_hash = {canada: 'Toronto', france: 'Paris', uk: 'London'}
=> {:canada=>"Toronto", :france=>"Paris", :uk=>"London"}

Notice how the return value doesn’t quite look like what you typed in—the format of the hash changed. What you typed in is referred to as JavaScript Object Notation (JSON) style, whereas the format shown in the return value is referred to as the Hashrocket style. (See the rockets in the hash?) In a sense, the two styles are equivalent. Many prefer the more compact JSON-style hash, though it has some limitations; with JSON-style hashes, the keys must be symbols, whereas Hashrocket supports any object as a key. You will see both styles regularly used.

We’ve created a hash and assigned it to the “my_hash” variable. In this example, the keys of our array are countries, and the values are cities. To reference a specific value of a hash, you pass the hash a key, and it will return the value to you:
irb(main):006:0>  my_hash[:uk]
=> "London"
We’ve passed the hash a key of :uk, and it returned the value of “London.”
irb(main):007:0>  my_hash[:canada] = 'Calgary'
=> "Calgary"
This is the same idea, but here we’re changing the value out for the key Canada. So the value of “Toronto” goes away and is replaced by “Calgary.”
irb(main):008:0>  my_hash.first
=> [:canada, "Calgary"]
In this example, we use the first method, which returns the first key-value pair. Notice in this case the return value is an array. The first element in the array is the key, and the second is the value. The keys method will return an array of all the keys contained in the hash. Here is an example:
irb(main):010:0> my_hash.keys
=> [:canada, :france, :uk]
It is important to note that in all of our examples, we have assigned strings to different positions to both our hashes and arrays, but any object could be stored in an array or hash. For instance, you might want to store numbers or even another array or hash. The possibilities are unlimited:
irb(main):001:0> numbers_array = [1, 2, 3, 4, 5]
=> [1,2,3,4,5]
irb(main):002:0> numbers_hash = {one: 1, two: 2, three: 3}
=> {:one => 1, :two => 2, :three => 3}

Language Basics

Like other programming languages, Ruby includes variables, operators, control-flow statements, and methods. This section shows you how to use them.

Variables

Variables are used to hold values you want to keep for later processing. When you perform a calculation, you probably want to use the result of that calculation somewhere else in your application code, and that’s when you need a variable. In Ruby, variables are easily created. You just need to give a variable a name and assign a value to it; there’s no need to specify a data type for the variable or define it in your code before you use it.

Let’s create a few variables to hold some values you may need later. Notice that you can reuse a variable name by reassigning a value:
irb(main):001:0>  test_variable = 'This is a string'
=> "This is a string"
irb(main):002:0>  test_variable = 2010
=> 2010
irb(main):003:0>  test_variable = 232.3
=> 232.3

You’ve created a variable named test_variable and assigned a few different values to it. Because everything in Ruby is an object, the test_variable variable holds a reference to the object you assigned.

Variable names can be any sequence of numbers and letters, as long as they start with a letter or an underscore; however, the first character of a variable indicates the type of the variable. Variables also have a scope, which is the context in which the variable is defined. Some variables are used in a small snippet of code and need to exist for only a short period of time; those are called local variables. Table 4-1 lists the different types of variables supported by Ruby and shows how to recognize them when you’re coding. Type some variable names in irb, and you’ll get results similar to those shown here.
Table 4-1

Ruby Variables

Example

Description

$user

Global variables start with $. Global variables are not constrained by any scope—they’re available anywhere. While this sounds convenient, global variables can also lead to bugs which are difficult to diagnose. Global variables should generally be avoided except in unique circumstances.

@@count

Class variables start with @@. Class variables exist in the scope of a class, so all instances of a specific class have a single value for the class variable.

@name

Instance variables start with @. Instance variables are unique to a given instance of a class.

SERVER_IP

You can create a constant in Ruby by capitalizing the first letter of a variable, but it’s a convention that constants are written in all uppercase characters. Constants are variables that don’t change throughout the execution of a program. In Ruby, constants can be reassigned; however, you get a warning from the interpreter if you do so.

my_string

Local variables start with a lowercase letter or an underscore, and they live for only a short period of time. They usually exist only inside the method or block of code where they’re first assigned.

In Ruby, it’s considered best practice to use long and descriptive variable names. For example, in Java, you may have a variable named phi; but in Ruby, you write out place_holder_variable for clarity. The basic idea is that code is much more readable if the person looking at it (probably you) doesn’t have to guess what phi stands for. This is extremely important when you come back to a piece of code after a year or so.

Operators

You can combine Ruby code using operators. Many classes implement operators as methods. Table 4-2 lists the most common operators and their functions.
Table 4-2

Ruby Operators

Operator

Description

[] []=

Assignment

* / % + **

Arithmetic

<= >= < >

Comparison

.. ...

Range

& ^ |

AND, exclusive OR, regular OR (bitwise)

|| && not or and

Logical operators

Ruby contains a ternary operator that you can use as a short notation for if-else-end. The ternary operator uses the form expression ? value_if_true : value_if_false:
a = 10
b = 20
a > b ? a : b
# => 20

In plain English, we’re saying if a is greater than b, then return a; otherwise, return b. The ternary operator is very concise but still easy to read.

Blocks and Iterators

Any method in Ruby can accept a code block—a fragment of code between curly braces or do..end constructs. It determines whether the method in question calls the given block. The block always appears immediately after the method call, with the start of the block coming on the same line as the method invocation.

Here’s an example using the times method; times executes the given code block once for each iteration. In this case, “Hello” is printed five times:
5.times { puts "Hello" }
Hello
Hello
Hello
Hello
Hello
If a method yields arguments to a block, the arguments are named between two pipe characters (|) on the same line as the method call. In the next example, the block receives one argument, item:
[1,2,3,4,5].each { |item| puts item }
1
2
3
4
5

Here, each number is yielded to the block in succession. You store the number in the block variable item and use puts to print it on its own line.

The convention is to use braces for single-line blocks and do..end for multiline blocks. Here’s an example similar to the previous one; it uses each_with_index, which yields the item and its index in the array:
["a", "b", "c"].each_with_index do |item, index|
  puts "Item:  #{item}"
  puts "Index: #{index}"
  puts "---"
end
Item:  a
Index: 0
---
Item:  b
Index: 1
---
Item:  c
Index: 2
---

Comments

Sometimes developers feel the need to annotate their code with information to help future developers understand some code. Such annotations are called comments . In Ruby, comments are most often identified by an unquoted #, and anything between the # and the end of the line of code is ignored by the Ruby interpreter. You can also use a # to “comment out” a line of code—essentially temporarily disabling it while developing or debugging. Here’s an example with both an “informative” comment and a commented-out line:
# This method creates a widget
def create_widget
  widget = Widget.new
  # widget.forge!
  widget
end

To make the best use of comments, avoid using them to state the obvious (like the preceding example). Instead, reserve comments for explaining difficult sections of code. (Or better yet, rewrite the code so it’s intuitively understood without comments.) Comments can be a liability when they add too much clutter to the code or when they’re not updated to accurately reflect changes to the code since they were first written.

Control Structures

In all of the previous examples, the Ruby interpreter executed the code from top to bottom. However, in the majority of cases, you want to control which methods are to be executed and when they should be executed. The statements you want to be executed may depend on many variables, such as the state of some computation or the user input. For that purpose, programming languages have control-flow statements , which allow you to execute code based on conditions. Here are a few examples of how to use if, else, elsif, unless, while, and end. Notice that control structures in Ruby are terminated using the end keyword:
now = Time.now
# => 2020-01-20 20:00:37 -0600
if now == Time.now
  puts "now is in the past"
elsif now > Time.now
  puts "nonsense"
else
  puts "time has passed"
end
# => time has passed

The first if statement will never trigger because there is a slight bit of time that passes between when you set the now variable and when you test it against Time.now. The second conditional won’t trigger because the now variable will obviously be in the past, if only slightly. The third conditional “else” will always trigger, because neither of the first two conditionals triggered.

A trick that makes simple conditionals easy to read is to place if and unless conditional statements at the end of a code line so they act as modifiers . Here’s how it looks:
a = 5
b = 10
puts "b is greater than a" if a < b
b is greater than a
puts "a is greater than b" unless a < b
nil

The unless structure was confusing for us at first. Once we started reading it as “if not,” it made sense. In the previous example, reading the statement as “puts ‘a is greater than b’ if not a < b” makes the most sense.

You can also use while statements, as in all major programming languages:
a = 5
b = 10
while a < b
  puts "a is #{a}"
  a += 1
end
a is 5
a is 6
a is 7
a is 8
a is 9

Methods

Methods are little programmable actions that you can define to help your development. Let’s leave irb for the moment and talk about pure Ruby code. (All of this also works if you type it into irb.)

Suppose that, several times in the application you’re writing, you need to get the current time as a string. To save yourself from having to retype Time.now.to_s over and over, you can build a method. Every method starts with def:
def time_as_string
  Time.now.to_s
end
Anywhere in the application that you want to get the time, you type time_as_string:
puts time_as_string
"2020-01-20 20:03:15 -0600”
See how easy that is? Obviously with this code, you didn’t do much, but methods can be much more complex. Methods can also take in variables:
def say_hello_to(name)
  "Hello, #{name}!"
end
puts say_hello_to("John")
"Hello, John!"

Here you defined a method named say_hello_to that accepts one argument name. That method uses string interpolation to return a string of “Hello, name that was passed to the method!” The puts then sends the response of that method to the screen.

Next, let’s look at how to put methods together into classes to make them really powerful.

Note

You already know that local variables must start with a lowercase letter and can’t contain any characters other than letters, numbers, and underscores. Method names are restricted to the same rules, which means they often look like variables. Keywords (like if, or, when, and, etc.) share the same set of properties. How does the Ruby interpreter know the difference? When Ruby encounters a word, it sees it as a local variable name, a method invocation, or a keyword. If it’s a keyword, then Ruby knows it and responds accordingly. If there’s an equals sign (=) to the right of the word, Ruby assumes it’s a local variable being assigned. If it’s neither a keyword nor an assignment, Ruby assumes it’s a method being invoked and sends the method to the implied receiver, self.

Classes and Objects

You’ve reviewed all the basic types of items in a Ruby application, so let’s start using them.

Objects

Ruby is an object-oriented (OO) programming language. If you’ve never worked in an OO language before, the metaphors used can be confusing the first time you hear them. Basically, objects are simple ways to organize your code and the data it contains. Objects are just like objects in the real world. You can move them around, make them do things, destroy them, create them, and so forth. In OO programming, you act on objects by either passing messages to them or passing them in messages to other objects. This will become clearer as we go along.

To better understand OO programming, let’s start out with some procedural code (which is decidedly not object oriented) first. Let’s say you’re writing a program to help track the athletic program at a school. You have a list of all the students who are currently participating in a team, along with their student IDs. This example looks at the rowing team. You could keep an array of arrays representing the students on the team:
rowing_team = [[1975, "Smith", "John"], [1964, "Brown", "Dan"], ...]
Note

This is called a multidimensional array. It’s simply an array that contains more arrays as elements. You could reference the first array in the array like so:

rowing_team.first=> [1975, "Smith", "John"]

This is an array of [id, first_name, last_name]. You’d probably need to add a comment to explain that. If you wanted multiple teams, you could wrap this in a hash:
teams = { :rowing => [[1975, "Smith", "John"], [1964, "Brown", "Dan"], ...],
          :track  => [[1975, "Smith", "John"], [1900, "Mark", "Twain"], ...]
        }

That works for now. But it’s kind of ugly, and you could easily get confused, especially if you kept adding teams. This style of coding is referred to as procedural, and it’s not object oriented. You’re keeping track of huge data collections that are made up of simple types. Wouldn’t it be nice to keep all these data more organized? You’ll need to define what your objects will look like, so you’ll need a pattern, called a class. Then you will instantiate your class to make an instance.

Classes

A class is like a blueprint for creating an object. You’ve been using classes all over the place—Array, String, User, and so on. Now, let’s construct a Student class and a Team class.

Here is the basic blueprint for a Student class:
class Student
  # Setter method for @first_name
  def first_name=(value)
    @first_name = value
  end
  # Getter method for @first_name
  def first_name
    @first_name
  end
  # Setter method for @last_name
  def last_name=(value)
    @last_name = value
  end
  # Getter method for @last_name
  def last_name
    @last_name
  end
  # Returns full name
  def full_name
    last_name + ", " + first_name
  end
end
Note

“Getter” and “setter” methods are methods that get an instance variable or set an instance variable, respectively. It’s that simple. They are used to expose this functionality both inside and outside your instance. In this case, you have a getter and setter method for last_name and first_name. They use instance variables (as opposed to local variables) so that the getter and setter methods can share the same data. Otherwise, if they used local variables, the getter and setter methods for last_name, for example, would have their own data for the last name—meaning you couldn’t “get” what you “set.”

Right now, you’re keeping track of the student’s first_name and last_name strings. As you can see, you define a method named first_name=(value), and you take value and put it into an instance variable named @first_name. Let’s try using this class:
# Take the Class, and turn it into a real Object instance
@student = Student.new
@student.first_name = "Bob"
@student.last_name = "Jones"
puts @student.full_name
"Jones, Bob"

Instead of building a dumb array, you’ve built a smart class. When you call new on the class, it builds a version of itself called an object, which is then stored in the @student variable. In the next two lines, you use the = methods to store the student’s first and last names. Then, you use the method full_name to give a nicely formatted response.

It turns out that creating getter and setter methods like this is a common practice in OO programming. Fortunately, Ruby saves you the effort of creating them by providing a shortcut called attr_accessor:
class Student
  attr_accessor :first_name, :last_name, :id_number
  def full_name
    last_name + ", " + first_name
  end
end

This behaves in exactly the same way as the first version. The attr_accessor bit helps by automatically building the methods you need, such as first_name=. Also, this time you add an @id_number.

Let’s build a Team class now:
class Team
  attr_accessor :name, :students
  def initialize(name)
    @name = name
    @students = []
  end
  def add_student(id_number, first_name, last_name)
    student = Student.new
    student.id_number  = id_number
    student.first_name = first_name
    student.last_name  = last_name
    @students << student
  end
  def print_students
    @students.each do |student|
      puts student.full_name
    end
  end
end

You’ve added something new to this class: the initialize method. Now, when you call new, you can pass in the name. For example, you can type Team.new('baseball'), and the initialize method is called. Not only does initialize set up the name but it also sets up an instance variable named @students and turns it into an empty array. The method add_students fills the array with new Student objects.

Let’s see how you use this class:
team = Team.new("Rowing")
team.add_student(1982, "John", "Smith")
team.add_student(1984, "Bob", "Jones")
team.print_students
 Smith, John
 Jones, Bob

Containing things in objects cleans up your code. By using classes, you ensure that each object only needs to worry about its own concerns. If you were writing this application without objects, everyone’s business would be shared. The variables would all exist around one another, and there would be one huge object. Objects let you break things up into small working parts.

By now you should have a general idea of what’s going on with some of the Ruby code you’ve seen floating around Rails. There is a lot more to Ruby that we haven’t touched on here. Ruby has some amazing metaprogramming features you can read about in a book that specifically focuses on Ruby, such as Beginning Ruby: From Novice to Professional, Third Edition, by Peter Cooper (Apress, 2016).

Ruby Style
Style is important when you’re programming. Ruby programmers tend to be picky about style, and they generally adhere to a few specific guidelines, summarized here:
  • Indentation size is two spaces.

  • Spaces are preferred to tabs.

  • Variables should be lowercase and underscored: some_variable, not someVariable or somevariable.

  • Method definitions should include parentheses and no unnecessary spaces: MyClass.my_method(my_arg), not my_method( my_arg ) or my_method my_arg.

Whatever your personal style, the most important thing is to remain consistent. Nothing is worse than looking at code that switches between tabs and spaces or mixed and lowercase variables.

Ruby Documentation

You can refer to the following documentation for more information about Ruby:
  • Core library: The Ruby distribution comes with a set of classes known as the Ruby Core library, which includes base classes such as Object, String, Array, and others. In the Ruby Core application programming interface (API) documentation, you can find all the classes and methods included in the Core library. In this short chapter, you’ve already seen a few classes in action. One of the secrets to effectively using Ruby is to know which classes and methods are available to you. We recommend that you go to the Ruby Core API documentation page at www.ruby-doc.org/core/ and start to learn more about Ruby classes and methods.

  • Standard library: In addition to the Core library, the Ruby distribution comes bundled with the Ruby Standard library. It includes a set of classes that extends the functionality of the Ruby language by helping developers perform common programming tasks, such as network programming and threading. Make sure you spend some time reading the Standard library documentation at www.ruby-doc.org/stdlib/.

  • Online resources: The Ruby documentation project home page is located at www.ruby-doc.org. There you can find additional reading resources to help you learn Ruby, such as articles and tutorials, as well as the Core and Standard Ruby API documentation.

Summary

This chapter gave a strong introduction to the Ruby language. You now have the tools to start learning the Rails framework and start building web applications. As you progress, you’ll more than likely come to love Ruby, especially if you have a background in other languages. Its power is only matched by its simplicity, and it’s genuinely fun to program with. The next chapter will dive into Active Record and learn how Rails lets you easily interact with your database.