Ruby Crash Course

In 2015, I created a web app using Ruby on Rails. My reasons are Ruby was fast and Rails was robust. Of course, there are other benefits such as the syntax being simple and easy to learn.

Today, many people think that Ruby is dead. For example, people like to start web applications with NodeJS or GoLang. Maybe I agree with that statement. But Ruby was in a lot of legacy projects. Sometimes, tweaking a feature is cheaper than revamping the entire system with the new language.

Let's simplify this course like Ruby. I hope this article can help you learn Ruby in no time or warm up your existing Ruby skills.

Installation

In development, the best way to install Ruby is by using a Ruby manager. There are a lot of Ruby managers out there but I recommend Ruby Version Manager (RVM). For Windows, you can try rbenv.

Ruby Version Manager (RVM)

  1. gpg2 --keyserver keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
    • In macOS, you can replace gpg2 with gpg. But make sure you install it first, brew install gnupg.
    • If you failed at this command, I recommend you download and install GPG keys manually. Sorry, I didn't give the commands or clear instructions here because there are a lot of ways to do that. Maybe https://rvm.io/rvm/install can help.
  2. \curl -sSL https://get.rvm.io | bash -s stable
    • We don't need --rails here. We can install Rails later by using gem install rails.
  3. rvm list is to list available Ruby.
  4. rvm install ruby-3.3.1 to install Ruby 3.3.1 for example. Now, you can install multiple versions of Ruby on your local.
  5. rvm use 3.3.1 to use it. You can add ~/.rvm/gems/ruby-3.3.1/bin to your path to make it permanent.

Variables

number = 9
decimal = 9.99
hello1 = "Hello!"
hello2 = 'Hello!'
is_active = false
not_is_active = !is_active

Variables in String

name = "John Doe"
hello = "Hello #{name}!"

Comments

# I am a comment.

=begin
We are comments.
We are comments.
We are comments.
=end

Operations

Arithmetic

puts 1 + 1 # 2
puts 2 - 1 # 1
puts 2 / 1 # 1
puts 2 * 2 # 4
puts 2 ** 3 # 8
puts 4 % 2 # 0

Bitwise

puts ~7 # 8
puts 5 & 3 # 1
puts 5 | 3 # 7
puts 5 ^ 3 # 6
puts 4 >> 1 # 2
puts 4 << 1 # 8

Control

if score >= 100
  puts "Perfect!"
elsif score >= 80
  puts "Good!"
elsif score >= 70
  puts "Okay."
else
  puts "Sad..."
end

puts score >= 70 ? "Passed!" : "Not qualified!"
puts "Congratulations!" if score >= 70

unless score >= 70
  puts "Try again!"
end

There is unless in Ruby. In the example above, unless the score is >= 70 then you can avoid "Try again!".

Case

case grade
  when "A"
    puts "Very good!"
  when "B"
    puts "Good!"
  else
    puts "Not qualified!"
end

The case is just like the switch in other programming languages.

Comparison

puts 1 > 2 # false
puts 1 < 2 # true
puts 2 >= 2 # true
puts 2 <= 2 # true
puts 1 == 2 # false

puts 1 <=> 2 # -1
puts 2 <=> 2 # 0
puts 3 <=> 2 # 1

The <=> is the comparison syntax in Ruby. It returns the integer. It returns lower than 0 if the first operand is less than the second. It returns greater than 0 if the first operand exceeds the second. The syntax is useful when sorting.

Looping

for i in 1..3
  print i # 123
end

i = 1
while i <= 3 do
  print i # 123
  i += 1
end

(1..3).each do |i|
  print i # 123
end

i = 1
loop do
  print i # 123
  i += 1
  break if i > 3
end


i = 1
until i == 3 do
  print i # 12
  i += 1
end

(1...3).each do |i|
  print i # 12
end


3.times do
  print "ha" # "hahaha"
end

As we can see, there is a lot of syntax in Ruby for looping.

Next

for i in 1..8
  next if i % 2 == 1
  print i # 2468
end

The next is just like the continue in other programming languages. We skip the iteration when found the condition.

Collection

numbers = [1, 2, 3, 4, 5]
unrestricted = [1, 2.3, true, "Hello", 'World!']

number.push(6)
unrestricted << 6

Sorting

1
2
3
4
5
6
numbers = [3, 4, 1, 5, 2]
numbers.sort
puts numbers # 3, 4, 1, 5, 2
numbers.sort!
puts numbers # 1, 2, 3, 4, 5
puts numbers.sort { |a, b| b <=> a } # 5, 4, 3, 2, 1

Hash

map = {
  "a" => 1,
  "b" => 2,
  "c" => 3
}

