This chapter is a tightrope walk between oversimplification and a degree of detail that is unnecessary for a Rails newbie. After all, the objective is not to become a Ruby guru but to understand Ruby on Rails. I will elaborate on the most important points, and the rest is then up to you. If you would like to know more about Ruby, I recommend the book The Ruby Programming Language by David Flanagan and Yukihiro Matsumoto.
“It is easy to program in Ruby, but Ruby is not a simple language.”
—Yukihiro Matsumoto
Ruby 2.5
I’m going to use Ruby 2.5, but for most part of this book you can use older versions too. Ruby 2.5 is just a bit faster. You can check the installed Ruby version by running the command ruby -v, as shown here:
If your system is running an older version and you want to upgrade it, take a look at https://rvm.io , which is my preferred way of installing and using different Ruby versions.
Basics
Ruby is a scripting language. So, it is not compiled and then executed; instead, it is read by an interpreter and then processed line by line.
Hello World
A simple hello-world.rb program consists of one line of code, as shown in Listing 1-1.
hello-world.rb
Use your favorite editor to open a new file with the filename hello-world.rb and insert the previous line into it. You can then execute this Ruby program at the command line as follows:
A program line in a Ruby program does not have to end with a semicolon. The Ruby interpreter is even so intelligent that it recognizes if a program line was split over two or more lines for the sake of readability. Indenting code is also not necessary. But it does make it much easier to read for human beings!
puts and print
puts prints a string, followed by a newline.
print prints a string (without a newline).
Listing 1-2 shows an example program (an extension of the program hello-world.rb).
hello-world.rb
On the screen, you will see this:
Comments
A comment in a Ruby program starts with a # sign and ends with a newline. As an example, I added a comment to the earlier hello-world.rb program; see Listing 1-3.
hello-world.rb
A comment can also follow a program line, as shown in Listing 1-4.
hello-world.rb
A # sign within strings in a single quote mark is not treated as the start of a comment, as shown in Listing 1-5.
hello-world.rb
Help via ri
When programming , you do not always have a Ruby handbook available. Fortunately, the Ruby developers thought of this and provided a built-in help feature in form of the program ri.
Of course, you must have installed the documentation, which is the default. If you used rvm to install Ruby, you can run rvm docs generate to generate the documentation.
This is a typical chicken-and-egg situation. How can I explain the Ruby help feature if you are only just getting started with Ruby? So, I am going to jump ahead a little and show you how you can search for information on the class String.
Many times it is easier and more informative to use Google instead of ri.
irb
irb stands for “Interactive Ruby ” and is a kind of sandbox where you can play around with Ruby at your leisure. You can launch irb by entering irb on the shell and end it by entering exit.
An example is worth a thousand words.
In future examples, I use IRB.conf[:PROMPT_MODE] = :SIMPLE in my .irbrc config file to generate shorter irb output (without the irb(main):001:0> part). You can do the same by using irb --simple-prompt.
Ruby Is Object-Oriented
Ruby only knows objects. Everything is an object (sounds almost like Zen). Every object is an instance of a class. You can find out the class of an object via the method .class.
An object in Ruby is encapsulated and can be reached from the outside only via the methods of the corresponding object. What does this mean? You cannot change any property of an object directly from the outside. The corresponding object has to offer a method with which you can do so.
Please do not panic if you have no idea what a class or an object is. I won’t tell anyone, and you can still work with them just fine without worrying too much. This topic alone could fill whole volumes. Roughly speaking, an object is a container for something, and a method changes something in that container.
Please go on reading and take a look at the examples. The puzzle will gradually get clearer.
Methods
In other programming languages , the terms you would use for Ruby methods would be functions, procedures, subroutines, and of course methods.
Here I go with the oversimplification. You cannot compare non-object-oriented programming languages with object-oriented ones. Plus, there are two kinds of methods (class methods and instance methods). I do not want to make it too complicated. So, I simply ignore those “fine” distinctions.
At this point, you probably want to look at a good example, but all I can think of are silly ones. The problem is the assumption that you are only allowed to use knowledge that has already been described in this book .
So, let’s assume that you use the code sequence in Listing 1-6 repeatedly (for whatever reason).
hello-worldx3a.rb
So, you want to output the string “Hello World!” three times in separate rows. As this makes your daily work routine much longer, you are now going to define a method (with the meaningless name three\_times), with which this can all be done in one go, as shown in Listing 1-7.
Names of methods are always written in lowercase.
hello-worldx3b.rb
Let’s test this by starting irb and loading the program with the command load './hello-worldx3b.rb'. After that, you have access to the three_times method.
When defining a method, you can define required parameters and use them within the method. This enables you to create a method to which you pass a string as a parameter, and you can then output it three times, as shown in Listing 1-8.
hello-worldx3c.rb
Incidentally, you can omit the brackets when calling the method.
Ruby gurus and would-be gurus are going to turn up their noses on the subject of “unnecessary” brackets in your programs and will probably pepper you with more or less stupid comments of comparisons to Java and other programming languages.
There is one simple rule in the Ruby community: the fewer brackets, the cooler you are!
But you won’t get a medal for using fewer brackets. Decide for yourself what makes you happy.
If you do not specify a parameter with the previous method, you will get this error message: wrong number of arguments (0 for 1).
You can give the variable value a default value, and then you can also call the method without a parameter, as shown in Listing 1-9.
hello-worldx3d.rb
Classes
For now you can think of a class as a collection of methods. The name of a class always starts with an uppercase letter. Let’s assume that the method belongs to the new class This_and_that. It would then be defined as shown in Listing 1-10 in a Ruby program.
hello-worldx3e.rb
Let’s play it through in irb.
Now you try to call the method three_times.
This results in an error message because This_and_that is a class and not an instance. As you are working with instance methods, it works only if you have first created a new object (a new instance) of the class This_and_that with the class method new. Let’s name it abc .
I will explain the difference between instance and class methods in more detail in the section “Class Methods and Instance Methods” (another chicken-and-egg problem).
Private Methods
Quite often it makes sense to only call a method within its own class or own instance. Such methods are referred to as private methods (as opposed to public methods), and they are listed after the keyword private within a class, as shown in Listing 1-11.
pm-example.rb
You run this in irb, first the public and then the private method, which raises an error.
Method initialize()
If a new instance is created (by calling the method new), the method that is processed first and automatically is the method initialize. The method is automatically a private method, even if it not listed explicitly under private, as shown in Listing 1-12.
pm-example-a.rb
Here is an irb test of it:
The instance kitchen is created with Room.new, and the method initialize is processed automatically.
The method new accepts the parameters specified for the method initialize, as shown in Listing 1-13.
initialize-example-b.rb
return
puts is nice to demonstrate an example in this book, but normally you need a way to return the result of something. The return statement can be used for that, as shown in Listing 1-14.
circle-a.rb
But it wouldn’t be Ruby if you couldn’t do it shorter, right? You can simply skip return, as shown in Listing 1-15.
circle-b.rb
You can actually even skip the last line because Ruby returns the value of the last expression as a default, as shown in Listing 1-16.
circle-c.rb
Obviously you can go one step further with this code, as shown in Listing 1-17.
circle-d.rb
return is sometimes useful to make a method easier to read. But you don’t have to use it if you feel more comfortable with out.
Inheritance
A class can inherit from another class . When defining the class, the parent class must be added with a less-than (<) sign.
Rails makes use of this approach frequently (otherwise I would not be bothering you with it).
In Listing 1-18, you define the class Abc that contains the methods a, b, and c. Then you define a class Abcd and let it inherit the class Abc and add a new method d. The new instances example1 and example2 are created with the class method new and show that example2 has access to the methods a, b, c, and d but example1 only to a, b, and c.
inheritance-example-a.rb
Run it in irb.
It is important to read the error messages. They tell you what happened and where to search for the problem. In this example, Ruby says that there is an undefined method for #<Abc:0x007fac5a845630>. With that information you know that the class Abc is missing the method that you were trying to use.
Class Methods and Instance Methods
There are two important kinds of methods: class methods and instance methods.
You now already know what a class is. An instance of such a class is created via the class method new. A class method can only be called in connection with the class (for example, the method new is a class method). An instance method is a method that works only with an instance. So, you cannot apply the method new to an instance.
Let’s first try to call an instance method as a class method, as shown in Listing 1-19.
pi-a.rb
Run it in irb.
So, that does not work. Well, then let’s create a new instance of the class and try again.
Now you just need to find out how to define a class method. Hard-core Rails gurus would now whisk you away into the depths of the source code and pick out examples from ActiveRecord. I will spare you this and show an abstract example; see Listing 1-20.
pi-b.rb
Here is the proof to the contrary:
There are different notations for defining class methods . The two most common ones are self.xyz and class << self.
The result is always the same.
Of course, you can use the same method name for a class and an instance method. Obviously that doesn’t make code easier to read. Listing 1-21 shows an example with pi as a class and an instance method .
pi-c.rb
List of All Instance Methods
You can read all the defined methods for a class with the method instance_methods. Try it with the class Knowledge (first you create it once again in irb), as shown in Listing 1-22.
pi-a.rb
But that is much more than you have defined! Why? It’s because Ruby gives every new class a basic set of methods by default. If you want to list only the methods that you have defined, then you can do it like this:
Basic Classes
Many predefined classes are available in Ruby. For a newbie, probably the most important ones handle numbers and strings.
Strings
Let’s experiment a little bit in irb. The method .class tells you which class you are dealing with.
That was easy. As you can see, Ruby “automagically” creates an object of the class String. You can also do this by explicitly calling the method new.
If you call String.new or String.new() without a parameter, this also creates an object of the class String. But it is an empty String.
Single and Double Quotations Marks
Strings can be defined either in single quotes or in double quotes .
There is a special feature for the double quotes: you can integrate expressions with the construct #{}. The result is then automatically inserted in the corresponding place in the string.
To show this, you have to jump ahead and use variables in the example.
If the result of the expression is not a string, Ruby tries to apply the method to_s to convert the value of the object into a string. Let’s try that by integrating an Integer into a String.
If I mention single or double quotation marks in the context of strings, I do not mean typographically correct curly quotation marks (see wikipedia.org/wiki/Quotation_mark); instead, I mean the ASCII symbols referred to as apostrophe (') or quotation mark (").
Built-in Methods for String
Most classes already come with a bundle of useful methods. These methods are always written after the relevant object, separated by a dot.
Here are a few examples for methods of the class String:
With instance_methods(false), you can get a list of the built-in methods.
Numbers
Let’s discuss numbers.
Integers
Ruby used to have different types of integers depending on the length of the number. Since Ruby version 2.4, things are easier; you just deal with Integer.
Floats
Float is a class for real numbers (“floating-point numbers”). The decimal separator is a dot.
Mixed Class Calculations
Adding two integers will result in an integer . Adding an integer and a float will result in a float.
Boolean Values and nil
For Boolean values (true and false) and for nil (no value), there are separate classes.
nil (no value) is, by the way, the contraction of the Latin word nihil (nothing); or if you look at it in terms of programming history , the term derives from “not in list” from the legacy of the programming language Lisp (the name is an acronym of “list processing”).
Variables
Let’s discuss variables .
Naming Conventions
Normal variables are written in lowercase. Please use snake_case. The same goes for symbols and methods.
Constants
Constants start with an uppercase letter.
A constant can also be overwritten with a new value since Ruby 2.3 (but you will get a warning message). So, please do not rely on the constancy of a constant.
You are on the safe side if you are using only ASCII symbols. But with Ruby 2.5 and the right encoding, you could also use special characters (for example, the German umlaut) more or less without any problems in a variable name. But if you want to be polite toward other programmers who probably do not have those characters directly available on their keyboards, it is better to stick to pure ASCII.
Scope of Variables
Variables have a different scope (or “reach”) within the Ruby application and therefore also within a Ruby on Rails application .
You need to keep this scope in mind while programming. Otherwise, you can end up with odd effects.
Local Variables (aaa or _aaa)
Local variables start with either a lowercase letter or an underscore (_). Their scope is limited to the current environment (for example, the current method). Listing 1-23 defines two methods that use the same local variable radius. Because they are local, they don’t interact with each other.
variable-a.rb
Global Variables ($aaa)
A global variable starts with a $ sign and is accessible in the entire program. Listing 1-24 shows an example program.
variable-b.rb
Global variables are used rarely! You wouldn’t harm yourself by forgetting that they exist right now.
Instance Variables (@aaa)
Instance variables (“attributes,” which is why there’s an @ sign) apply only within a class, but they apply everywhere in it—they’re mini versions of global variables, so to speak. Unlike global variables, you will find instance variables all over the place in a Rails application. Let’s tackle them in form of an example program with the name color.rb, as shown in Listing 1-25.
color.rb
If you start this program, you will see the following output:
In the method initialize, you set the instance variable @color to the value white. The method paint_it(value) changes this instance variable.
With the method color you can access the value of @color outside of the instance. This kind of method is called a setter method.
Methods Once Again
To keep the amount of chicken-and-egg problems in this chapter at a manageable level, you need to go back to the topic of methods and combine what you have learned so far.
Method Chaining
You may not think of it straightaway, but once you have gotten used to working with Ruby, then it makes perfect sense (and is perfectly logical) to chain different methods.
Getters and Setters
As instance variables (attributes) exist only within the relevant instance, you always need to write a “getter” method for exporting such a variable. If you define a class Room that has the instance variables @doors and @windows (for the number of doors and windows in the room), then you can create the getter methods doors and windows. Listing 1-26 shows an example program called room.rb.
room.rb
Here is the output from the execution of the program:
Because this scenario—wanting to simply return a value in identical form—is so common, there is already a ready-made getter method for it with the name attr_reader, which you would apply as follows in the program room.rb, as shown in Listing 1-27.
room.rb
attr_reader is a method called on the Room class. That is the reason why you use symbols (e.g., :doors and :windows) instead of variables (e.g., @doors and @windows) as parameters.
attr_reader is a good example for metaprogramming in Ruby. When working with Rails, you will frequently come across metaprogramming and be grateful for how it works “automagically.”
If you want to change the number of doors or windows from the outside, you need a setter method. It can be implemented as shown in Listing 1-28.
room.rb
The corresponding output is as follows:
As you can probably imagine , there is also a ready-made and easier way of doing this. Via the setter method attr_writer, you can simplify the code of room.rb as shown in Listing 1-29.
room.rb
And (who would have thought?) there is even a method attr_accessor that combines getters and setters. The code for room.rb would then look like Listing 1-30.
room.rb
Converting from One to the Other: Casting
There is a whole range of useful instance methods for converting (casting) objects from one class to another. First, let’s use the method .to_s to convert a Fixnum to a String.
Incidentally, that is exactly what puts does if you use puts to output a Fixnum or a Float (for nonstrings, it simply implicitly adds the method .to_s and outputs the result).
Now you use the method .to_i to change a Float to a Fixnum.
Method to_s for Your Own Classes
Integrating a to_s method is often useful. Then you can simply output a corresponding object via puts (puts automatically outputs an object via the method to_s).
Listing 1-31 shows an example.
person-a.rb
Is + a Method?
Why is there also a plus symbol in the list of methods for String? Let’s find out by looking it up in ri.
Let’s see what it says for Integer.
Let’s play around with this in irb. You should be able to add the + to an object, just as any other method, separated by a dot and then add the second number in brackets as a parameter.
Aha! The plus symbol is indeed a method, and this method takes the next value as a parameter. Really, you should put this value in brackets, but thanks to Ruby’s well-thought-out syntax, this is not necessary.
Can You Overwrite the Method +?
Yes, you can overwrite any method. Logically, this does not make much sense for methods such as +, unless you want to drive your fellow programmers mad. I am going to show you a little demo in irb so you will believe me.
The aim is overwriting the method + for Fixnum. You want the result of every addition to be the number 42. You can write a so-called monkey patch , as shown in Listing 1-32.
monkey-patch-a.rb
Now you use the + method before and after that monkey patch.
First you perform a normal addition . Then you redefine the method + for the class Integer, and after that you do the calculation again. But this time, you get different results.
if Condition
An abstract if condition looks like this:
The program between the expression and end is executed if the result of the expression is not false and not nil.
You can also use a then after the expression, as shown here:
The construct for a simple if branch in a Ruby program looks like the following example program:
The == is used to compare two values. Please don’t mix it up with the single =.
You can test an expression really well in irb .
Shorthand
The following code shows a frequently used shorthand notation of an if condition:
else
You can probably imagine how this works, but for the sake of completeness, here is a little example :
elsif
Again, most programmers will know what this is all about. Here’s an example:
Loops
There are different ways of implementing loops in Ruby. The iterator variation is used particularly often in the Rails environment.
while and until
An abstract while loop looks like this:
The do that follows expression is optional. Often you will also see this:
Here is an irb example:
You build until loops similarly.
Again, here is the corresponding irb example:
Blocks and Iterators
Block and iterator are some of the favorite words of many Ruby programmers. Now I am going to show you why.
In the following loop, i is the iterator, and puts i is the block.
You can also express the whole thing in the following syntax:
Iterators
Iterators are just a specific type of method. As you probably know, the word iterate means to repeat something. For example, the class Integer has the iterator times(). Let’s see what help ri Integer.times offers:
It also gives a nice example that you can try in irb.
There is also a single-line notation for small blocks.
By the way, an iterator does not necessarily have to pass a variable to the block.
Blocks
A block is the code that is triggered by an iterator . In the block, you have access to the local variable (or variables) passed by the iterator.
Method upto
In addition to times, there is also the method upto for easily implementing a loop. ri offers a nice example for this, too.
Arrays and Hashes
As in many programming languages , arrays and hashes are popular structures in Ruby for storing data.
Arrays
An array is a list of objects. Let’s play around in irb.
That is simple and easy to understand.
Let’s see if it also works with strings in the array.
That also works.
So, all that’s missing now is an array with a mixture of both. Obviously that will work, too, because the array stores objects , and it does not matter which kind of objects they are (i.e., String, Integer, Float, …). But a little test can’t hurt.
Arrays can also be created via the method new (like any class). Individual new elements can then be added at the end of an array via the method <<. Here is the corresponding example:
Iterator each
You can work your way through an array piece by piece via the method each. Here’s an example:
ri Array.each provides help and an example in case you forget how to use each.
Hashes
A hash is a list of key-value pairs. Here is an example with strings as keys :
Of course, hashes can store not just strings as objects in the values but, as with arrays, also classes that you define yourself (see the section “Arrays”).
Symbols
Symbols are a strange concept and difficult to explain. But they are useful and used frequently with hashes, among others.
Normally, variables always create new objects.
In both cases, you have the variable a, but object_id is different. You could carry on in this way indefinitely. Each time, it would generate a different object ID and therefore a new object. In principle, this is no big deal and entirely logical in terms of object orientation. But it is also rather a waste of memory space .
A symbol is defined by a colon before the name and cannot store any values itself, but it always has the same object ID, so it is very well suited to be a key.
Let’s do another little experiment to make the difference clearer. Use a string object with the content white three times in a row and then the symbol :white three times in a row. For white, a new object is created each time. For the symbol :white, it’s created only the first time.
Using symbols as key for hashes is much more memory efficient.
You will frequently see symbols in Rails . If you want to find out more about symbols, go to the help page about the class Symbol via ri Symbol.
Iterator each
With the method each you can work your way through a Hash step-by-step. Here’s an example:
Again, ri Hash.each offers help and an example in case you cannot remember one day how to use each.
Range
The class Range represents an interval. The starting and ending points of the interval are defined enclosed in normal brackets and separated by two dots in between them. Here is an example in which you use a range like an iterator with each:
Via the method to_a, you can generate an array from a range.
A range can be generated from objects of any type. It’s only important that the objects can be compared via <, >, == and can use the method succ for counting on to the next value. So, you can also use Range to represent letters.
As alternative notation, you may sometimes come across Range.new(). In this case, the starting and ending points are not separated by two dots but by a comma. This is what it looks like: