Ruby Tutorial

Ruby Tutorial

The Ruby programming language is one of the main server-side scripting languages of the Web alongside Python and JavaScript on Node.js. Initially released in 1995 by Yukihiro "Matz" Matsumoto its fame grew with the stellar expansion of the Ruby on Rails framework which was released in 2004. In Ruby everything (well, almost) is an object and an expression. In this Ruby Tutorial, you will find out what this actually means and get acquainted with topics such as object-oriented programming (OOO), functional programming, class inheritance, variables & constants, arrays, hashes, enumerators, and others.

Intro

Inception

Ruby is a high-level programming language that was first publicly released in 1995 by Yukihiro "Matz" Matsumoto. Ruby was designed for programmer productivity as a priority.

Ruby Interpreter

Ruby is an interpreted language which means that instructions written in Ruby are interpreted by an interpreter without previous compiling. The original Ruby interpreter is referred to as Matz's Ruby Interpreter (MRI) and was written in C language. The current interpreter is Yet Another Ruby VM (YARV). There are alternate interpreters among which is JRuby that runs on Java Virtual Machine.

Multiple Paradigm

Ruby allows for multiple paradigm programming among which are object oriented programming, functional programming and prodedural programming.

Duck Typing

In Ruby (almost) everything is an object. An application of a particular object in Ruby is not determined by its type but rather by its capabilities i.e. existing methods and properties. In other words if we call an object a rabbit but it "walks like a duck and it quacks like a duck, then it must be a duck".

Monkey Patching

Due to its meta-programming capabilities Ruby supports Monkey Patching i.e. extending and modifying existing built-in Ruby objects (including classes) at runtime affecting only that particular program's runtime.

Ruby Gems

A Ruby package manager is called gem and it allows for installing Ruby gems i.e. community created Ruby libraries hosted on RubyGems.org.

gem install rails

Everything Is an Expression

Many programming languages distinguish between expressions which return values and statement which do not and are primarily used for flow control (it could be said that expressions are executed and evaluated and statements are simply executed). This is not the case in Ruby. In Ruby everything is an expression which means that everything returns a value. Method calls, method definitions, variable and constant assignments, literals, loops, class definitions, if control flow and other return a value.

Comments

One line comment:

# This is a comment
puts "This is not a comment" # But this is.

Multiline comment:

=begin
This
is
a multiline
comment.
=end

Object Oriented Programming

Identifying Objects

In Ruby almost everything is an object - including numerics, booleans, strings and symbols - and each object in Ruby has a unique id.

1.object_id # => 3
"1".object_id # => 70102006679520
class Animal; end
Animal.object_id # => 70102006843460
Animal.new.object_id # => 70102007385240
Animal.new.object_id # => 70102024293860

Sending Messages

It could be argued that the basis of Ruby programming is sending messages to objects and dealing with their responses. 1+2 is really sending a message :+ to the object 1 with the argument of 2. The whole adding operation is really a syntactic sugar for 1.send(:+, 2)

Responding to Messages

Each object in Ruby responds to a message in one way or another. If the object knows how to handle the message it returns some value and if it does not it raises a NoMethodError.

class Animal
  def self.walk
    "Walking ..."
  end
end

Animal.walk # => "Walking ..."
Animal.fly # => NoMethodError: undefined method `fly' for Animal:Class

Self

As opposed to JavaScript where this might be undefined in Ruby there is always a self object. self means the current object within context of which methods are being defined and code evaluation is being effected. Even after directly accessing a Ruby console and without creating any objects there is a self.

$ irb
> self # => main

Methods defined within a context of main object are not anonymous functions as they are associated with the main object. This puts into question an existence in Ruby of pure procedural programming paradigm.

Variables & Constants

Both variables and constants are names associated with references to objects under specific memory addresses.

The difference between variables and constants is that: 1) variables' references are allowed to change during runtime of a Ruby program and constants' references should not change. 2) only constants can be used to reference classes and modules.

