Chapter 1. Ruby Fundamentals

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.

Interactive Ruby

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.

Note

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.

Data Types

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.

Numbers

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.

Strings

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.

Note

A question mark at the end of method name, as in empty?, indicates that it is a predicate, and it will return a true or false value. An exclamation mark (!) usually signifies that the method does something dangerous such as modifying the object in place.

Symbols

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.

Arrays

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.

Note

If you try to access an element that isn’t in the array, the [] method will return nil.

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.

Hashes

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.

Booleans

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.

Note

Any expression in Ruby can be evaluated as a Boolean expression. In Ruby, only nil and false are considered false. Every other value is considered true. This differs from some other languages, where things like empty strings, empty collections, and the number zero are considered false.

Constants

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.

Variables

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.

Control Flow

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.

Conditionals

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.

Iteration

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.

Methods

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.

Note

If you’re using Ruby 2.1, method definitions return the name of the method as a symbol.

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.

Classes

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.

Class Methods

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.

Inheritance

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.

Summary

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.

Exercises

Q:

1. You can read plaintext files in Ruby with the File.read method. Create a file containing a paragraph or two from a blog post or book, and name it test.txt in the current directory. This next code sample reads a file named test.txt into the variable file and displays the contents of the file:

file = File.read("test.txt")
puts file

As you can see, file contains a string. Use file.split to convert the string into an array of words. You can now use Ruby’s built-in array methods to operate on the contents of the file. For example, use file.split.length to count words in the file. file.split.uniq.length tells you how many unique words are in the file.

Q:

2. Using the array of words from Exercise 1, count how many times each word appears in the file. One way to do this is by iterating over the array and storing the count for each word in a hash where the key is the word and the value is the count.

Q:

3. Create a WordCounter class to perform the operations from Exercises 1 and 2. The class should accept a filename to read when it is initialized and include methods named count, uniq_count, and frequency for performing the operations from the previous two exercises. The following class definition should help you get started:

class WordCounter
  def initialize(file_name)
    @file = File.read(file_name)
  end

  # your code here...
end
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.174.23