Ruby is a dynamically typed, object-oriented scripting language that is interpreted. It was created by Yukihiro Matsumoto in 1995 and influenced by Smalltalk, Perl, Lisp and Python. Some key features of Ruby include being garbage collected, supporting inheritance and mixins, and being suitable for meta-programming. It gained popularity through frameworks like Ruby on Rails. The core data types in Ruby are implemented as objects and include integers, floats, strings, arrays, hashes, symbols, booleans and nil. Classes and modules provide namespaces and support object-oriented programming principles like inheritance. Ruby supports functional programming patterns through blocks, procs and lambdas.
6. Hello World
(and a glimpse at Ruby Syntax)
print("Hello World");
print("Hello World")
• Semicolons are optional:
• Practically never used.
• Use newline instead.
• Classic (and verbose)
• Parenthesis in methods are optional:
• Cleaner syntax!
• Works well for DSLs (will see later...).
print "Hello World"
Cool, but pay attention:
f(3+2) + 1
f (3+2) + 1
6
7. Data Types
• Ruby has many built-in data types:
• Numeric: Integer, Floats, Complex, Rational, etc.
• String: single-quoted, doubly-quoted, here documents,
backtick, characters, etc.
• Array
• Hash
• Boolean and nil
• etc.
7
8. Data Types
• Ruby has many built-in data types:
• Numeric: Integer, Floats, Complex, Rational, etc.
• String: single-quoted, doubly-quoted, here documents,
backtick, characters, etc.
• Array
• Hash
• Boolean and nil
• etc.
All are implemented
by objects!
7
9. Numeric Types
• Implemented as objects
•All inherits from Numeric class
404 # Fixnum
1_000_000_000 # Better readability
241.1 # Float
-3.7e4 # Float
0xFF # Fixnum given in hex
1 + 1.0 # Float
8
10. Strings
• Enclosed in single or double quotes
• Small differences between the two
• String interpolation
• Unicode escapes
• Mutable objects
"six times four is: #{6*4}"
=> "six times four is: 24"
"this is a string"
=> "this is a string"
'this is a string too'
=> "this is a string too"
"u20ac"
=> "€"
9
11. Arrays
• Dynamically sized
• Untyped and mutable
• Example:
[1, 2, 3] # an array with three Fixnum objects
[] #empty array
[0] * 10 # new array containing 10 zeros
["a string", 42, 3.3, []] # can hold any type of object
a = [1, 1, 2, 3, 5, 8]
a[0] # first element is 1
a[-1] # last element is 8
a[10] # nil if exceeding limits
a[7] = 21 # extends the array (with nil)
a[2,3] # subarray starting from 2, 3 elements long
a[2,3] = ['A','B','C'] #substitute subarray
a + [0, -1] # concatenates two arrays
[1, 2, 3, 4, 5] - [2, 4] # removes elements from the first array
10
12. Hashes
• Unordered collection of key-value pairs
• Mutable
• Indexed by keys
a = {} # create an empty hash
a['one'] = 1 # map string 'one' to Fixnum 1
a['two'] = 2# map string 'two' to Fixnum 1
a = {'one' => 1, 'two' => 2} # create and initialize Hash
a['one']
=> 1
11
13. Symbols
• Same as scheme symbols
• Constant, immutable strings
• I.e., placeholder for identifiers and strings
• Always prefixed by a semicolon
• Created in memory as soon as referenced
:red # create a new symbol called red
"blue".to_sym # convert a string to a symbol
=> :blue
:"red" # same as :red
hash = {:red => "red", :blue => "blue" } # often used in hashes
(i.e., fast)
12
14. Boolean and Nil
• true: logical true value
• Singleton instance of TrueClass
• false: logical false value
• Singleton instance of FalseClass
• nil: null value (i.e., absence of value)
• Singleton instance of NilClass
13
15. Rules for objects
1. All variables are references to objects (there are no
primitive types!)
2. Objects communicate via messages (i.e., method calls)
3. Each object has its own (private) state
4. Every object is an instance of a class
14
16. Object References
• It always works with references to object (i.e., similar
to Scheme, Java)
• Call by object sharing:
• When an object is passed to a method, its reference
is passed (i.e., the reference is copied).
s = "Ruby" # Creates a String object. Store the reference to it in s
t = s # Copy reference to t
t[-1] = "" # Modify the object through reference to t
print s # Access (modified) object through s
t = "Java" # t now refers to a different object
print s,t # Prints "RubJava"
15
17. Classes and methods
• class keyword to define a class
• def to define methods
class ClassName
" def method1(par1, par2)
" " expression1
" end
" def method2(par1, par2)
" " expression2
" end
end
16
18. Constructor and instance
variables
• Method initialize is the constructor
• Instance variables start with @ symbol
class Point
" def initialize(x,y)
" " @x = x
" " @y = y
" end
" def to_s()
" " return "#{@x},#{@y}"
" end
end
17
19. Cleaner version...
• Ruby supports “parallel assignments”
• Return is implicit (i.e., like Scheme):
• Last expression is returned if not specified
class Point
" def initialize(x,y)
" " @x, @y = x, y
" end
" def to_s()
" " "#{@x},#{@y}"
" end
end
Instance variables are
created only when stored
for the first time
18
20. Creating an object
• Invoke constructor with new
p = Point.new(10,20)
• Invoking a method:
print p.to_s
• Sends the to_s message to object referenced by p
19
21. Getters and Setters
• Lets define getters and setters for Point class
class Point
" def initialize(x,y)
" " @x, @y = x, y
" end
" def x
" " @x
" end
" def y
" " @y
" end
" def x=(value)
" " @x = value
" end
" def y=(value)
" " @y = value
" end
end
p = Point.new(10,20)
p.y = 10 # notice the space
p.x= 20 #
print p.x # same as p.x()
print p.y
Look like variable references.
They are actually method calls
(without parentheses)!
20
22. Getters and Setters
• Concise way to define getters and setters
• Use symbols to identify the instance variable
class Point
"
" attr_accessor :x, :y
" def initialize(x,y)
" " @x, @y = x, y
" end
end
• attr_accessor creates getters and setters
• attr_reader creates getters only
• attr_writer creates setters only
21
23. Defining new operators
class Point
" attr_accessor :x, :y
" def initialize(x,y)
" " @x, @y = x, y
" end
" def +(other)
" " Point.new(@x + other.x, @y + other.y)
" end
" def -@ # Unary operator
" " Point.new(-@x, -@y)
" end
" def *(scalar)
" " Point.new(@x * scalar, @y * scalar)
" end
end
+, - and * are just
method names!
22
24. ClassVariables and
Class methods
• Class variables are shared among all
instances of that class
• Introduced with @@ prefix
• Class methods are methods
associated with a class (not with an
instance)
• Introduced by prepending self. to
the method name
class Point
" attr_accessor :x, :y
" @@total_points = 0
" def initialize(x,y)
" " @x, @y = x, y
" " @@total_points += 1
" end
" def self.total_points
" " @@total_points
" end
end
p1 = Point.new(10,15)
p2 = Point.new(20,30)
print Point.total_points
23
25. Inheritance
• By default a class extends the Object class
• Inherits all predefined methods
• Classic subclass/superclass relationships
• No multiple inheritance
• Methods can be overridden
• Instance variables are not inherited!
class Point3D < Point
" attr_accessor :z
" def initialize(x,y,z)
" " super(x,y)
" " @z = z
" end
"
end
24
26. Modules
• A container of:
• Methods
• Constants
• Class variables
• Modules cannot be instantiated
• Effective way to create namespaces
module Base64
" DIGITS = ('A'..'Z').to_a
" def Base64.encode
" end
" def Base64.decode
" end
end
Base64.encode
Base64.decode
Base64::DIGITS
25
27. Nesting namespaces
• Modules and classes can be
nested
• Better readability and avoid
name clashes
module Base64
" DIGITS = ('A'..'Z').to_a
" class Encoder
" " def encode
" " end
" end
" class Decoder
" " def decoder
" " end
" end
end
a = Base64::Encoder.new
26
28. Mixins
• If module define some instance methods, they can
be “mixed-in” a class.
• Mixin code can interact with code in the class
module A
" def a
" end
end
module B
" def b1
" end
" def b2
" end
end
class MyClass
" include A
" include B
" def m
" end
end
m = MyClass.new
m.a
m.b1
m.b2
27
29. Control structures
• Classic control structures:
• If, else, elsif, case, etc. (terminated with “end” keyword)
• Plus some syntactic sugars:
print "greater" if Math::PI > 3Conditional modifier
Unless
unless Math::PI <= 3
" print "greater"
end
28
30. Loops
• Classic for, while, until, etc:
• The object must have an each method
x = 0
puts x += 1 while x < 10
x = 0
puts x += 1 until x >= 10
array = [1,2,3,4]
for element in array
" puts element
end
hash = {:a => 1, :b => 2, :c => 3}
for key, value in hash
" puts "#{key} => #{value}"
end
Also with modifiers version of loops (one line)
29
31. Iterators and yield
• Allow to loop over a collection (or value)
• Receive a block of code which is executed with the
current element
• Use yield to execute the passed block
30
32. Iterators and yield
• Allow to loop over a collection (or value)
• Receive a block of code which is executed with the
current element
• Use yield to execute the passed block
3.times{ puts "thank you!"}
data.each{|x| puts x}
[1,2,3].map{|x| x * x }
factorial = 1
2.upto(n) {|x| factorial *= x}
30
33. Iterators and yield
• Allow to loop over a collection (or value)
• Receive a block of code which is executed with the
current element
• Use yield to execute the passed block
3.times{ puts "thank you!"}
data.each{|x| puts x}
[1,2,3].map{|x| x * x }
factorial = 1
2.upto(n) {|x| factorial *= x}
30
35. Yield examples
def my_method
" yield
end
my_method{ puts "hello!"}
# This method expects a block.
# It generates n values
# of the form m*i + c for 0..n-1,
# and yields them, one at a time
def sequence(n, m, c)
" i = 0
" while i < n
" " yield m * i + c
" " i += 1
" end
end
sequence(5,3,20){|y| puts y}
Yield without parameters Yield with parameters
31
36. Blocks
• Delimited with curly braces or do end keywords (i.e., multi line).
• If method does not “yield”, the block is ignored.
• Value returned from a block is the the value of the last expression.
• Invoking return within a block returns from the calling method!
def print_sequence
" SPECIAL_VALUE = 5
" s = sequence(5,3,20) do |y|
" " if y == SPECIAL_VALUE
" " " return 0
" " end
" " y * 2
" end
" print s
end
The return statement exits
from the print_sequence
method!
32
37. Procs and Lambdas
• Blocks are syntactic structures
• They are not objects!
• However, it is possible to create
block objects:
• Proc
• Lambda
• Invoke method call
33
38. Procs and Lambdas
• Blocks are syntactic structures
• They are not objects!
• However, it is possible to create
block objects:
• Proc
• Lambda
• Invoke method call
p = Proc.new{|x,y| x + y}
puts p.call(10,20)
Creating a proc object
33
39. Procs and Lambdas
• Blocks are syntactic structures
• They are not objects!
• However, it is possible to create
block objects:
• Proc
• Lambda
• Invoke method call
p = Proc.new{|x,y| x + y}
puts p.call(10,20)
Creating a proc object
l = lambda{|x,y| x + y}
puts l.call(10,20)
Creating a lambda object
33
40. Procs and Lambdas
• Blocks are syntactic structures
• They are not objects!
• However, it is possible to create
block objects:
• Proc
• Lambda
• Invoke method call
p = Proc.new{|x,y| x + y}
puts p.call(10,20)
Creating a proc object
l = lambda{|x,y| x + y}
puts l.call(10,20)
Creating a lambda object
Get the “arity” of a block
l = lambda{|x,y| x + y}
puts l.arity
=> 2
33
41. Converting blocks to procs
• Blocks do not have a name
• Blocks can be converted into proc
• Use & (i.e., ampersand)
# Explicit conversion into proc
def sequence3(n, m, c, &b)
" i=0
" while(i < n)
" " b.call(i*m + c)
" " i += 1
" end
end
sequence3(5, 2, 2) {|x| puts x }
def sequence4(n, m, c, b)
" i=0
" while(i < n)
" " b.call(i*m + c)
" " i += 1
" end
end
p = Proc.new {|x| puts x} # create proc
sequence4(5, 2, 2, p) # ordinary argument
34
42. Procs vs Lambdas
• Lambda behave similar to method calls
• Blocks behave similar to block evaluation
35
43. Procs vs Lambdas
• Lambda behave similar to method calls
• Blocks behave similar to block evaluation
def test
" puts "entering method"
" p = Proc.new { puts "entering proc"; return }
" p.call # Invoking the proc makes method return
" puts "exiting method" # This line is NEVER executed
end
Return with proc
35
44. Procs vs Lambdas
• Lambda behave similar to method calls
• Blocks behave similar to block evaluation
def test
" puts "entering method"
" p = Proc.new { puts "entering proc"; return }
" p.call # Invoking the proc makes method return
" puts "exiting method" # This line is NEVER executed
end
def test
" puts "entering method"
" p = lambda { puts "entering lambda"; return }
" p.call # Invoking the lambda does not make the method
return
" puts "exiting method" # This line IS executed
end
Return with proc
Return with lambda
35