Although, a once defined constant's reference to an object should not change during a run time of a Ruby program Ruby does not enforce it.

DEFAULT_CURRENCY = "USD"
DEFAULT_CURRENCY = "EUR" # => warning: already initialized constant DEFAULT_CURRENCY; warning: previous definition of DEFAULT_CURRENCY was here
DEFAULT_CURRENCY # => "EUR"

Naming Rules

Naming variables and constants in Ruby is primarily not about conventions but hard rules.

Variable and constant names in Ruby can use alphanumeric characters and underscores with the reservation that they cannot begin with a number.

Ruby is a case sensitive language. A name of reference starting with a small letter or an underscore is a variable and a name of reference starting with a capital letter is a constant.

# variables
answer = 42 # => 42
an_answer_to_the_universe = 42 # => 42
a1 = "some value" # => "some value"
1a = "some value" # SyntaxError: unexpected tIDENTIFIER, expecting end-of-input
# constants
FavoriteAnimal = "elephant"
FORREST_ANIMALS = ["wolf", "owl", "deer"]

Dynamic Typing

Ruby is a dynamically typed language. This means that in Ruby the type (i.e. Integer, String, Array) of a variable is allowed to change during the runtime of a Ruby program.

Variable Scopes

A variable in Ruby is defined within a scope i.e. an area within which the variable exists. Two variables defined in two different scopes are different non-conflicting entities that are being defined, changed and used separately from each other.

A variable scope is being set through use of special identifiers - sigils. A variable without a sigil e.g. an_animal is a local variable. A variable starting with @ e.g. @an_animal is an instance variable. A variable starting with @@ e.g. @@forrest_animals is a class variable. Finally, a variable starting with $ e.g. $zoo is a global variable.

Local & Global Variables

A local variable exists only within a local scope i.e. a scope of a method, a block or a module. An instance variable exists within a scope of an instance of some class. A class variable exists within a scope of a class and is therefore available to all its instances. A global variable exists in the global scope and is available everywhere in a Ruby program.

Methods & Blocks

Methods

Methods in Ruby are set of expressions that 1) always return a value, 2) are always associated with some Ruby object.

Ruby methods are not straightforward objects but can be obtained as objects using an Object.method method. Such a method object might be 1) called, 2) used as a block or 3) unbound from its associated object and bound to another.

class Mathematician
  def sum(a, b)
    a + b
  end
end

engineer = Mathematician.new
engineer.sum(2, 2) # => 4
engineer.sum.class # => ArgumentError: wrong number of arguments (given 0, expected 2)
object_method = engineer.method(:sum)
object_method.class # => Method
object_method.call(3, 3) # => 6

Method Arguments

Arguments can be provided to Ruby methods through: 1) standard arguments, 2) keyword arguments, and 3) variable length arguments.

# Standard arguments:
def sum(first_number, second_number)
  first_number + second_number
end

sum(2, 2) #=> 4
sum(2) #=> ArgumentError: wrong number of arguments (given 1, expected 2)

# Keyword arguments:
def sum(first_number:, second_number:)
  first_number + second_number
end

sum(first_number: 2, second_number: 2) # => 4
sum(second_number: 2, first_number: 2) # => 4
sum(first_number: 2) # => missing keyword: second_number

# Variable length arguments:
def sum_all(*args)
  args.inject(0, :+)
end

sum_all(1, 2, 3) # => 6
sum_all(1, 2, 3, 4, 5) # => 15

With both - standard arguments and keyword arguments - it is possible to use default values making the arguments with default values optional.

def sum(first_number, second_number = 2)
  first_number + second_number
end

sum(2, 3) #=> 5
sum(2) #=> 4

def sum(first_number:, second_number: 2)
  first_number + second_number
end

sum(first_number: 2, second_number: 3) # => 5
sum(first_number: 2) # => 4

Blocks

It might be said that an inspiration for blocks were higher-order functions.

Blocks in Ruby are set of expressions accepting arguments that 1) do not return a value by itself, 2) are not associated with any Ruby object. Blocks are not objects but can be turned to objects with & notation signalled in a given method's arguments.

Blocks are signalled by the do ... end notation or the { ... } notation and their arguments are enclosed in |...| notation.

Blocks can be 1) called from within a method or 2) yielded to.

Calling Blocks

Blocks can be turned into objects (namely procs) and called when its underlying method uses & in its arguments.

def sum(&my_block)
  my_block.call(2, 2)
end

sum { |a, b| a + b } # => 4
sum { |a, b| a - b } # => 0
sum { |a, b| a + a + b + b } # => 8

Yielding to Blocks

The same results as with calling blocks can be achieved with yielding to blocks with a yield keyword.

def sum
  yield(3, 4)
end

sum { |a, b| a + b } # => 7
sum { |a, b| a - b } # => -1
sum { |a, b| a + a + b + b } # => 14

Classes, Instances & Modules

Classes

Class is a built-in Ruby object which inherits from the Module class, another built-in Ruby object.

The Ruby Class object can be used to create Ruby classes that is objects with the ability of instantiation.

class Animal
end

Animal.class # => Class

Instances

To initialize an instance of a given class the static method :new is called. The arguments (zero or more) and behaviour of :new method is defined using initialize method.

class Animal
  def initialize(species:, name:)
    @species = species
    @name = name
  end
end

Animal.new(species: 'fish', name: 'Wanda').class # => Animal

Methods & Self

Methods defined within a class are instance methods not the class object's (aka static) methods unless they are defined on the class object itself using def self.foo (or def Bar.foo) or an idiom class << self is used.

class Thing
  def inspect_instance_self
    puts self
  end

  def self.inspect_class_self # == def Thing.inspect_class_self
    puts self
  end
end

Thing.inspect_instance_self # NoMethodError (undefined method `inspect_self' for Thing:Class)
Thing.inspect_class_self # => Thing

Thing.new.inspect_instance_self # => #<Thing:0x00007fa25187e808>
Thing.new.inspect_class_self # => NoMethodError (undefined method `inspect_class_self' for #<Thing:0x00007fa25500d418>)

When the idiom class << self is used the methods defined within its block are the class static methods.

class Item
  class << self
    def inspect_self
      puts self
    end
  end
end

Item.inspect_self # Item 
Item.new.inspect_self # NoMethodError (undefined method `inspect_self' for #<Item:0x00007fcf8c0845e0>)

Each Object Has Two Classes

It is sometimes inaccurately said that in Ruby a given object has only one class. In Ruby each object has exactly one named class - its direct ancestor - and the second one - existing beside the direct ancestor class - called a singleton or an eigenclass class. Methods defined with self.foo or << self are defined as methods of the eigenclass and used as singleton methods.

Inheritance

Each class in Ruby inherits from some other class - its direct ancestor. Methods and constants defined within a given class ancestor and ancestors of its ancestor are available within the class.

class Airplane
  def self.inspect
    'Inspecting'
  end

  def fly
    'Flying'
  end
end

class Jet < Airplane
end

Jet.inspect # "Inspecting"
Jet.new.fly # "Flying"

Instance Variables

Instance variables are denoted with @ and assigned to a specific instance of a Ruby class.

class Animal
  def initialize(species:, name:)
    @species = species
    @name = name
  end
end

an_animal = Animal.new(species: 'horse', name: 'Roach') # => <Animal:0x00007fd4801896e0 @name="Roach", @species="horse">

If a get method is defined within the class of the instance then it is possible to get a specific instance variable through sending a message with the name of the get method to the instance.

If a set method is defined within the class of the instance it is possible to set a specific instance variable through sending a message with the name of the set method to the instance (syntactic sugar for = can be used).

class Animal
  def initialize(species:, name:)
    @species = species
    @name = name
  end

  # get method
  def name
    @name
  end

  # set method
  def name=(name)
    @name = name
  end
end

an_animal = Animal.new(species: 'horse', name: 'Roach') # => <Animal:0x00007fd4801896e0 @name="Roach", @species="horse">

an_animal.name # => "Roach"
an_animal.name = "Kelpie"
an_animal.name # => "Kelpie"

attr_reader :name can be used instead of def name, attr_writer :name can be used instead of def name=(name). attr_accessor combines attr_reader and attr_writer.

Modules

The Module class is a built-in Ruby object instances of which 1) provide namespaces allowing for collision-free defining of constants and methods, 2) enable broadening of classes' functionalities through mixins.

Creation of a module is signalled by the module reserved word.

module CustomMath
  def sum(a, b)
    a + b
  end
end

Including Modules

When a class includes a module with an include keyword the non-static methods defined in the module become the class'es instance methods.

class SimpleMath
  include CustomMath
end

simple_math = SimpleMath.new # => <SimpleMath:0x00007fd630054700>
simple_math.sum(2, 2) # => 4

Prepending Modules

prepend works similarly to include and the difference between the two is that prepend gives precedence to methods defined in the module to those defined directly in the class.

Extending Classes with Modules

Extending a class with a module with an extend keyword makes the non-static methods defined in the module to be available within the class as the class methods.

Conditional Execution

Booleans

There are three boolean values in Ruby: true, false & nil.

They are all built-in Ruby objects.

false & nil are the only falsy values in Ruby. All other expressions, including true, 0 (zero) and "" (empty string), are truthy. This disambiguation is important especially with regards to control flow in Ruby.

puts "I will be printed!" if true # => "I will be printed!"
puts "I won't be printed!" if false # => nil
puts "I won't be printed as well!" if nil # => nil

if

if expression controls whether a selected expression is executed or not.

if can be used as a multi-line expression:

if should_do_something?
 do_something
end

if can also be used as a single line expression:

do_something if should_do_something?

In both cases do_something is executed if should_do_something? evaluates to a truthy value.

if elsif

if expression can also be extended with an elsif keyword. The code following the elsif keyword is executed provided should_do_a? evaluates to a falsy value and should_do_b? evaluates to a truthy value.

if should_do_a?
  do_a
elsif should_do_b?
  do_b
end

if else

if expression can be extended with an else keyword. The code following the else keyword is executed provided should_do_a? evaluates to a falsy value.

if should_do_a?
  do_a
else
  do_b
end

if elsif else

if, elsif and else can be combined.

if expression can be extended with both elsif and else keywords. The code following the if expression is executed provided should_do_a? evaluates to a truthy value. The code following the elsif keyword is executed provided should_do_a? evaluates to a falsy value and should_do_b evaluates to a truthy value. The code following the else keyword is executed provided both should_do_a? and should_do_b evaluate to falsy values.

if should_do_a?
  do_a
elsif should_do_b?
  do_b
else
  do_c
end

case

case expression can be used to check for multiple conditions.

case foo
when condition_a
  do_a
when condition_b
  do_b
else
  do_c
end

The conditions are being evaluated using === method where the condition is the receiver and the expression following the case keyword is the argument. In other words when truthfulness of the condition_a is being evaluate it sends message === to condition_a with the argument of foo.

Loops & Iterators

A loop in Ruby are blocks that are repeated while a certain condition is being met or until a certain condition is reached.

While Loop

A while loop in Ruby is signalled with a keyword while and it repeats a certain block while (i.e. as long as) a certain condition is being met.

count = 10

while count > 0 do
  count -= 1
  print count
end # => 9876543210

The keyword while can also be placed after a block which however makes the block to execute before the condition is checked and therefore the block is always executed at least once.

count = 10

begin
  count -= 1
  print count
end while count > 0 # 9876543210 => nil

A similar construct can be created with an unitl keyword.

count = 10

until count == 0 do
  count -= 1
  print count
end # 9876543210 => nil

The while and until loops return nil.

For Loop

A for loop goes through a collection of elements and separately executes a block with each of those elements as an argument.

elements = [1,2,3,4,5]

for element in elements
  print element
end

The for loop returns the underlying collection.

Next & Break

When a next keyword is used within a loop it will skip the current iteration and go to the next one.

count = 10

while count > 0 do
  count -= 1
  next if count == 5

  print count
end # 987643210 => nil

When a break keyword is used within a loop it will stop the whole loop.

count = 10

while count > 0 do
  count -= 1
  break if count == 5

  print count
end # 9876 => nil

In addition to next and break there is also a redo keyword in Ruby.

Iterators

Iterators are methods called on Ruby objects that iterate one by one on the objects underlying data. The most notable iterator is each and is discussed under Enumerators section.

Exception Handling

Exception Class

When an exception occurs in Ruby code an instance of one of descendants of class Exception is used to carry data about the exception. Such an object has its class, may have a description and may include backtrace. Such an object may be a built-in Ruby object or a custom made class furnished with additional functionalities.

The most notable built-in subclasses of Exception are: SyntaxError, StandardError (default for rescue block), ArgumentError, NameError, NoMethodError, and RuntimeError (default for raise block).

It is possible to define a custom-cut exception class through subclassing of Exception class or one of its subclasses.

class MyCustomError < Exception
end

Raising

An exception in Ruby can be raised implicitly due to some underlying code defect or explicitly by a programmer in a manner decided by the programmer.

1/0 # ZeroDivisionError: divided by 0

raise "Nobody expects the Spanish inquisition!" # RuntimeError: Nobody expects the Spanish inquisition!

raise ZeroDivisionError, "Please, don't divide by zero!" # ZeroDivisionError: Please, don't divide by zero!

Rescuing

Once an exception occurs code execution stops unless it is rescued. The reserved keyword rescue can be used within a method or within a block.

def divide_by_zero
  1/0
rescue
  puts "Rescued!"
end

divide_by_zero # Rescued!

begin
  1/0
rescue
  puts "Rescued!"
end
# Rescued!

It is also possible to rescue from a specific subclass of Exception only.

def divide_by_zero
  1/0
rescue SyntaxError
  puts "Rescued!"
end

divide_by_zero # ZeroDivisionError: divided by 0

It is also possible to catch the exception object.

def divide_by_zero
  1/0
rescue => e
  p e
  puts "Rescued!"
end

divide_by_zero
# #<ZeroDivisionError: divided by 0>
# Rescued!

e (or other name set by the programmer) denotes an object that is an instance of one of the subclasses of the Exception class. In this particular case e is an istance of ZeroDivisionError class.

e.backtrace and e.backtrace_locations methods can be used to track traceback data.

Combining Rescue & Raise

We can use raise and rescue keywords within the same block.

def divide_by_zero
  raise ZeroDivisionError, "Please, don't divide by zero!"
rescue ZeroDivisionError => e
  puts e
  puts e.backtrace 
  puts "Rescued!"
end

divide_by_zero
# #<ZeroDivisionError: divided by 0>
# ["(pry):296:in `divide_by_zero'", "(pry):302:in ...] 
# Rescued!

Else

Within a method or a block in addition to the rescue keyword the keyword else can also be used. The code following else is only executed when no exception is raised.

Ensure

Within a method or a block in addition to the rescue and else keywords the keyword ensure can also be used. The code following ensure is always executed irrespective of whether any exception occured or not.

Retry

retry can be placed within rescue block to tell the program to retry the rescued block. This should be used with caution as not to create an infinite loop.

Comparing Objects

==

== method is implemented by all the aforementioned built-in Ruby objects. It is used to compare them and evaluates to true or false. Generally, the method does not care whether the objects are equal but whether their values are the same. Therefore, comparing with == two different string objects with the same values gives true.

1 == 1 # => true
"ruby" == "ruby" # => true
1 == 2 # => false
"ruby" == "javascript" # => false

!=

