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.
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.
Your irb prompt might look slightly different depending on your version of Ruby and your computer environment. This is perfectly okay.
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?
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
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.
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
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:
Symbols
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.
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.
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.
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.
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.
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
Ruby Operators
Operator | Description |
---|---|
[] []= | Assignment |
* / % + ** | Arithmetic |
<= >= < > | Comparison |
.. ... | Range |
& ^ | | AND, exclusive OR, regular OR (bitwise) |
|| && not or and | Logical operators |
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, 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.
Comments
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
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.
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.
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.)
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.
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.
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"]
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.
“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.”
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.
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.
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.
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).
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
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.