Ruby

File size
13.6KB
Lines of code
469

Ruby

In Ruby, everything is an object.

Printing

puts "this prints with a newline" # puts
print "this prints without a newline/n" # print

Comment

# single-line comment

=begin
this
is
a
multi-line
comment
=end

# everything is an object in Ruby

3.class # Integer
"Hello".class # String
"Hello".method(:class).class # Method
:pending.class # Symbol

Variables, Constants, Scope

# ---------- VARIABLE ----------
    # variable assignment returns the value assigned
    # by convention, snake_case used for variable names

a_variable = 25
another_variable = "awesome sauce"
yet_another_variable = true

x = y = 10 # this will assign 10 to y, and that will then return 10 which is assigned to x

# ---------- SCOPE ----------
    # $ for global variables
    # @ for instance scope 
    # @@ for class scope
    # capitalised variable names for constants

$var = "I'm a global variable"
@var = "I'm an instance variable"
@@var = "I'm a class variable"
Var = "I'm a constant"

Types

# ---------- STRING ----------

eg_string = "hello world" 

# FORMATTED STRINGS

placeholder = "watermelon"
put "I really am the largest #{placeholder} in this County." # formatted string

# STRING CONCATENATION
    # + can concatenate two strings together but not with other types

"hello" + "world" # "hello world"
"hello" + 3.to_s # "hello3", this is okay because the number 3 undergoes type conversion
"hello#{3}" # "hello3"
"hello" << " world" # "hellow world", appends a string to a string

# STRING MULTIPLICATION
    # just like Python!

"hello" * 3 # evaluates to "hellohellohello"
"ok and " * 2 # evaluates to "ok and ok and "

# ---------- NUMBER ----------
    # covers integers, floats and doubles like Typescript

eg_number = 1000
another_eg_number = 1.23

# ARITHMETIC OPERATORS
    # arithmetic is just syntatic sugar for calling a method on an object

1 + 1 # addition
8 - 1 # subtraction
10 * 2 # multiplication
35 / 5 # division
2 ** 5 # power 
5 % 3 # modulo
1 < 10 # comparison operators
1 > 10 # comparison operators
1 <= 10 # comparison operators
1 >= 10 # comparison operators
1 == 1 # complete equality, of both type and value
1 != 1 # complete inequality, of both type and value

# ---------- BOOLEAN -----------

eg_bool = true # of the TrueClass
other_eg_bool = false # of the FalseClass

# LOGICAL OPERATORS

true && false # and, evaluates to false
true || false # or, evaluates to true

# by convention, all methods that return booleans end with a question mark
5.even? # returns false
5.odd? # returns true

# ---------- SYMBOL ----------
    # symbols are immutable reusable constants often used in place of strings to convey specific meaning
    # represented internally by an integer

status = :pending
status == :pending # returns true
status == "pending" # returns false
status == :approved # returns false
status == "pending".to_sym # returns true since type conversion of String to Symbol

# ---------- SPECIAL VALUES ----------
nil # evaluates to false, same as null, of the NilClass

Data structures

# BOTH ARRAYS AND HASHES ARE ENUMERABLE!

# ---------- ARRAY ----------
    # similar to Javascript arrays, arrays can store multiple values of different types

eg_array = [1,2,3,4,5]
mixed_array = [1, "hello", true]
another_mixed_array = %w[foo bar baz] # this evaluates to ["foo", "bar", "baz"]

# ARRAY METHODS

# INDEXING and SLICES
    # similar to Python
    # this is also just syntatic sugar to call the method [] on the array object

eg_array[0] # returns 1
eg_array.first # also returns 1
eg_array[12] # returns nil since index out of range

eg_array[-1] # returns last element, 5
eg_array.last # also returns 5

eg_array[2,3] # returns subarray of start index and length, [3,4,5]
eg_array[1..3] # returns a subarray of indexes specified in a range, similar to Python, [2,3,4]

## APPEND VALUE

eg_array << 6 # eg_array is now [1,2,3,4,5,6]
eg_array.push(6) # this does the same as the above

## CHECK IF VALUE IN ARRAY

eg_array.include?(1) # evaluates to true
eg_array.include?(100) # evaluates to false

# REVERSE AN ARRAY

[1,2,3].reverse # evaluates to [3,2,1]

# ---------- HASH ---------- 
    # similar to PHP's associative arrays
    # the equivalent of Python's dictionaries or Javascript's objects
    # stores key-value pairs
    # you can use symbols for keys also

eg_hash = {
    "color" => "green",
    "number" => 5
}

eg_hash.keys # returns ["color", "number"]
eg_hash["color"] # returns "green"
eg_hash["number"] # returns 5
eg_hash["nothing here"] # returns nil since the key doesn't exist 

# to use symbol as keys
another_hash = {
    :defcon => 3,
    :action => true
}
another_hash.keys # returns [:defcon, :action]

# an alternative syntax when using symbols for keys

