Learning Ruby Basics in < 9 minutes

Chapter 2 Notes on Design Patterns in Ruby by Russ Olsen

Russ Olsen

in his book Design Patterns in Ruby

makes it easy for beginners in the language to view an excellent intro about Ruby to help build better software programs.

In this article, I'm distilling the notes I wrote about chapter 2.

Check out chapter 1 here

Getting Started with Ruby

The easiest way to run Ruby language is to use the interactive Ruby shell, irb.

  • You can run interactively on the shell:
$ irb
irb(main):001:0> 2 + 2
=> 4
irb(main):002:0> 
  • You can run a ruby program with the ruby command (the Ruby interpreter):
$ ruby hello.rb
hello world

where hello.rb is:

puts 'hello world'

As you can see, puts prints out values, and the function in Ruby can accept the parameter(s) without a parenthesis.

Also, in Ruby calculations can continue on the second line like in calc.rb file:

x = 1 +
    2 + 3
puts x

running the file:

$ ruby calc.rb
6

Take care if you continue the line before the desired statement ends like this:

x = 1
+ 2 + 3
puts x

it will give you 1. If you want to include adding 2 & 3, you should extend the first line with /:

x = 1 \
+ 2 + 3
puts x
  • For variable names, The well-bred Ruby programmer uses words_separated_by_underscores instead of camelCase.

  • In Ruby, constants start with a capital letter. You shouldn't change the constant's value although you can change it, but you'll get a warning for your trouble.

Pounds = 2.2
FACTS = 'Life and Death'
  • / does integer division in Ruby:
6/3 # is 2
7/3 # is still 2
  • Everything in Ruby is an object. With the class method you can know its type:
irb(main):012:0> 7777777777777777777.class
=> Integer
irb(main):013:0> 77.77777777777777777.class
=> Float
irb(main):014:0> 'str'.class
=> String

You can check the type of the object with instance_of method. You can check if it's a nil or not by nil method. You can also convert the object to string with to_s method.

irb(main):023:0> 'hello'.instance_of? String
=> true
irb(main):021:0> 'hello'.nil?
=> false
irb(main):022:0> 44.to_s
=> "44"

Because everything in Ruby is an object, it is not correct to say that the expression x = 44 assigns the value 44 to the variable x. Instead, what is really happening is that x receives a reference to an object that happens to represent the number after 43.

  • nil and false both evaluate to false:
nil || false # false
nil or false # false

as you might guess, || and or are the OR boolean operator while && and and are the AND operator. Also not and ! are the same for negation.

If you come from the world of C or C++, you will be shocked to learn that in Ruby, zero, being neither false nor nil, evaluates to true in a Boolean expression. Surprisingly, this expression:

if 0
    puts 'Zero is true'
else
    puts 'Zero is false'
end

will print out

Zero is true
  • The else if in Ruby is elsif
  • There is an unless statement in Ruby which reverses the if statement:
unless weight < 100
    puts 'way too heavy'
end

A short form is also available:

puts 'way too heavy' unless weight < 100
  • Ruby programmers tend to write loops like this:
array = ['first', 'second', 'third']
array.each do |element|
    puts element
end

instead of a for loop:

for element in array
    puts element
end
  • until is the evil twin of the while loop; reverses the condition.
  • Some methods for strings in Ruby:
irb(main):001:0> name = 'Ezz'
=> "Ezz"
irb(main):002:0> name.length
=> 3
irb(main):003:0> name.upcase
=> "EZZ"
irb(main):004:0> name.downcase
=> "ezz"
  • In many ways, Ruby strings act like arrays; mutable:
irb(main):005:0> name[0] = 'Z'
=> "Z"
irb(main):006:0> name
=> "Zzz"
  • Substitution inside a string is done by #{expression}
n = 42
puts "The value of n is #{n}."

which prints out:

The value of n is 42

Take care, you should the string, which contains a substitution, should be enclosed by double-quotes.

