Ruby Tutorial
What is Ruby?
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 railsEverything 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.
=endObject-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 # => 70102024293860Sending 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:ClassSelf
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 # => mainMethods 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
Introduction
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) # => 6Method 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) # => 15With 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) # => 4Blocks
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 } # => 8Yielding 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 } # => 14Modules & Classes
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 # => ClassInstances
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 # => AnimalMethods & 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
endIncluding 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) # => 4Prepending 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 # => nilif
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
endif 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
endif 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
endif 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
endcase
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
endThe 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
Definition
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 # => 9876543210The 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 => nilA similar construct can be created with an until keyword.
count = 10
until count == 0 do
count -= 1
print count
end # 9876543210 => nilThe 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
endThe 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 => nilWhen 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 => nilIn 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
endRaising
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 0It 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 Values
==
== 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" # => trueComparing 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 # => trueIntegers
Integer 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 # 70275396044460Symbols
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 # 676828Enumerators
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 endCreating 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 # => 8Arrays
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' # => CRanges
What Is a Range in Ruby?
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 & Date
Introduction
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 2007Time 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 UTCFormatting 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:...>
# feathersLambdas
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) # => 42Converting 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
What Are Regular Expressions in Ruby?
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') # => trueMatch
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') # => nilThe 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.