We are using Jupyter Notebooks to make this tutorial. For those who don't know about Jupyter Notebooks, go here. Below is a sample, about how notebooks work.
my_num = 15
my_string = "Tushar"
my_bool = true
my_own_string = "tuteja"
puts my_num, my_string, my_bool
puts my_own_string
15
Tushar
true
tuteja
Ruby is a dynamically typed Language, as we see in the above code, we assigned a number, a string value and a boolean to three different variables
'puts' is a function that takes a list of arguements and prints to console on a new line, You may use print statement as well, the only difference is, it prints in continuation.
print my_num, my_string, my_bool
15Tushartrue
my_num, my_string and my_bool are variables which are holding different objects. For naming variables in ruby we use snakecase as a convention.
Everything is a an Object in Ruby,
Everything!
To find out class of a ruby object we can call a 'class' method on any object.
puts my_num.class() , my_string.class() , my_bool.class()
Fixnum
String
TrueClass
Basic Arithmetic operators are '+','-','*','/' and '%'.
five = 5
two = 2
seven = 5 + 2
7
ten = five * two
10
one = five % two
1
two = five / two
2
three = five - two
3
my_string = "Programming in Ruby Is Fun."
"Programming in Ruby Is Fun."
my_string.length()
27
my_string.reverse()
".nuF sI ybuR ni gnimmargorP"
puts my_string
Programming in Ruby Is Fun.
my_string.reverse!()
".nuF sI ybuR ni gnimmargorP"
puts my_string
.nuF sI ybuR ni gnimmargorP
my_string = "Programming in Ruby Is Fun."
my_string.split(" ")
["Programming", "in", "Ruby", "Is", "Fun."]
words = my_string.split(" ")
puts words #Gives an array, we'll come to it
["Programming", "in", "Ruby", "Is", "Fun."]
word = "Programming"
"Programming"
word.downcase() #Get a word with lower case letters
"programming"
word.upcase() # get a word with upper case letters
"PROGRAMMING"
#This is a single line comment
puts "comment" #this can come at the end of any expression
=begin
This is a multiline comment.
=begin should start at the first character of the line to be it a comment, other wise it wont work
=end
comment
For methods and variables we follow snake case like example_variable, example_function. For Classes we follow upper case letters, MyOwnClass. For functions which are risky to use, like reverse!, we add exclamation mark at the end.
my_string = "tushar tuteja"
"tushar tuteja"
my_string.reverse().upcase() # You can chain function calls one after the other
"AJETUT RAHSUT"
my_name = "Tushar Tuteja"
my_city = "Delhi"
puts "Hi, I am #{my_name}, I belong to #{my_city}." #way to format complex strings.
Hi, I am Tushar Tuteja, I belong to Delhi.
if true
puts "true"
else
puts "false"
end
true
false == "false" # this is false
false
false == nil # this is false
false
my_object = nil
my_object == nil
true
my_object.nil? # this is the way to check nil
true
Ruby has infinite true expressions, only two false expressions, one is false and the other is nil. So 0 would evaluate to true, this is different form C and C++ language.
if my_object
puts "true"
else
puts "nil or false"
end
nil or false
my_object = 0
if my_object
puts "this evaluates to true"
else
puts "should have been false if we were coding in C."
end
this evaluates to true
first_condition = false
second_condition = true
if first_condition
puts "first condition is true"
elsif second_condition
puts "second condition is true"
elsif 0 > 3
puts "won't happen"
end
second condition is true
hungry = true
true
if not hungry
puts "keep working"
else
puts "eat"
end
eat
Ruby gives us more than if else, it gives us unless else, which is more verbose than if not. Example
unless hungry
puts "keep working"
else
puts "Eat"
end
Eat
5 == '5'
false
5*4 == 4*5
true
4 < 7
true
8 > 9
false
"tushar" > "tuteja"
false
5 >= 5
true
4 != 5
true
!true
false
work_done = true
if work_done || hungry
puts "eat"
else
puts "work"
end
eat
puts false || true
puts true || false
puts true && true
puts true && false
puts false || false
true
true
true
false
false
if not false
puts "it is true"
else
puts "it is false"
end
it is true
i = 0
while i < 5
puts "ruby is fun"
i = i + 1
end
ruby is fun
ruby is fun
ruby is fun
ruby is fun
ruby is fun
i = 5
while i != 0
puts "ruby is more verbose"
i = i - 1
end
ruby is more verbose
ruby is more verbose
ruby is more verbose
ruby is more verbose
ruby is more verbose
i = 5
until i == 0
puts "ruby is more verbose"
i = i - 1
end
ruby is more verbose
ruby is more verbose
ruby is more verbose
ruby is more verbose
ruby is more verbose
for i in 1..5
puts "ruby has many ways to do the same thing."
end
ruby has many ways to do the same thing.
ruby has many ways to do the same thing.
ruby has many ways to do the same thing.
ruby has many ways to do the same thing.
ruby has many ways to do the same thing.
1..5
for i in 1...5
puts "..(two dots) inclues 1 and 5 both, where as ...(three dots) doesn't include 5"
end
..(two dots) inclues 1 and 5 both, where as ...(three dots) doesn't include 5
..(two dots) inclues 1 and 5 both, where as ...(three dots) doesn't include 5
..(two dots) inclues 1 and 5 both, where as ...(three dots) doesn't include 5
..(two dots) inclues 1 and 5 both, where as ...(three dots) doesn't include 5
1...5
5.times do
puts "this needs to be done 5 times"
end
this needs to be done 5 times
this needs to be done 5 times
this needs to be done 5 times
this needs to be done 5 times
this needs to be done 5 times
5
my_string = "Ruby is made for productivity."
words = my_string.split(" ")
words.each do |word| #would discuss more of this in arrays and hashes
puts word.capitalize # This would capitalize every word
end
Ruby
Is
Made
For
Productivity.
["Ruby", "is", "made", "for", "productivity."]
a = [] # this creates an empty array
[]
Arrays are lists in ruby, means they doesn't need to be homegenous.
a = ["tushar", 5, [4,5,6]]
["tushar", 5, [4, 5, 6]]
Arrays are continuous data structures
a = []
a[45] = 5
puts a
[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 5]
a[45] = 0, created 45 empty spaces and then added 5 at 45th index. Indexing starts from zero.
puts a[45]
5
puts a[0] # Would print nothing
puts a.length
46
Sets are used to keep unique elements, no duplicates are allowed.
my_set = Set.new() # New Empty Set
my_set.add(1) # Add one Element
my_set.each do |v|
puts v
end
my_set.add([1,2,3,4,5]) # adding [1,2,3,4,5] as a single element
my_set.each do |v|
puts v
end
1
1
[1, 2, 3, 4, 5]
#<Set: {1, [1, 2, 3, 4, 5]}>
a = [1,2,3,4,5,1,2,3,4,5]
my_set = a.to_set # Easy way to create a set from array
puts my_set.size
5
my_set = Set.new([1,2,3,1,2,3]) # Easy way to create a set while initializing
#<Set: {1, 2, 3}>
Hashes are smilar to javascript dictionaries, the only difference is that they are ordered.
my_hash = {} # creates a new Hash
{}
my_hash = Hash.new # creates a new Hash
{}
my_hash = {:name => "Tushar", :city => "Delhi"} # Creates a hash
{:name=>"Tushar", :city=>"Delhi"}
my_hash[:name] # to access :name from my_hash
"Tushar"
:name and :city are actually symbols. they are used for faster accessing, we may use strings as well.
my_hash = {"name" => "tushar"}
{"name"=>"tushar"}
puts my_hash["name"]
puts my_hash[:name] # This won't work, you need to remember that strings and symbols are different
tushar
my_hash = {:name => "Tushar", :city => "Delhi", :age => 25}
{:name=>"Tushar", :city=>"Delhi", :age=>25}
my_hash.each do |key| # iterating over the keys
puts key
end
[:name, "Tushar"]
[:city, "Delhi"]
[:age, 25]
{:name=>"Tushar", :city=>"Delhi", :age=>25}
If you look at the above code, the keys are ordered.
my_hash.each do |key,value|
puts "#{key} : #{value}"
end
name : Tushar
city : Delhi
age : 25
{:name=>"Tushar", :city=>"Delhi", :age=>25}
def my_method
puts "hello method"
end
:my_method
my_method()
hello method
my_method # parenthesis are optional in ruby, it makes the code more verbose
hello method
def is_odd number # parenthesis are optional
return number % 2 == 1
end
is_odd 1 # parenthesis are optional
true
def is_even number
number % 2 == 0 #last expression in every function is a return statement by default.
end
:is_even
is_even 2
true
def join_words words_array
result = ""
words_array.each do |word|
result = result + word + " " # String concatenation
end
result
end
puts join_words ["Ruby","is","flexible"]
Ruby is flexible
What is you want to pass a variable number of arguments and you don't want to pass an array.
Voila, we have *args arguement.
def join_words *args
result = ""
args.each do |word|
result = result + word + " " # String concatenation
end
result
end
puts join_words "Ruby","is","flexible" # This code is more verbose
puts join_words "Ruby","is","flexible",",", "This", "code" ,"is" ,"more" ,"verbose"
Ruby is flexible
Ruby is flexible , This code is more verbose
class MyClass # Defines a new Class
end
my_class = MyClass.new # Creates a new instance of MyClass Object
#<MyClass:0x007fde14956ec0>
By default every class Inherits from Object class. When We Called MyClass.new , we called a constructor of Object class.
class MyClass
def initialize #Constructor method
puts "Constructor of MyClass is called"
end
end
:initialize
my_class = MyClass.new
Constructor of MyClass is called
#<MyClass:0x007fde13b31778>
Classes contain methods and variables. By Default Methods are of two types
- Instance Methods, that belongs to one instance of a class
- class methods, that belongs to one class. Visibility of Methods is of three types
- private
- protected
- public, by default every method is public.
Variables could be of three types
- Local Variable in a function
- Instance variables
- Class variables
We'll discuss all of above in the following section
class Person
def initialize (name,age)
@name = name # this is how we declare instance variable, by having @ at the start
@age = age # same with age
end
end
:initialize
person = Person.new("Tushar",25)
#<Person:0x007fde140fa1f0 @name="Tushar", @age=25>
person.public_methods
[:name, :name=, :details, :is_adult, :instance_of?, :public_send, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :instance_variables, :tap, :is_a?, :extend, :define_singleton_method, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :display, :send, :object_id, :to_s, :method, :public_method, :singleton_method, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :frozen?, :public_methods, :singleton_methods, :!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]
We didn't define public_methods function, but it was called and it gave us some results. This is method that every object inherits from Object class.
public_method functions gives us all the functions that could be called on that Object. In total Object class gives us 56 methods, which is a good thing and a bad thing. Bad thing as in now we have 56 names where our own name can collide. example, if we were to make SMS class, we would have send method, that would override the send method from Object class.
Lets add some more functionality to our Person class
class Person
def initialize (name,age)
@name = name # this is how we declare instance variable, by having @ at the start
@age = age # same with age
end
def details # By default this method would be public
"Name: #{@name}"
end
end
:details
person = Person.new("Tushar",25)
#<Person:0x007fde132cba20 @name="Tushar", @age=25>
person.details
"Name: Tushar"
Every Instance variable in any object is a private variable. You need to write getter methods for them. I can not access name and age directly from the person Object. So lets add those methods. This is a good news, as we don't want to reveal the age of the person, but we should have a way to check if the person is adult or not.
class Person
def initialize (name,age)
@name = name # this is how we declare instance variable, by having @ at the start
@age = age # same with age
end
def details # By default this method would be public
"Name: #{@name}"
end
def name
@name
end
def is_adult
@age > 18
end
end
:is_adult
person = Person.new("Tushar",25)
puts person.name, person.is_adult
Tushar
true
We cannot change name or age once set, what if we want to change them, we need to add a setter method. Which is done in ruby as follows.
class Person
def initialize (name,age)
@name = name # this is how we declare instance variable, by having @ at the start
@age = age # same with age
end
def details # By default this method would be public
"Name: #{@name}"
end
def name
@name
end
def name= name # we are defining a special method, with '=' in the signature
@name = name
end
def is_adult
@age > 18
end
end
:is_adult
person = Person.new("Tushar",25)
puts person.name
person.name = "Tuteja"
puts person.name
Tushar
Tuteja
class Person
def initialize (name,age)
@name = name # this is how we declare instance variable, by having @ at the start
@age = age # same with age
end
def details # By default this method would be public
"Name: #{@name}"
end
def name
@name
end
def name= name # we are defining a special method, with '=' in the signature
@name = name
end
def is_adult
@age > 18
end
private # special keyword in ruby, after this everything we write would be private
def full_details # A private function
return @name, @age # Yes you can return more than one value, that would be returned as an array
end
end
:full_details
person = Person.new("Tushar",25)
person.full_details # Would throw an error
NoMethodError: private method `full_details' called for #<Person:0x007fde131fbcf8 @name="Tushar", @age=25>
<main>:1:in `<main>'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/backend.rb:44:in `eval'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/backend.rb:44:in `eval'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/backend.rb:12:in `eval'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/kernel.rb:87:in `execute_request'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/kernel.rb:47:in `dispatch'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/kernel.rb:37:in `run'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/command.rb:70:in `run_kernel'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/command.rb:34:in `run'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/bin/iruby:5:in `<top (required)>'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/bin/iruby:22:in `load'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/bin/iruby:22:in `<main>'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/bin/ruby_executable_hooks:15:in `eval'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/bin/ruby_executable_hooks:15:in `<main>'
class RichPerson < Person # RichPerson Inherits from Person
def print_details
puts full_details # RichPerson Objects are able to call private functions of their parent classes,
end
end
:print_details
rich_person = RichPerson.new("Tushar",25)
ArgumentError: wrong number of arguments (given 2, expected 4)
<main>:5:in `initialize'
<main>:in `new'
<main>:in `<main>'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/backend.rb:44:in `eval'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/backend.rb:44:in `eval'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/backend.rb:12:in `eval'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/kernel.rb:87:in `execute_request'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/kernel.rb:47:in `dispatch'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/kernel.rb:37:in `run'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/command.rb:70:in `run_kernel'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/lib/iruby/command.rb:34:in `run'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/gems/iruby-0.2.9/bin/iruby:5:in `<top (required)>'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/bin/iruby:22:in `load'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/bin/iruby:22:in `<main>'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/bin/ruby_executable_hooks:15:in `eval'
/Users/tushartuteja/.rvm/gems/ruby-2.3.0/bin/ruby_executable_hooks:15:in `<main>'
rich_person.print_details
["Tuteja", 25]
Child Class Objects can Call any private function of parent class.
lets add some more functionality, lets suppose we want to add money in RichPerson class
class RichPerson < Person # RichPerson Inherits from Person
def initialize name, age, money
super name, age # special keyword in ruby to call constructor of parent class
@money = money
end
def money
@money
end
def print_details
puts full_details # RichPerson Objects are able to call private functions of their parent classes,
end
end
:print_details
rich_person = RichPerson.new("Bill Gates",25,1)
#<RichPerson:0x007fde14070270 @name="Bill Gates", @age=25, @money=1>
rich_person.money
1
It is a hassle to write getter method, like we wrote above for money and setter method like we wrote for name. For this ruby gives us short cuts.
class RichPerson
attr_accessor :name, :money # Gives us getter and setter methods for name and money
attr_reader :age # gives us only getter
attr_writer :phone # gives us only setter
def initialize(name, money,age,phone)
@name = name
@money = money
@age = age
@phone = phone
end
end
rich_person = RichPerson.new("Tushar",1,25,9999999999)
#<RichPerson:0x007fde13a6b410 @name="Tushar", @age=25, @money=1, @phone=9999999999>
rich_person.age # attr_reader, created a method by the name age that returns @age
25
puts rich_person.name # attr_accesor, created a method by the name that returns @name
rich_person.name = "Tuteja"# attr_accesor, created a method by the name= that sets @name for us
puts rich_person.name
Tushar
Tuteja
Similarly we can only change phone number but cannot see it