Symbols are more or less immutable strings and Ruby programmers use them as identifiers:

:a_symbol
:an_other_symbol
:first_name

For example, you can't edit str2 here:

str1 = 'Ezz'
str2 = :Ezz
str1[0] = 'Z'
# str2[0] = 'Z'
puts str1
puts str2
  • Appending arrays in Ruby can be done with << operator:
a = ['banana', 'apple', 'orange']
a << 'mango'
puts a # ['banana', 'apple', 'orange', 'mango']

You can sort or reverse the array. If you want to change the array in place (changes happen to the original array), add ! to the method.

a = ['banana', 'apple', 'orange']
a << 'mango'
a.sort
puts "original array not sorted: #{a}"
a.reverse
puts "original array not reversed: #{a}"
a.sort!
puts "original array sorted: #{a}"
a.reverse!
puts "original array reversed: #{a}"
  • You can create hashes in Ruby with a pair of braces:
h = {}
h['first_name'] = 'Abu Bakr'
h['last_name'] = 'El Seddiq'
puts h
h_new = {'first_name' => 'Abu Bakr', 'last_name' => 'El Seddiq'}
puts h_new

Symbols make good hash keys:

h_improved = {:first_name => 'Abu Bakr', :last_name => 'El Seddiq'}
puts h_improved
  • Regular expressions in Ruby, sit between a pair of forward slashes. The =~ operator when you match an RE with a string and !~ for testing whether an RE does not match:
/old/ =~ 'this old house' # 5 - the index of 'old'
/new/ !~ 'this old house' # true - 'new' is not matching anything
/Russ|Russel/ =~ 'Fred' # nil - Fred is not Russ nor Russel
/.x*/ =~ 'any old string' # 0 - the RE will match anything

Classes

class BankAccount
    def initialize account_owner
        @owner = account_owner
        @balance = 0
    end

    def deposit amount
        @balance = @balance + amount
    end

    def withdraw amount
        @balance = @balance - amount
    end
end
  • Classes in Ruby start with capital letters and use camel case
  • The initialize method is special and it's the Ruby version of a constructor
  • To create a new instance of our class BankAccount
my_account = BankAccount.new('Ezz')
  • Let's say I'd like to access the balance instance variable:
my_account = BankAccount.new('Ezz')
puts my_account.balance

it seems this code is not working and produces the following error:

BankAccount.rb:17:in `<main>': undefined method `balance' @owner="Ezz", @balance=0> (NoMethodError)

This is because the instance variable on a Ruby object can not be accessed outside the object. That's why we define an accessor method:

def balance
    @balance
end

If we add this to the BankAccount class and tried to get the balance, we will receive 0.

We might want to set a new balance, so we add a setter method:

def set_balance new_balance
    @balance = new_balance
end

If you tried to take it easy and ignore just setter method and wrote this:

my_account.balance = 100

it will output a NoMethodError. What you need to do instead of that and instead of the ugly setter method is:

def balance=(new_balance)
    @balance = new_balance
end

which we can use to set balance using:

my_account.balance=(100)

and as always in Ruby, you can omit the parenthesis.

What we did above is a trick to make Ruby know that balance= is a method and it takes one argument. Yes, as you might realize, this equal sign is part of the method's name that Ruby translates it into a plain old method call.

So now, the class looks good from the outside world. We have balance; a method to get and balance=; a method to set.

Boring, isn't it?

Unsurprisingly, Ruby has a solution. As Ruby programmers use getter and setter methods a lot. Ruby supplies us with a great shortcut:

attr_accessor :balance

Now, this statement creates the value of the instance variable @balance to be able to get. It also creates the balance=(new_value) setter method.

What if you want to just have access to getting an instance variable not setting it? In this case, you can use attr_reader like so:

attr_reader :name

in this case, the name variable is read-only.

Similarly, for the setter method only, use attr_writer.

  • Sometimes a method needs a reference to the current object, we can use self:
class SelfCentered
    def talk_about_me
        puts "Hello I am #{self}"
    end
end

conceited = SelfCentered.new
conceited.talk_about_me

but when you run that code, you'll get something like this:

Hello I am #<SelfCentered:0x00005600a5ccfa78>

pointing to a hex address of the instance SelfCentered.

  • Ruby supports single inheritance -- all the classes that you create have exactly one parent of the superclass. If the superclass is not specified, your class is automatically a subclass of Object.

In this example, we will create a subclass of BankAccount:

class InterestingBearingAccount < BankAccount
    def initialize owner, rate
        @owner = owner
        @balance = 0
        @rate = rate
    end

    def deposit_interest
        @balance += @rate * @balance
    end
end

If you compare this with the superclass BankAccount, you'll see duplicate information in the initialize method. We have the same owner and balance as in the BankAccount's initialize method:

def initialize account_owner
    @owner = account_owner
    @balance = 0
end

In this case, it's better to avoid that messy code duplication and we should use super method like so:

def initialize owner, rate
    super(owner)
    @rate = rate
end

When a method calls super, it's saying, "Find the method with the same name as me in my superclass, and call that."

So basically, what super is doing is that it calls the initialize method in the superclass BankAccount. If this method doesn't exist in the first superclass, Ruby will continue in the root of inheritance until it finds that particular method.

Arguments

  • You can set a default value in Ruby function like:
def add_args name, car="BMW"
    puts "#{name} has #{car}"
end

add_args("Ezz")
add_args("Ezz", "Rolls Royce")

For the first return, it will say that I have BMW and the second return states that I got richer and have Rolls Royce.

  • For a list of arguments, you can use asterisks:
def describe_langs name, *languages
    puts "#{name}"
    for lang in languages
        puts "learns: #{lang}"
    end
end

describe_langs("Ezz", "Python", "MATLAB", "Javascript", "Rust")

Modules

  • In Ruby, you can include a module inside a class but you can create an instance from the module.
module Chatty
    def say_hi
        puts "Hello, my name is #{name}"
        puts "My job title is #{title}"
    end
end

class Employee
    include Chatty

    def name
        'Ezz'
    end

    def title
        'Data Engineer'
    end
end

employee = Employee.new
puts employee.say_hi

so you can't do something like this:

employee = Chatty.new
puts employee.say_hi

Exception

  • Ruby will catch any exception with the keyword rescue. You can also specify the type of exception:
begin
    quotient = 1 / 0
rescue ZeroDivisionError
    puts "Division by zero"
end

If you want to raise the exception, you can use raise.

Importing source files

  • In Ruby, you can import source files with require which loads the classes inside the Ruby the file name followed. Import classes like this:
require 'account.rb'

or

require 'account'

This also applies to the standard files included with Ruby. For example, you can parse some URLs with URI class that comes with Ruby:

require 'uri'
ezz = URI.parse 'https://wwww.ezzeddinabdullah.com'
  • Let's say you want to require a package called runt that exists in RubyGems; a software packaging system. You need then to require RubyGems before the the package itself:
require 'rubygems'
require 'runt'

Final Thoughts

In this chapter, we've been on a quick tour of the Ruby language.

We've seen that everything in Ruby, from a string to a number to arrays, is an object. Here, the author wraps up Ruby for beginners so that you can use it to build better software programs implementing design patterns. Stay tuned because we're about to start.

See you in chapter 3 notes!

Get Design Patterns in Ruby from Amazon!!

Design Patterns in Ruby by Russ Olsen
Image by the Author

Credit

Disclosure: The Amazon link for the book (in this section) is a paid link so if you buy the book, I will have a small commission

Get the next FREE ebook 'Disciplined' once released and more exclusive offers in your inbox

Published on medium

Join the conversation

Get FREE coupons and discounts
on my upcoming courses
when you subscribe!

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.