!= method is implemented by all the aforementioned built-in Ruby objects. Similarly to ==, the method takes into account the objects' values and is used to compare them. It evaluates to true when the objects' values differ and to false when the objects' values are the same.

1 != 1 # => false
"ruby" != "ruby" # => false
1 != 2 # => true
"ruby" != "javascript" # => true

Comparing Numbers

In addition to comparing numbers with == and != we can also compare them with < (less than), > (more than), <= (less than or equal to) and >= (more than or equal to).

1 < 2 # => true
1 <= 1 # => true

Numerics

Integers class is a built-in Ruby object instances of which (integers) are used to denote whole numbers e.g. -128, -64, 0, 1, 42, 43, 100.

Integers have many built-in methods. Some of the methods allow for basic arithmetic.

1 + 2  # Addition of 2 to 1. Gives 3.
1 - 2  # Subtraction of 2 from 1. Gives -1.
2 * 3  # Multiplication of 2 by 3. Gives 6.
10 / 2 # Division of 10 by 2. Gives 5.
2 ** 3 # Exponentiation of 2 to the power of 3. Gives 8.# 
10 % 6 # Remainder of division of 10 by 6 (modulo). Gives 4.

Strings & Symbols

Strings

String class is a built-in Ruby object instances of which (strings) are used to denote words.

There can be multiple strings with the same value. Strings with the same value are different objects although they evaluate to true when compared.

a = "ruby"
b = "ruby"
a == b # true
a.equal?(b) # false
a.object_id # 70275395962400
b.object_id # 70275396044460

Symbols

Symbol class is a built-in Ruby object instances of which (symbols) are used as memory efficient names during Ruby runtime. For example, when a method is defined it returns a symbol. Furthermore, a symbol - just like a string - can be used as a hash key name.

Symbols are denoted by words but adversely to strings they are not mutable.

There can be only one symbol with a given value in a Ruby program.

a = :ruby
b = :ruby
a.equal?(b) # true
a == b # true
a.object_id # 676828
b.object_id # 676828

Enumerators

Intro

Enumerator class is a built-in Ruby object. Its instances and instances of its subclasses have capabilities of internal and external iteration. Enumerator is a descendent class - just like Array, Hash and Range classes - of Enumerable class.

Blockless Each

Arrays, hashes and ranges in Ruby all have an :each method defined. The method does and returns a different thing depending on whether a block was provided to it or not.

[1,2,3].each { |a| print a } # 123 => [1, 2, 3]

[1,2,3].each # => #<Enumerator: ...>

An enumerator has a next method defined which returns subsequent values of the enumerator's underlying data.

an_enum = [1,2,3].each # => #<Enumerator: ...>

an_enum.next # => 1
an_enum.next # => 2
an_enum.next # => 3
an_enum.next # => StopIteration: iteration reached an end

Creating Enumerators with Loops

It is possible to create a custom made enumerator using a method new, a block and a loop.

even_numbers = Enumerator.new do |y|
  a = 0
  loop do
    y << a
    a += 2
  end
end

even_numbers.next # => 0
even_numbers.next # => 2
even_numbers.next # => 4
even_numbers.next # => 6
even_numbers.next # => 8

Arrays

The Array class is a built-it Ruby object instances of which (arrays) are used as ordered and indexed lists of Ruby objects.

Commonly arrays in Ruby are created using literal constructors.

my_array = [1, 2, "three"]

The :[] method is used to access elements of an array.

captains = ["Kirk", "Picard", "Hook"]
captains.send(:[], 0) # => "Kirk"
captains[0] # => "Kirk"
captains[-1] # => "Hook"

letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
letters[2..4] # => ["c", "d", "e"]
letters.first # => "a"
letters.last # => "e"
letters.take(3) # => ["a", "b", "c"]

Other common methods that come with Ruby arrays are empty?, any?, all?, none?, one?, count, size, length, include?, push, shift, unshift, insert, pop, delete_at, delete, compact, uniq, select, reject, flatten, shuffle, slice, sort.

Hashes