set = {
  1 => true,
  2 => false,
  3 => true
}

start = {}
same_start = Hash.new

That is called Hash in Ruby. We can make a Map or Set from Hash. To create an empty hash is with {} or Hash.new.

Default

m = Hash.new(9)

puts m["a"] # 9

The difference between {} and Hash.new is we can set the default with Hash.new.

Symbol

map = {
  :a => 1,
  :b => 2,
  :c => 3
}

It is better to use symbols for hashes. It can make your program faster.

Accessing

numbers = [1, 2, 3]
map = {
  :a => 1,
  :b => 2,
  :c => 3
}

puts numbers[1] # 2
puts map[:b] # 2

We need brackets to access the element whether it is array or hash.

Key and Value

map = {
  :a => 1,
  :b => 2,
  :c => 3
}

map.each { |k, v| puts "#{k}: #{v}" }

map.each_key { |k| print k } # abc
map.each_value { |v| print v } # 123

Select

map = {
  :a => 1,
  :b => 2,
  :c => 3
}

puts map.select { |_, v| v % 2 == 0 } # {:b=>2}

We can filter the hash by using select.

Method

def power(number, value)
  return number ** value
end

def hello
  "Hello, world!"
end

puts power(2, 3) # 8
puts hello # "Hello, world!"

The hello function above was returning "Hello, world!" even without return.

Splat

def count(*numbers)
  numbers.each { |number| puts number }
end

def single_count(numbers)
  numbers.each { |number| puts number }
end

count(1, 2, 3)
single_count([1, 2, 3])

With Splat, *, we can put many arguments to the method.

def count(*numbers)
  a, b = *numbers
  
  puts a, b
end

count(1, 2, 3)

But, with Splat, we also can take how many arguments we need.

Yield

def ac
  print "a"
  yield
  print "c"
end

ac { print "b" } # abc

Proc vs Lambda

def proc_method
  demo = Proc.new { return "Printed" }
  demo.call
  
  return "Not printed"
end

def lambda_method
  demo = lambda { return "Not printed" }
  demo.call
  
  return "Printed"
end

puts proc_method
puts lambda_method

The results are the same. The difference is that proc_method returns the result of the Proc, not the method, and lambda_method returns the result of the method, not the Lambda.

Object-Oriented Programming

class Animal
  def initialize(name, feets, has_paws)
    @name = name
    @feets = feets
    @has_paws = has_paws
  end

  protected
    attr_accessor :name
    attr_accessor :feets
    attr_accessor :has_paws
  
  public
    def print
      puts "#{@name} has #{@feets} feets."
      puts "#{@name} has paws." if @has_paws
    end
end

class Monster < Animal
  def initialize(name, feets, has_paws, abilities)
    super(name, feets, has_paws)
    @abilities = abilities
  end
  
  public
    def print
      puts "#{@name} is a monster!!!"
      puts "#{@name} has:"
      @abilities.each do |ability|
        puts "- #{ability}"
      end
    end
end

monster = Monster.new("Dragon", 4, true, ["Nuclear blast", "Ice smoke"])
monster.print()

That is my common OOP example, Animal and Monster.

class Animal
  @@counter = 0

  def initialize(name, feets, has_paws)
    @name = name
    @feets = feets
    @has_paws = has_paws
  end

  def print
    @@counter += 1
    puts "#{@name} has #{@feets} feets."
    puts "#{@name} has paws." if @has_paws
  end

  def self.printed
    @@counter
  end
end

dog = Animal.new("Scooby", 4, true)
dog.print

puts Animal.printed

We can define a class variable with double @. So, class variables are attached to entire classes, not just instances of classes. For example, take a look at the self.printed, we tried to print the counter and it was counted. We can call it by using Animal.printed. The self.printed is just like a static method in PHP.

Access Modifiers

Ruby has 4 access modifiers; default, private, protected, and public. That is similar to Java and PHP.

Module

module Environment
  DEFAULT = "development"
end

puts Environment::DEFAULT # development

In that Environment module, we have a constant (a variable that starts with an uppercase), called DEFAULT.

Interface

module Interface
  def print
    raise "Not implemented"
  end
end

class Animal
  include Interface
end

animal = Animal.new
animal.print

With modules, we can include constants and methods in classes. We also can trick it into interfaces like in Java and PHP. Like an example above. To avoid an error, the Animal class needs to override (or implement) the print method.

In Ruby, everything is an object. Just like Java. So, they have methods. What methods are available? You can visit https://ruby-doc.org to look for help. Go to the documentation of your Ruby version.

Getting serious? You can visit Ruby Style Guide for more best practices.

Related Articles