yet_another_hash = {
    defcon:3, 
    action:true
}
yet_another_hash.keys # also returns [:defcon, :action]

yet_another_hash.key?(:defcon) # checks the existence of keys in hash, evaluates to true
yet_another_hash.value?(3) # checks the existence of values in hash, evaluates to true

Control structures and logic flow

# ---------- CONDITIONALS ----------
    # similar syntax to bash
    # postfix-if notation is available also

# if elsif else

if true
    "if statement"
elsif false
    "else if, optional"
else
    "else, also optional"
end

warnings = ["Patronimic is missing", "Address is too short"]
puts("Some warnings occured:\n" + warnings.join("\n")) if !warnings.empty? # postfix-if notation can be used for single statements with no code blocks
puts("Some warnings occured:\n" + warnings.join("\n")) unless warnings.empty? # unless can be used in place with if

# case when else
    # else functions as the default statement
    # cases can also use ranges!

grade = "B"
case grade
when "A"
    puts "lovely"
when "B"
    puts "ok but good job"
when "C"
    puts "watermelon sugar high"
else
    puts "Alternative grading system, eh?"
end

num_grade = 82
case num-grade
when 90..100
    puts "nice one"
when 80..90
    puts "lovely job"
else
    puts "You failed!"
end

# ---------- LOOPS ----------
    # traditional for loops aren't common
    # basic loops are implemented with each enumerable
    # also similar syntax to bash and rust
    # Ruby has other looping functions like map, reduce and inject

# .each DO AND .each_with_index DO LOOPS

# APPROVED SYNTAX AND COMMONLY SEEN
(1..5).each do |counter|
    puts "this is the ${counter}"
end

# this is also approved syntax since blocks can be wrapped in curly braces
(1..5).each {|counter| puts "this is also the #{counter}"}

# APPROVED SYNTAX BUT RARELY SEEN
for counter in 1..5
    puts "iteration #{counter}"
end

# you can also iterate over elements in data structrues like Hashes and Maps
array.each do |element|
    puts "this is an #{element}"
end
hash.each do |key,value|
    puts "this is a #{key} and this is a #{value}"
end

# .each_with_index returns an index with an iterable, similar to enumerate() in python
array.each_with_index do |element, index|
    puts "#{element} and this is an #{index}"
end

# WHILE DO LOOPS
counter = 1
while counter <= 5 do
    puts "iteration #{counter}"
    counter += 1
end

Methods

Since Ruby is largely OOP, functions are methods called on objects.

# ---------- METHODS ----------
    # similar to Scala and Rust, methods implictly return the value of the last statement
    # def
    # yield

def double(x)
    x * 2 # x * 2 is returned
end

double(2) # this returns 4
double 3 # parantheses are optional when interpretation of methods is unambigious, this returns 6

double double 3 # this returns 12

def sum(x,y)
    x + y
end

sum 3,4 # method arguments are separated by commas, this returns 7
sum sum (3,4), 5 # this returns 12

# yield
    # implicit optional block parameter that can be returned

def surround 
    puts "{"
    yield
    puts "}"
end

surround {puts "hello world"} # this returns { hello world } with hello world being a variable

# by convention, if the method name ends with an exclamation mark, the method does something destructive like mutate the receiver
    # many methods have ! version that modifies the existing object, and a non-! version that returns a copy of the changed version

Class

# ---------- CLASSES ----------
class Human # create a class

    @@species = "Homo Sapiens" # @@ creates a class variable that is shared across all instances of the Human class

    # INSTANCE METHODS

    def initialize(name, age=0) # default constuctor method, it can be named anything really and serves the purpose of assigning values to the attributes of the instance object
        @name = name
        @age = age
    end

    def name=(name) # basic setter method
        @name = name
    end

    def name # basic getter method
        @name
    end

    # CLASS METHODS
        # only be called on the class, not an instance object

    def self.say(msg) # the self. distinguishes a class method from an instance method
        puts msg
    end

    def species
        @@species
    end

end

# INSTANTIATING A NEW CLASS OBJECT
    # .new()

jim = Human.new("Jimmy Neutron")
dwight = Human.new("Dwight Howard")

# calling instance methods
jim.species
jim.name # returns the name as a String
jim.name = "Jim the science guy" # takes in the assigned String value as the new instance object attribute
dwight.species

# calling class methods
Human.say("Hello sir") # returns "Hello sir"

# INHERITANCE

class Worker < Human # class Worker inherits all attributes and methods of the Human class
end

Exception handling

# ---------- EXCEPTION HANDLING ----------
    # begin
    # raise
    # rescue =>
    # ensure
    # else

begin
  raise NoMemoryError, 'You ran out of memory.' # raises an exception
rescue NoMemoryError => exception_variable
  puts 'NoMemoryError was raised', exception_variable
rescue RuntimeError => other_exception_variable
  puts 'RuntimeError was raised now'
else
  puts 'This runs if no exceptions were thrown at all'
ensure
  puts 'This code always runs no matter what'
end

More on