The Hash class is a built-in Ruby object instances of which (hashes) are dedicated to storing and retrieving values under specific keys.

Commonly hashes in Ruby are created using literal constructors.

student_grades = {
  michael: 'A',
  amy: 'A+',
  anthony: 'B'
}

In the above example symbols are used as keys and the notation michael: A is tantamount to :michael => 'A' where the symbol :michael is a key and the string 'A' is a value. In addition to symbols strings are commonly used as Ruby hashes' keys.

:[] method is used to retrieve a value stored under a specific key. Remember that the symbol :michael is not the same key as the string 'michael'.

student_grades[:michael] # => "A"
student_grades['michael'] # => nil

:[]= method is used to store a new key-value pair in an existing hash.

student_grades[:jenny] = 'C' # => C

Ranges

Intro

The Range class is a built-in Ruby object. Its instances (ranges) denote sequential collection of values with a indicated beginning and end.

Initializing Ranges

An instance of the Range class can be created with a new method but more often literals - .. and ... are used. A range initialized with .. includes the end value and the range initialized with ... does not.

1..5 # Includes values 1, 2, 3, 4 and 5.
(1..5).to_a # => [1, 2, 3, 4, 5]

1...5 # Includes values 1, 2, 3 and 4.
(1...5).to_a # => [1, 2, 3, 4]

Out-of-the-box ranges can also be initialized with letters as underlying values instead of integers.

'a'..'e' # Includes values 'a', 'b', 'c', 'd' and 'e'.

<=>

Ranges can be initialized for any Ruby objects that are capable of being compared with <=> operator.

Time & Dates

Ruby includes Time and Date classes which can be used to initialize objects representing a given point in time or a given date.

# To get current point in time:
Time.now # => 2021-06-17 07:08:00 +0200

# To get current day:
Date.today # => Thu, 17 Jun 2021

# To get a humanly readable point in time at a given Unix time i.e. seconds since the Epoch (1970.01.01 00:00:00 UTC):
Time.at(1) # => 1970-01-01 01:00:01 +0100
Time.at(1623906931) # => 2021-06-17 07:15:31 +0200

# To get a humanly readable point in time using integers:
Time.new(2007, 1, 9, 12, 30) # => 2007-01-09 12:30:00 +0100
Date.new(2007, 1, 9) # => Tue, 09 Jan 2007

Time Zones

Each instance of Time class has a time zone included.

time_now = Time.now
time_now.zone # => "CEST"
Time.now.utc # => 2021-06-17 05:23:47 UTC

Formatting Time & Date

In Ruby representations of time & dates can be formatted using strftime method.

Functional Programming

Procs

Proc class is a built-in Ruby object instances of which (procs) are representations of callable blocks of Ruby code. As all other Ruby objects procs can be stored under a variable or a constant, be an array or a hash value or used as a method or a block argument.

Some of ways of creating a proc is using the Proc #new method or the Kernel #proc method

my_proc = Proc.new { |x| x }
my_other_proc = proc { |x| x }

To call a proc #call method is used.

my_proc.call('Great') # => "Great"

In addition, a proc is created when a block is passed as an argument to a method.

Further, a proc of a special kind - namely lambda - can be created using Kernel #lambda method or a lambda literal syntax.

Procs as Closures

Procs are closures which mean that they are bound to their identifiers (i.e. self objects) current at the time of their creation and they carry with them used within their body variable and constant references to objects available in the scope of place in which they were created.

class Bird
  def fly
    wings = 'feathers'

    Proc.new do
      puts self
      puts wings
    end
  end
end

class Airplane
  def fly
    Bird.new.fly.call
  end
end

Airplane.new.fly.call
# <Bird:...>
# feathers

Lambdas

There is not separate class for lambdas from Proc. Lambdas are just a special kind of Proc class' instances.

Lambdas are special to other procs in the following manner: 1) return in lambdas exits only from the lambda itself but not - as is the case for other kind of procs - from a method or another proc from which they are called, 2) lambdas are strict about arguments they accept - just like methods - which is not the case for other kind of procs.

