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 rubycommand (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.
- niland- falseboth 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
falsenornil, evaluates totruein 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 unlessstatement in Ruby which reverses theifstatement:
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
- untilis the evil twin of the- whileloop; 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 initializemethod 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 requirewhich 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!!
Image by the Author