A lambda can be created in two ways: 1) using lambda reserved word, or 2) a literal syntax ->.

multiply_by_2 = lambda { |a| a * 2 }
multiply_by_3 = ->(a) { a * 3 }

multiply_by_2.call(5) # => 10
multiply_by_2.call # ArgumentError: wrong number of arguments (given 0, expected 1)

def lets_return
  lambda { return 'returned from lambda' }.call
  proc { return 'returned from proc' }.call
  return 'from block'
end

lets_return # => "returned from proc"

Due to its characteristics lambdas are well suited to be used as arguments to methods or procs which then cauld be described as higher order functions.

To check whether a given proc is a lambda the lambda? method is used.

Defining Methods with Procs

A method can be defined using a proc.

multiply_by_2 = proc { |a| a * 2 }
define_method(:times_two, &multiply_by_2)

times_two # ArgumentError: wrong number of arguments (given 0, expected 1)
times_two(21) # => 42

Converting to Procs

Ruby objects that have a method to_proc defined can be converted to procs with & operator. Symbol, Method and Hash are Ruby built-in classes that have to_proc method defined out-of-the-box.

Regular Expressions

Regexp is a built-in Ruby class instances of which denote regular expressions which are being used to test whether a string matches a pattern or to extract a whole or a part of a matched pattern.

There are three ways to create a regular expression: 1) /.../, 2) %r{...}, and 3) Regexp#new.

Match?

A Regexp instance method match? returns true if a given regular expression is matched and false otherwise.

/awesome/.match?('Ruby is awesome') # => true

Match

A Regexp instance method match returns an instance of MatchData class if a pattern is matched and nil otherwise.

It is possible to capture specific parts of strings and then refer to them using an instance of MatchData.

matched_data = /(I).*(juice)\./.match('I like juice.')
# => #<MatchData "I like juice." 1:"I" 2:"juice">

matched_data[1] # => "I"
matched_data[2] # => "juice"

=~

=~ operand returns the index of a first match if a pattern is matched or nil otherwise.

/awesome/ =~ ('Ruby is awesome') # => 8
/lame/ =~ ('Ruby is awesome') # => nil

The operand =~ is defined by both Regexp and String classes and therefore the order does not matter.

Dynamic Regular Expressions

It is possible to create dynamic regular expressions using interpolation with #{...}.

dynamic_regex = 'cat'
/#{dynamic_regex}/.match('I like cat memes.') # => #<MatchData "cat">

Loading & Requiring Files

Load

The Kernel#load method loads a file provided to it as a string argument. It returns true if successful or raises LoadError if file was not loaded successfully.

With load Ruby searches for the file in the absolute path and if the file is not found it searches in the paths listed under $LOAD_PATH (i.e. $:). It is also permissible to provide a relative path to load.

load loads and makes all constants and global variables to be available in the global namespace but not local variables.

Require

The Kernel#require method loads a file provided to it as a string argument. It returns true if successful, false if the file is already loaded or raises LoadError if file was not loaded successfully and is not already loaded.

With require Ruby searches for the file in the absolute path and if the file is not found it searches in the paths listed under $LOAD_PATH (i.e. $:). It is possible to provide a relative path to require but this will search for the file in the path relative to $LOAD_PATH and not the calling file path.

require makes all constants and global variables to be available in the global namespace but not local variables.

Require Relative

The Kernel#require_relative method loads a file provided to it as a string argument but as opposed to require method does not look for the file in the absolute path or in the paths listed under $LOAD_PATH but relatively to the path of the calling file.

We use cookies and similar technologies to enhance the quality of services, maintain statistics and adjust marketing content. You will find more information in the Cookies Policy.

By clicking OK you grant consent to processing of your personal data by us and our Trusted Partners with the purpose of maintain statistics and adjustment of the marketing content pursuant to the Privacy Policy. If you wish to not grant that consent and/or limit its extent click Settings.