Ruby Syntax Notes

75 mins read.

As a language, Ruby prioritizes programmer productivity over program optimization. This means that Ruby may not always run a program in the fastest way possible, but it strives to be a language that programmers find easy and fun to use.

The more intuitive a language’s syntax is, the more productive its users can be.

Additional notes

  • By convention, class names start with a capital letter and use CamelCase instead of relying_on_underscores.
  • String interpolation is done by using the #{} when inside a string,
    name = "Nikola Tesla"
    puts "#{name} is the best scientific of the last century"
    

    When working with only string we can also use + and <<, but if we want to use other data types we have to convert them to strings with .to_s

    balance = 15100
    last_expenses = [100, 256, 7863]
    puts "Your balance is: " + balance.to_s + " and your expenses where " << last_expenses.to_s
    Your balance is: 15100 and your expenses where [100, 256, 7863]
    => nil
    

    but that is not the Rubyist way, we better use the interpolation notation

    puts "Your balance is: #{balance} and your expenses where #{last_expenses}"
    

    which give us a smaller and clearer way to output information.

  • In Ruby, curly braces ({}) are generally interchangeable with the keywords do (to open the block) and end (to close it).
    loop { print "Hello, world!" }
    loop do
      print "Hello, world"
    end
    

    As a good practice, is common to use {} when there is a single line of code, and do-end when it is necessary to execute multiple code lines.

  • String interpolation in Ruby is done with #{}, example
      who = "World"
      puts "Hello #{who}!"
      => nil
    
  • You can use !! to know the boolean value of an element, e.g
      !!nil   #=> false
      !!false #=> false
      !!0     #=> true
      !!""    #=> true
      !!true  #=> true
    

    false (not true) and nil (nothing at all) are the only falsely values in Ruby.

  • Combined comparison operator > The combined comparison operator returns:
    • 0 when both operands are equal
    • 1 when first operand is greater
    • -1 when second operand is greater
      1 > 10
      => -1
      10 > 1
      => 1
      10 > 10
      => 0
    
  • Method names usually are semantically relevant:
    • Method names ending with ? return a boolean value.
    • Method names ending with ! change the current data instead of creating a new copy
  • Ruby methods will implicitly return the result of the last evaluated expression. This means that instead of
    def add(a,b)
      return a + b
    end
    

    you can simply write:

    def add(a,b)
      a + b
    end
    
  • Conditional assignment ||= will only assign a value to a variable if it hasn’t been assigned before.
    favorite_book = nil
    puts favorite_book
    
    favorite_book ||= "Cat's Cradle"
    puts favorite_book
    => Cat's Cradle
    
    favorite_book ||= "Why's (Poignant) Guide to Ruby"
    puts favorite_book
    => Cat's Cradle
    
    
    favorite_book = "Why's (Poignant) Guide to Ruby"
    puts favorite_book
    => Why's (Poignant) Guide to Ruby
    
  • Maintaining the principle that there are multiple ways to do things in Ruby, where besides the .push method we can also use the << operator to append an element to the end of the array. The good news is that it also works on strings, e.g.
    [1, 2, 3] << 4
    => [1, 2, 3, 4]
    "hello" << " world!"
    => "hello world!"
    

Data types

  • Booleans true false
  • Numbers 2 4 6, Ruby allows us to write large numbers like 1_000_000 which makes them easier to read.

Strings (or Text literals)

String data types are a sequence of characters, each of which occupies 1 byte of memory. Technically, you could represent the string using an array(or some collection) of characters, similar to that of classic languages like C.

Ruby provides 3 ways of including string literals into your source code:

  1. Single quoted strings (rubocop recommended)

    The easiest way of adding text is by surrounding it with single quote ' symbols. However characters like an apostrophe and a backslash within the string need to be escaped if they are present.

     'Hello! Programmer. How\'s it going?'
    
  2. Double quoted strings

    Double quoted strings are more flexible, and they allow special escape sequences, e.g. \t, \n, which don’t work with single quoted strings. More importantly, they allow the embedding of arbitrary expressions. When a string is created, the expression in the string is evaluated, converted to a string and inserted into the text in place of the expression, i.e interpolation.

     "Hello! There is a tab \t here. Did you see?"
     "My name is Circle, and I love Pi. Pi is equal to #{Math::PI}"
    
  3. Here documents

    This is helpful for putting large amounts of text without worrying about escape sequences or string evaluation. “Here documents” begin with <<- followed immediately by and identifier or string that specifies the ending delimiter (No space is allowed preventing ambiguity with the left-shift operator). The text of the string literal begins on the next line and continues until the text of the delimiter appears on a line by itself.

     document = <<-HERE
     This is a string literal.
     It has two lines and abruptly ends with a newline...
     HERE
    

Encoding

In Ruby, strings are objects of the String class, which defines a powerful set of operations and methods for manipulating text (indexing, searching, modifying, etc.)

Since Ruby 1.9, strings have additional encoding information attached to the bytes which provides information on how to interpret them, e.g.

str = "With ♥!"
print("My String's encoding: ", str.encoding.name)
print("\nMy String's size: ", str.size)
print("\nMy String's bytesize: ", str.bytesize)

produces this output:

My String's encoding: UTF-8
My String's size: 7
My String's bytesize: 9

Some observations about the above code:

  • The string literal creates an object which has several accessible methods.
  • The string hash attached encoding information, indicating it’s a UTF-8 string.
  • A String’s size corresponds to the number of characters we see.
  • A String’s bytesize corresponds to the actual space taken by the characters in memory ( the ♥ requires 3 bytes instead of 1).

Although UTF-8 is the most popular (and recommended) encoding style for content, Ruby supports 100 other encodings (try puts Encoding.list). With this in mind we should learn how to convert between different encodings.

def transcode(iso_8859_1_string)
  iso_8859_1_string.force_encoding(Encoding::UTF_8)
end

String Indexing

The String class provides a convenient array-like square bracket [] operation, which allows you to extract portions of the string as well as altering the content when used on the left side of an assignment.

str = 'Hello World!'
str[str.size-1] # strings are 0-indexed
=> "!"
str[-1] # first character from the end of the string
=> "!"
str[-2] # second last character
=> "d"
str[0] # first character
=> "H"

More often, you’d want to extract specific portions of the string rather than individual characters. To do this use comma separated operands between the square brackets. The first operand specifies an index (which may be negative), and the second specifies a length (which must be non-negative).

str = 'Hello World!'
str[0, 4] # first four characters
=> "Hell"
str[6, 3] # 3 characters starting from index 6
=> "Wor"
str[-1, 1] # 1 character starting from the END of string
=> "!"
str[-3, 3] # 3 characters starting from the last 3 characters of the string
=> "!"
str[-1, 3] # 3 characters starting from the END of string
=> "!"

The same way we can assign/delete/insert characters.

str = 'Hello'
str[str.size, 0] = ' World!' # append by assigning at the end of the string
str
=> "Hello World!"
str[5, 0] = "," # insert a comma after the 5th position (without replacing something)
str
=> "Hello, World!"
str[5, 7] = "" # delete 7 characters starting from index 5 (replace 7 characters with an empty string starting from index 5)
str
=> "Hello!"
str[1, 4] = "allowing" # replace 4 characters with the string starting at index 1
str
=> "Hallowing!"

Iterating on Strings

Since Ruby 1.9, we have explicit choices to iterate over a string bases on what we need to iterate:

  • .each_byte iterates sequentially through the individual bytes that comprise a string.
  • .each_char iterates the characters and is more efficient than [] operator or character indexing.
  • .each_codepoint iterates over the ordinal values of characters in the string.
  • .each_line iterate the lines.
money = "¥1000"
money.each_byte {|x| p x} # first char represented by two bytes
194
165
49
48
48
48
money.each_char {|x| p x} # prints each character
"¥"
"1"
"0"
"0"
"0"
def count_multibyte_char(string)
  multibyte = 0
  string.each_char { |c| multibyte += 1 if c.bytesize > 1 }
  multibyte
end

count_multibyte_char('¥1000')
=> 1

String methods

Text info can be read from varied sources and is often unsuitable for direct processing or usage by core functions. This necessitates methods for post-processing and data-fixing.

  • String.chomp(separator=$/)

    Returns a new string with the given separator removed from the end of the string (if present). If $/ has not been changed from the default Ruby record separator, then chomp also removes carriage return characters, i.e it will remove \n, \r, \r\n.

      "Hello World!  \r\n".chomp
      => "Hello World!  "
      "Hello World!".chomp("orld!")
      => "Hello W"
      "hello \n there".chomp
      => "hello \n there"
    
  • String.strip

    Returns a new string with the leading and trailing whitespace removed.

      "    hello    ".strip
      => "hello"
      "\tgoodbye\r\n".strip
      => "goodbye"
    
  • String.chop

    Returns a new string with the last character removed. Note that carriage returns (\n, \r, \r\n) are treated as single characters and, in the case they are not present, a character from the string will be removed.

      "string\n".chop
      => "string"
      "string".chop
      => "strin"
    

A use example:

def process_text(strings_array)
  strings_array.inject("") do |processed, string|
    processed << " " << string.strip.chomp
  end.strip
end

There are also methods for search and replace portions of the string based on a text or pattern.

  • String.include?(string)

    Returns true if str contains the given string or character.

      "hello".include? "lo"
      => true
      "hello".include? "ol"
      => false
    
  • String.gsub(pattern, <hash|replacement>)

    Returns a new string with all the occurrences of the pattern substituted for the second argument: The pattern is typically a Regexp, but a string can also be used.

      "hello".gsub(/[aeiou]/, '*')
      => "h*ll*"
      "hello".gsub(/([aeiou])/, '')
      => "hll"
    

Either method will depend upon the problem you are trying to solve, and the nature of input-output behavior you desire, e.g.

def strike(string)
  "<strike>#{string}</strike>"
end

def mask_article(string, words)
  words.inject(string) { |masked, word| masked.gsub(word, strike(word)) }
end

strike("Meow!")
=> "<strike>Meow!</strike>"
strike("Foolan Barik")
=> "<strike>Foolan Barik</strike>"
mask_article("Hello World! This is crap!", ["crap"])
=> "Hello World! This is <strike>crap</strike>!"

Arrays

You can make an array of any collection of Ruby objects, e.g

[1, 2, 3, 5]
=> [1, 2, 3, 5]
["a", "b", "z"]
=> ["a", "b", "z"]
[true, false, false, true]
=> [true, false, false, true]
[true, 235, "Hello world", [2, 6, false]]
=> [true, 235, "Hello world", [2, 6, false]]

Array initialization

The are various method to initialize a ruby array:

  • Initialize an empty array
    array = Array.new
    # or
    array = []
    
  • Initialize and array with exactly one nil element
    array = Array.new(1)
    # or
    array = [nil]
    
  • Initialize an array with exactly two elements with value 10
    array = Array.new(2, 10)
    # or
    array = [10, 10]
    

Accessing array elements

You can access array elements by using the [] operator or the .at() method in each one using the index of the element.

The [] operator is the most powerful option to access arrays elements, as it can take various arguments:

arr = [ 9, 5, 1, 2, 3, 4, 0, -1 ]
arr[4]  # a number which is the position of element
=> 3
arr[1..3] # a range where both index are inclusive
=> [ 5, 1, 2 ]
arr[1...3] # a range where the las index is excluded
=> [ 5, 1 ]
arr[1, 4] # a start index and the length of the range
=> [ 5, 1, 2, 3 ]

Some other ways to access array objects in Ruby are:

arr = [ 9, 5, 1, 2, 3, 4, 0, -1 ]
arr[-1] # access the elements from the end of the list, use negative indices
=> -1
arr.first # first element of the array
=> 9
arr.last # last element of the array
=> -1
arr.take(3) # the first `n` elements of the array
=> [ 9, 5, 1 ]
array.drop(3) # everything but the first `n` elements of the array
=> [ 2, 3, 4, 0, -1 ]

Adding array elements

Arrays provide a variety of methods that allow to add elements to them.

arr = [ 1, 2 ]
arr.push(3) # allow us to append elements to the array
=> [ 1, 2, 3 ]
arr.unshift(0) # allows us to prepend elements to the array
=> [ 0, 1, 2, 3 ]
# insert allows us to add elements starting from a given position, shifting elements after the given index in the process.
arr.insert(2, 1.1, 1.5, 1.9)
=> [ 0, 1, 1.1, 1.5, 1.9, 2, 3 ]

Removing array elements

The array class has various methods of removing elements from the array.

arr = [ 5, 6, 5, 4, 3, 1, 2, 5, 4, 3, 3, 3 ]

# pop removes elements from the end of the array and returns them
arr.pop
=> 3
arr
=> [ 5, 6, 5, 4, 3, 1, 2, 5, 4, 3, 3 ]
arr.pop(2)
=> [ 3, 3 ]
arr
=> [ 5, 6, 5, 4, 3, 1, 2, 5, 4 ]

# shift removes elements form the beggining of the array and returns them
arr.shift
=> 5
arr
=> [ 6, 5, 4, 3, 1, 2, 5, 4 ]

# delete_at deletes one element at a given position
arr.delete_at(2)
=> [ 6, 5, 3, 1, 2, 5, 4 ]

# delete deletes all occurrences of a given element
arr.delete(5)
=> 5
arr
=> [ 6, 3, 1, 2, 4 ]

Selecting array’s elements

The array class also allows to select and return a subset of an array based on some criteria defined in a block.

An element in a block is selected, rejected, deleted or kept based on the true or false value generated by the block on that element.

arr = [ 3, 4, 2, 1, 2, 3, 4, 5, 6 ]
arr.select { |i| i > 2 } # select elements that satisfy a given criteria
=> [ 3, 4, 3, 4, 5, 6 ]
arr.reject { |i| i > 2 } # reject elements that satisfy a given criteria
=> [ 2, 1, 2 ]
arr.drop_while { |i| i > 2 } # drop elements till the block returns false for the first time
=> [ 1, 2, 3, 4, 5, 6 ]
arr
=> [ 3, 4, 2, 1, 2, 3, 4, 5, 6 ]

As noted in the last example, all of this methods are non-destructive, meaning that they return a new array containing all the elements of the array that satisfy the block without modifying the array itself.

For destructive behavior, you can use

arr = [ 3, 4, 2, 1, 2, 3, 4, 5, 6 ]
arr.delete_if { |i| i < 2 } # remove any element that satisfy the block
=> [ 3, 4, 2, 2, 3, 4, 5, 6 ]
arr # note that the array was changed
=> [ 3, 4, 2, 2, 3, 4, 5, 6 ]
arr.keep_if { |i| i < 4 }
=> [ 3, 2, 2, 3 ]
arr # note that the array was changed
=> [ 3, 2, 2, 3 ]

Although ruby provide us with this methods is more common to use the enforced versions of some methods (the same method name but ending with a !), e.g

arr = [ 3, 4, 2, 1, 2, 3, 4, 5, 6 ]
arr.reject! { |i| i < 2 } # remove any element that satisfy the block
=> [ 3, 4, 2, 2, 3, 4, 5, 6 ]
arr
=> [ 3, 4, 2, 2, 3, 4, 5, 6 ]
arr.select! { |i| i < 4 }
=> [ 3, 2, 2, 3 ]
arr
=> [ 3, 2, 2, 3 ]

Hashes

Are collections of key-value pairs which are similar to arrays, but instead of using integers to index and object, hashes use any object as its index. Note that keys must be unique in a hash but values can repeat.

Initializing hashes

  • Literal Notation
hash = {
  "key1" => "value1",
  key2 => value2,
  key3 => value3
}
  • Hash contructor notation (Using the new operator)
my_hash = Hash.new
my_hash["key1"] = "value1"
my_hash[true] = 2
my_hash["key3"] = [2, 4, 5, 6]
my_hash[4] = false

If you try to access a key that doesn’t exist, unlike other languages that return an error of some kind, Ruby returns the special value nil, unless the hash was created with the Hash contructor and was initialized, e.g.

# literal notation
hash = {}
hash["key"]
=> nil
# class constructor
hash = Hash.new(0) # the default value of every key is set to 0
hash["key"]
=> 0

You can also set the default value for hashes’ keys after the hash was declared:

hash = Hash.new
hash.default = 0
hash["key"]
=> 0

Another way of creating a hash populated with the given objects is to use:

Hash[ key, value, ... ] -> new_hash
Hash[ [key, value], ... ] -> new_hash

e.g.


Hash[ "a", 1, "b", 2, "c", 3, "d", 4, "e", 5 ]
=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5}
Hash[("a".."e").to_a.zip((1..5).to_a)]
=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5}

Although when can certainly use strings as Ruby keys, the Rubyist’s way is to use symbols.

menagerie = {
    :foxes => 2,
    :giraffe => 1,
    :weezards => 17,
    :elves => 1,
    :canaries => 4,
    :ham => 1
}

Iterating over Hashes

To iterate on hashes we have a variety of methods:

  • .each can be used in two ways
      hash.each { |key, value| puts "#{key}: #{value}" }
    

    or

      hash.each { |touple| puts "#{touple[0]}: #{touple[1]}" }
      # here touple[0] is the key and touple[1] is the value
    
  • If we only want to iterate over just keys or just values .each_key and .each_value
    my_hash = { one: 1, two: 2, three: 3 }
    
    my_hash.each_key { |k| print k, " " }
    => one two three
    
    my_hash.each_value { |v| print v, " " }
    => 1 2 3
    
  • If we want to be selective .select
    grades = {
      alice: 100,
      bob: 92,
      chris: 95,
      dave: 97
    }
    
    grades.select { |name, grade| grade  97 }
    => { :bob => 92, :chris => 95 }
    
    grades.select { |k, v| k == :alice }
    => { :alice => 100 }
    

Addition, Deletion and Selection over Hashes

Considering the hash object:

hash = Hash.new
hash.default = 0
  • We can add a new key-value pair by using:
     hash["key"] = "value"
    

    or

     hash.store("key", "value")
    
  • An existing key can be deleted using the delete method
    hash.delete("key")
    
  • For destructive selection, we can use .keep_if or .delete_if, similar to how it was used for arrays.
    h = { 1 => 1, 2 => 4, 3 => 9, 4 => 16, 5 => 25 }
    => {1=>1, 2=>4, 3=>9, 4=>16, 5=>25}
    h.keep_if { |key, value| key.even? } # or h.delete_if { |key, value| key.odd? }
    => {2=>4, 4=>16}
    

Symbols

Although you can think of a Ruby symbol as a sort of name, it’s important to remember that symbols aren’t strings, two key differences are:

  1. While there can be multiple instances of a string with the same value, there’s only one instance of any particular symbol at a given time.
    puts "string".object_id
    => 18821600
    puts "string".object_id
    => 18821340
    
    puts :symbol.object_id
    => 802268
    puts :symbol.object_id
    => 802268
    
  2. A symbol is an special kind of object that exist without a lot of baggage.

Symbols make good hash keys for a few reasons:

  1. They’re immutable, meaning they can’t be changed once they’re created
  2. Only one copy of any symbol exists at a given time, so they save memory
  3. Symbol-as-keys are faster than strings-as-keys because the two above reasons.

Symbols always start with a colon :, they must be valid Ruby variable names, i.e the first character after the colon has to be a letter or underscore _; after that, any combination of letters, numbers and underscores is allowed.

The hash rocker syntax :key => "value" changed in Ruby 1.9, the new syntax is similar to the one used by Javascript objects

hash = { one: 1, two: 2, three: 3 }

Converting between strings and symbols

:sasquatch.to_s
=> "sasquatch"

"sasquatch".to_sym
=> :sasquatch

There are always many ways of accomplishing something in Ruby, converting to symbols is no different, besides using .to_sym, you can also use .intern (this will internalize the string into a symbol and works just like .to_sym)

"sasquatch".intern
=> :sasquatch

Call and response (passing methods as symbols)

symbols are awesome for referencing method names. The method .respond_to? takes a symbol as an argument and returns true if an object can receive that method and false otherwise, e.g.

[1, 2, 3].responds_to?(:push)
=> true
# we can call `push` on an array object
[1, 2, 3].respond_to?(:to_sym)
=> false
# since you can't convert an array in to a symbol.

Symbols and Procs

We can also convert symbols to procs using &.

strings = ['1', '2', '3']
nums = strings.map(&:to_i)
=> [1, 2, 3]

Here we are using : to pass the .to_i method as a symbol, then & is used to convert the symbol to a proc and passed to map which usually receives a block (remember a proc is a named block) as an argument.

Enumerable

There are various methods to iterate over a collection like unless, loop and the most commonly used each.

Ruby, however, provides an Enumerable module which packages a bunch of methods which can be sued with any other class by including it (referred to as a mixin in). That means that programmers don’t have to write all those methods many different times for different objects.

As long as the custom object defines an each method and includes Enumerable module, it can get access to all of its magic, i.e each is central to all of the methods provided by Enumerable class.

Iterating with enumerable

  • each

    Probably the most common iterator used in Ruby, required by Enumerable, it calls the given block once for each element in the collection, passing that element as a parameter. It returns the collection itself regardless of what is done by the block passed.

    a = [ 1, 2, 3, 4, 5 ]
    a.each { |i| print "#{i} -- " }
    1 -- 2 -- 3 -- 4 -- 5 --
    => [1, 2, 3, 4, 5]
    
  • each_with_index

    One of the Enumerable useful methods is each_with_index which allows us to iterate over items along with an index keeping count of the item.

    colors = %w[red green blue]
    colors.each_with_index { |item, index| p "#{index}:#{item}" }
    "0:red"
    "1:green"
    "2:blue"
    

The classic triumvirate

Beside simple methods to iterate over objects, Ruby Enumerable provides a number of important higher order constructs:

  • collect a.k.a. map

    Takes a function and maps (applies) it to a collection of values one by one and returns the collection result. This single powerful method helps us to operate on a large number of values at once.

    [1,2,3].map { |x| 2*x }
    => [2, 4, 6]
    {:a=>1, :b=>2, :c=>3}.collect { |key, value| 2*value }
    => [2, 4, 6]
    

    Note that these methods are different from each in the respect that these methods return a new collection while former returns the original collection, irrespective of whatever happens inside the block.

  • reduce a.k.a inject

    A common scenario that arises when using a collection of any sort, is to perform a single type of operation with all the elements and collect the result.

    For example, the sum of all the elements in a collection and return the result.

    # get the sum of all the integers from 1 to 5
    (0..5).reduce { |accumulator, element| accumulator + element }
    => 15
    # when there's only one operation when can use a symbol operator
    (0..5).reduce(:+)
    => 15
    

    We can also pass an initial value to the accumulator

    (0..5).reduce(2) { |accumulator, element| accumulator + element }
    => 17
    (0..5).reduce(3, :+)
    => 18
    

Checking for validity

Ruby offers various enumerables on collections that check for validity of the objects within it, e.g.

arr = [1, 2, 3, 4, 5, 6]
=> [1, 2, 3, 4, 5, 6]
hash = { "a" => 1, "b" => 2, "c" => 3 }
=> {"a"=>1, "b"=>2, "c"=>3}
  • The .any? method returns true if the block ever returns a value other than false or nil for any element passed to it:
    arr.any? { |a| a.even? } # check if any number in the array is even
    => true
    hash.any? { |key, value| value.is_a? String } # check if any value if a string
    => false
    
  • The .all? method returns true if the block never returns false or nil for any collection’s element passed to it:
    arr.all? { |a| a.is_a? Integer } # check if all elements are Integers
    => true
    hash.all? { |key, value| key.is_a? String } # check if all keys are strings
    => true
    
  • The .none? method returns true if the block never returns true for any element passed to it:
    arr.none? { |a| a.nil? } # check if none of the elements are nil
    => true
    hash.none? { |key, value| value < 3 } # check if all values are less than 3
    
  • The .find method returns the first element for which block is not false:
    arr.find { |a| a > 5 } # returns the first element greater than 5, nil if none
    => 6
    hash.find { |key, value| key == "b" } # returns the first match (an array), nil if none
    => ["b", 2]
    

Grouping

Another function often used with data collections is one which groups the elements according to some evaluation result.

  • The .group_by method groups the collection by result of the block. It returns a hash where the keys are the evaluated result from the block and the values are arrays of elements in the collection that correspond to the key, e.g.
    (1..5).group_by { |x| x % 2 }
    => {1=>[1, 3, 5], 0=>[2, 4]}
    (1..5).group_by { |x| x.even? }
    => {false=>[1, 3, 5], true=>[2, 4]}
    

    It’s use with a hash it’s similar, in this example the goal is to group students into two categories corresponding to their marks obtained.

    marks = {"Ramesh":23, "Vivek":40, "Harsh":88, "Mohammad":60}
    
    def group_by_marks(marks, pass_marks)
      marks.group_by do |_student, score|
        score > pass_marks ? 'Passed' : 'Failed'
      end
    end
    
    group_by_marks(marks, 30)
    => {"Failed"=>[["Ramesh", 23]], "Passed"=>[["Vivek", 40], ["Harsh", 88], ["Mohammad", 60]]}
    

Control Flow

Control Structures

  • if-elsif-else-end (execute code when conditionals are true)

      if boolean
        # code to execute when conditional is true
      elsif ther conditional>
        # code to execute when other conditional is true
      else
        # code to execute when all conditionals are false
      end
    
  • unless-else-end (complement of if)

      unless boolean
        # code to execute when conditional is false
      else
        # code to execute contional is true
      end
    

One-liners

  • if
      expression if boolean
    

    example

      puts "Hello world!" if 2 5
      Hello world!
      => nil
      puts "Hello world!" if 2 > 5
      => nil
    
  • unless
      expression unless boolean
    

    example

      puts "Hello world!" unless 2 5
      => nil
      puts "Hello world!" unless 2 > 5
      Hello world!
      => nil
    

Remember that the order is important as Ruby will expect: an expression followed by if followed by a boolean. It’s also important to notice that we don’t need an end when we write one-liners.

Ternary operator

# boolean ? expression_if_true : expression_if_false
puts 3 < 4 ? "3 is less than 4!" : "3 is not less than 4."

The case statement

case language
  when "JS"
    puts "Websites!"
  when "Python"
    puts "Science!"
  when "Ruby"
    puts "Web apps!"
  else
    puts "I don't know!"
end

Boolean operators

  • And &&

    A B result
    true true true
    true false false
    false true false
    false false false

    Example

      (2 5) && (-11 < 5)
      => true
      (2 5) && (-11 < -15)
      => false
      (2 > 5) && (-11 > -15)
      => false
      (2 > 5) && (-11 -15)
      => false
    
  • Or ||

    A B result
    true true true
    true false true
    false true true
    false false false

    Example

      (2 5) || (-11 < 5)
      => true
      (2 5) || (-11 < -15)
      => true
      (2 > 5) || (-11 > -15)
      => true
      (2 > 5) || (-11 -15)
      => false
    

Note than Ruby in evaluates the operators || and && via short-circuit evalutation.

That means that Ruby doesn’t look at both expressions unless it has to, if it sees

false && true

it stops reading as soon as it sees && because it knows flase && anything must be false, the same way

true || false

it stops reading as soon as it sees || because it knows true || anything must be false.

Note than in the case of || if the first argument is true the whole operation is evaluated to true without checking the value of the second argument

Note than in the case of && if the first argument is false the whole operation is evaluated to false without checking the value of the second argument

  • Not !

    A ~A
    true false
    false true
      !(2 5)
      => false
      !(2 > 5)
      => true
    

Code Looping

  • while-end (execute code while the conditional is true)

      while onditional>
          # some code
      end
    

    Example

      a = 0
      while a 10
          puts a
          a += 1
      end
    
  • until-end (complement to while)

      while onditional>
          # some code
      end
    

    Example

      a = 0
      until a >= 10
          puts a
          a += 1
      end
    
  • loop (the simplest but least powerful lopping method)

      loop { print "Hello, world!" }
    

    Be careful of infinite loops, loop requires a conditional breaker to terminate its execution

      a = 0
      loop do
        print a
        break if a >= 10
        a += 1
      end
    
  • for-end (when you know exactly how many times your code must run)
      for i in 0...5
        puts i
      end
      0
      1
      2
      3
      4
      => 0...5
    
  • .times iterator (super compact for loop)
      3.times { puts "Hello, world!" }
      Hello, world!
      Hello, world!
      Hello, world!
      => 3
    
  • .each iterator (execute some code on each item of a collection)
      (1..5).each { |i| puts i*2 }
      2
      4
      6
      8
      10
      => 1..5
    
      (1..5).each do |i|
        puts "#{i} + #{2*i} = #{3*i}"
      end
      1 + 2 = 3
      2 + 4 = 6
      3 + 6 = 9
      4 + 8 = 12
      5 + 10 = 15
      => 1..5
    

The right tool for the job

Although Ruby includes for loops, the Rubyist way is to not use them, there are better tools available to us.

  • If we want to do something a specific number of times, we can use the .times method:
    3.times { puts "Odelay!" }
    Odelay!
    Odelay!
    Odelay!
    => 3
    
  • If we know the range of numbers we would like to include, we can use .upto and .downto, instead of trying to use a for loop that stops when a counter variable hits a certain value.
    98.upto(100) { |num| puts num }
    98
    99
    100
    => 98
    
  • If we want to repeat an action for every element in a collection, we can use .each:
    [1, 2, 3].each { |n| puts n * 10 }
    10
    20
    30
    => [1, 2, 3]
    

Methods

In simpler terms, a method is a group of several expressions (block, so to say) that can be referred with a name, can be passed some arguments (input) and are associated with one or more objects.

A method is a helpful way in which we can abstract similar computations into logical chunks of code that otherwise would be very clumsy and difficult to manage.

Methods, in a way, also behave like a black box where the programmer should ideally be concerned only with a basic description of the box — what it does, what it’s input and what is the expected output —, without having to worry about how it does it.

This allows to focus more on the functionality of a program instead of the implementation details.

Methods are defined using the def keyword followed by the method name, optional parameter names (input) written between parentheses (), a block of code between the def and end lines, and a returning output (usually implied in the block).

Note that the argument(s) is the data passed to the method when is called, and the parameter(s) is the name given to the input(s) when the method was defined (i.e the method declaration).

def square(number)
  number ** 2
end

square(5)
=> 25

In the example anterior square is the name of the method, number is the parameter name, number ** 2 is the method’s block, and the 5 in square(5) is the argument passed to the method.

Default Parameters

Default parameters are defined when the method is declared, and are only used if the method has not received the correct number of arguments, in which case the value of the argument defaults to the one defined in the declaration, e.g.

def alphabetize(arr, rev=false)
  if rev
    arr.sort { |item1, item2| item2 > item1 }
  else
    arr.sort { |item1, item2| item1 > item2 }
  end
end

books = ["Heart of Darkness", "Code Complete", "The Lorax", "The Prophet", "Absalom, Absalom!"]

puts "A-Z: #{alphabetize(books)}"
puts "Z-A: #{alphabetize(books, true)}"

Splat arguments

Splat arguments are preceded by a *, which tells the program that the method can receive one or more arguments, it must always be the last parameter.

def what_up(greeting, *friends)
  friends.each { |friend| puts "#{greeting}, #{friend}!" }
end

what_up("Whats up", "Ian", "Zoe", "Zenas", "Eleanor")
Whats up, Ian!
Whats up, Zoe!
Whats up, Zenas!
Whats up, Eleanor!
=> ["Ian", "Zoe", "Zenas", "Eleanor"]
def sum(first, *rest)
  rest.reduce(first) { |memo, new_num| memo + new_num }
end

sum(1)
=> 1
sum(1, 2)
=> 3
sum(1, 2, 3)
=> 6

Keyword arguments a.k.a Named parameters

Although splat arguments may seem as a handy feature to have, except a few circumstances, you are never going to use that many variables for your method.

Also, if you are passing several different types of variables to the method which then will be assigned to a array, it can be difficult for the programmer invoking the method to remember the proper order for those arguments.

Ruby allows you to (partially) mitigate this problem by passing a Hash as an argument or one of the arguments, e.g.

def fetch_file(uri, options)
  if options.has_key?(:proxy)
    # do something
  end
end

or using the .fetch method:

def foo(options = {})
  bar = options.fetch(:bar, 'default')
  puts bar
end

foo
=> default
foo(bar: 'baz')
=> baz

The main problem with this technique, is that you cannot easily set default values for arguments. Fortunately Ruby 2.1 introduced first-class support for keyword arguments.

In computer programming, named parameters or keyword arguments refers to a computer language’s support for functions calls that clearly state the name of each parameter within the function call itself.

We can rewrite the method of the last example as

def foo(bar: 'default')
  puts bar
end

foo
=> default
foo(bar: 'baz')
=> baz

We can also define required keyword arguments, with a trailing colon:

def foo(bar:)
 puts bar
end

foo
=> ArgumentError: missing keyword: bar
foo(bar: 'baz')
=> baz

We can also mix ordinary arguments with keyword arguments

def foo(x, str: 'foo', num: 424245)
    [x, str, num]
end

foo(13)
=> [13, 'foo', 424245]
foo(13, str: 'bar')
=> [13, 'bar', 424245]

If you want to mix keyword arguments with an options’ hash, you will have to use the notation (**name):

def foo(str: "foo", num: 424242, **options)
  [str, num, options]
end

foo
=> ['foo', 424242, {}]
foo(check: true)
=> ['foo', 424242, {check: true}]

Because using:

def foo(str: "foo", num: 424242, options = {})

will raise an error like: syntax error, unexpected ')', expecting end-of-input.

Ruby blocks can also be defined using keyword arguments:

define_method(:foo) do |bar: 'default'|
  puts bar
end

foo
=> default
foo(bar: 'baz')
=> baz
define_method(:foo) { |str: 'foo', num: 424245| [str, num] }

foo
=> ['foo', 424245]

Keyword arguments vs options hash

With first-class keyword arguments in the language, we don’t have to write the boilerplate code to extract hash options. Unnecessary boilerplate code increases the opportunity for typos and bugs.

Note that the calling code is syntactically equal to calling a method with has arguments, which makes for an easy transition from options hashes to keyword arguments.

Keyword arguments vs positional arguments

Connascence and trade-offs

Assume we have a method with positional arguments:

def mysterious_total(subtotal, tax, discount)
  subtotal + tax -discount
end

mysterious_total(100, 10, 5)
=> 105

This method does it job, but as a reader of the code using the mysterious_total method, I have no idea what those arguments mean without looking up the implementation of the method.

By using keyword arguments, we know what the arguments mean without looking up the implementation of the called method:

def obvious_total(subtotal:, tax:, discount:)
  subtotal + tax - discount
end

obvious_total(subtotal: 100, tax: 10, discount: 5)
=> 105

keyword arguments allows us to switch the order of the arguments, without affecting the behavior of the method:

obvious_total(subtotal: 100, discount: 5, tax: 10)
=> 105

Blocks

Higher order functions are one of the key components of functional programming. A higher order function is a tool that takes other functions as parameters or returns them as a result.

Blocks are sets of instructions without a defined name (they are similar to anonymous functions in Javascript). Blocks can be defined with either the keywords do and end or curly braces {}.

Passing a block to a method

A method can take a block as a parameter, e.g

[1, 2, 3, 4, 5].each { |i| puts i }

Here the bits between {} is the block

Block argument are listed between vertical bars |, instead of parentheses.

Passing a block to a method is a great way of abstracting certain tasks from the method and defining those tasks when we call the method.

Yield

Methods that accept blocks have a way of transferring control from the calling method to the block and back again. We can build into the methods we define by using the yield keyword.

def yield_name(name)
  puts "In the method! Let's yield."
  yield("Kim")
  puts "In between the yields!"
  yield(name)
  puts "Block complete! Back in the method."
end

# pass a block to yield_name method
yield_name("Eric") { |n| puts "My name is #{n}." }
In the method! Let's yield.
My name is Kim.
In between the yields!
My name is Eric.
Block complete! Back in the method.
=> nil
def factorial(num)
  puts yield
end

n = gets.to_i
# pass a block to factorial method
factorial(n) do |num|
  (1..n).reduce(1) { |factorial, curr_num| factorial *= curr_num }
end

Procs

Blocks are not objects, this is one of the very few exceptions to the “everything is an object” rule in Ruby.

Because of this blocks can’t be saved to variables and don’t have all the powers and abilities of a real object, for that we use procs.

You can think of a proc as a saved block.

Procs are great for keeping you code DRY. With blocks, you have to write your code out each time you need it; with a proc, you write your code once and can use it many times.

There are several methods to crate a Proc, the two most common are:

  • Use the Proc class constructor
    multiples_of_3 = Proc.new { |n| n % 3 == 0 }
    
  • Use the Kernel#proc method as a shorthand of ::new (this is the recommended way)
    multiples_of_3 = proc { |n| n % 3 == 0 }
    

We can then pass the proc to a method that would otherwise take a block, and we don’t have to rewrite the block over and over!

print (1..100).to_a.select(&multiples_of_3)
print (1..10).to_a.collect(&multiples_of_3)
print (1..10).to_a.map(&multiples_of_3)

The & is used to convert the multiples_of_3 proc into a block, it’s use it necessary every time we pass a proc to method that expects a block.

We can also directly call a proc by using Ruby’s .call method

test = Proc.new { puts "Hello!" }
test.call
Hello!
=> nil
def square_of_sum(my_array, proc_square, proc_sum)
  sum = proc_sum.call(my_array)
  proc_square.call(sum)
end

proc_square_number = proc { |num| num * num }
proc_sum_array     = proc { |arr| arr.reduce(0) { |sum, n| sum + n } }
my_array = gets.split.map(&:to_i)

puts square_of_sum(my_array, proc_square_number, proc_sum_array)

Lambdas

Lambdas are anonymous functions. Lambdas in Ruby are objects of the class Proc. They are useful in most of the situations where you would use a proc.

strings = ["leonardo", "donatello", "raphael", "michaelangelo"]

symbolize = lambda { |string| string.to_sym }

symbols = strings.collect(&symbolize)

The simplest lambda takes no arguments and returns nothing:

# good practice: multi-line lambdas use 'lambda' and the do-end keywords
lambda do
end
# good practice: single line lambdas use '->' (lambda literal syntax) and curly braces `{}`
-> {}

with arguments

sum = -> (a,b) { a + b } # note that lambda literal syntax don't require args between `|`
sum.call(2, 3)
=> 5

sum = lambda { |a, b| a + b } # note the precence of `|` like a proc
sum.call(2, 3)
=> 5

Lambdas can be used as arguments to higher-order functions. They can also be used to construct the result of a higher-order function that needs to return a function.

  • Lambda that takes no arguments
    def area(long, base)
      -> { long * base }
    end
    
    x = 10.0
    y = 20.0
    
    area_rectangle = area(x, y).call
    area_triangle = 0.5 * area(x, y).() # prefer the use of `.call` instead of `.()`
    
    puts area_rectangle     # 200.0
    puts area_triangle      # 100.0
    
  • Lambda that takes one or more arguments
    area = -> (long, base) { long * base }
    
    x = 10.0
    y = 20.0
    
    area_rectangle = area.(x, y) # prefer the use of `.call` instead of `.()`
    area_triangle = 0.5 * area.call(x, y)
    
    puts area_rectangle     # 200.0
    puts area_triangle      # 100.0
    

Lambdas vs Procs

There are only two main differences between lambdas and procs:

  1. A lambda checks the number of arguments passed to it, while a proc does not.

    A lambda will throw an error if you pass it the wrong number of arguments, whereas a proc will ignore unexpected arguments and assign nil to any that are missing.

    p = proc {|x, y| "x=#{x}, y=#{y}" }
    p.call(1, 2)      #=> "x=1, y=2"
    p.call([1, 2])    #=> "x=1, y=2", array deconstructed
    p.call(1, 2, 8)   #=> "x=1, y=2", extra argument discarded
    p.call(1)         #=> "x=1, y=", nil substituted instead of error
    
    l = lambda {|x, y| "x=#{x}, y=#{y}" }
    l.call(1, 2)      #=> "x=1, y=2"
    l.call([1, 2])    # ArgumentError: wrong number of arguments (given 1, expected 2)
    l.call(1, 2, 8)   # ArgumentError: wrong number of arguments (given 3, expected 2)
    l.call(1)         # ArgumentError: wrong number of arguments (given 1, expected 2)
    
  2. When a lambda returns, it passes control back to the calling method: when a proc returns, it does so immediately, without going back to the calling method.

    def batman_ironman_proc
      victor = proc { return 'Batman will win!' }
      victor.call   # returns from method, but continues to execute the method
      'Iron Man will win!'
    end
    
    puts batman_ironman_proc
    Batman will win!
    
    def batman_ironman_lambda
      victor = -> { return 'Batman will win!' }
      victor.call   # just returns from lambda into method body
      'Iron Man will win!'
    end
    
    puts batman_ironman_lambda
    Iron Man will win!
    

Here the proc says Batman will win because it returns immediately, without going back to the batman_ironman_proc method.

Quick Review

  1. A block is just a bit of code between do..end or {}. It’s not an object on its own, but it can be passed to methods like .each or .select.
  2. A proc is a saved block we can use over and over.
  3. A lambda is just like a proc, only it cares about the number of arguments it gets and it returns to its calling method rather than returning immediately.

There are obviously lots of cases in which blocks, procs, and lambdas can do similar work, but the exact circumstances of your program will help you decide which one you want to use.

Closures

A closure is a method that:

  • Can be passed around like an object.

    It can be treated like a variable, which can be assigned to another variable, passed as an argument to a method.

  • Remembers the value of variables no longer in scope.

    It remembers the values of all the variables that were in scope when the function was defined. It is then able to access those variables when it is called even if they are in a different scope.

def outer_function
  some_state = { count: 0 }
  {
    increment: -> { some_state[:count] += 1 },
    get_count: -> { some_state[:count] }
  }
end

counter = outer_function

counter[:increment].call
counter[:increment].call
counter[:get_count].call #=> 2
counter[:increment].call
counter[:increment].call
counter[:get_count].call #=> 4

some_state
# undefined local variable or method `some_state' for main:Object (NameError)

Blocks, Procs and Lambdas are closures

def block_message_printer
  message = 'Welcome to Block Message Printer'
  yield if block_given?
  puts "But in this function/method message is :: #{message}"
end

def proc_message_printer(my_proc)
  message = 'Welcome to Proc Message Printer'
  my_proc.call
  puts "But in this function/method message is :: #{message}"
end

def lambda_message_printer(my_lambda)
  message = 'Welcome to Lambda Message Printer'
  my_lambda.call
  puts "But in this function/method message is :: #{message}"
end

message = gets

block_message_printer { puts "This message remembers message :: #{message}" }

my_proc = proc { puts "This message remembers message :: #{message}" }
proc_message_printer(my_proc)

my_lambda = -> { puts "This message remembers message :: #{message}" }
lambda_message_printer(my_lambda)

Partial application

Partial application is a technique of fixing a number of arguments to a function, producing another function of smaller arguments, i.e a function that takes a function with multiple parameters and returns a function with fewer parameters.

Partial applications fixes (partially applies the function to) one or more arguments inside the returner function, and the returned function takes the remaining parameters as arguments in order to complete the function application.

In Ruby to use Partial Application, we create a lambda that takes a parameter and returns a lambda that does something with it.

def factorial(num)
  (1..num).inject { | result, num| result * num }
end

combination = lambda do |num|
  -> (new_num) { factorial(num) / (factorial(new_num) * factorial(num - new_num)) }
end

n = gets.to_i
r = gets.to_i
nCr = combination.(n)
puts nCr.(r)

Currying

Is a technique for converting function calls with N arguments into chains of N functions calls with a single argument for each function call.

Currying always returns another function with only one argument until all of the arguments have been applied. So, we just keep calling the returned function until we’ve exhausted all the arguments and the final value gets returned.

multiply_numbers = -> (x, y, z) do
    x * y * z
end

doubler = multiply_numbers.curry.call(2)
tripler = multiply_numbers.curry.call(3)

puts doubler.(4)    #8
puts tripler.(4)    #12

check the curry method for details

Lazy Evaluation

Lazy evaluation is an evaluation strategy that delays the assessment of an expression until its value is needed.

Ruby 2.0 introduced a lazy enumeration feature. Lazy evaluation increases performance by avoiding needless calculations, and it has the ability to create potentially infinite data structures.

power_array = -> (power, array_size) do
    1.upto(Float::INFINITY).lazy.map { |x| x**power }.first(array_size)
end

puts power_array.(2 , 4)    #[1, 4, 9, 16]
puts power_array.(2 , 10)   #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
puts power_array.(3, 5)     #[1, 8, 27, 64, 125]

In this example, lazy avoids needless calculations to compute power_array. If we remove lazy from the above code, then out code would try to compute all x ranging from 1 to Float::INFINITY.

To avoid timeouts and memory allocation exceptions, we use lazy. Now, our code will only compute up to first(array_size).

Sorting

Sorting algorithms can be a great introduction to computer science as a theoretical discipline, but Ruby already has a built-in sorting library, which implements a few of these algorithms.

Our job is to decide how to compare two items in the list (maybe passing a block to a method), and let Ruby decide which strategy to use.

Combined comparison operator > \ - 0 when both operands are equal. \ - 1 when first operand is greater \ - -1 when second operand is greater

A block that is passed into the sort method must return either 1, 0, or -1. Is should return -1 if the first block parameter should come before the second, 1 if vice versa, and 0 if they are of equal weight, meaning one does not come before the other.

Object Oriented Programming

Ruby is an object-oriented programming language, which means it manipulates programming constructs called objects. (Almost) everything in Ruby is an object!

  1. Class definition

    class keyword and the name of the class (use CamelCase).

     class ClassName
     end
    
  2. Object initialization

     def initialize(args)
     end
    

Remember that an object is an instance of a class.

Scope

Another important aspect of Ruby classes is scope. The scope of a variable is the context in which it’s visible to the program.

Not all variables are accessible to all parts of a Ruby program at all times.

When dealing with classes, you can have variables:

  1. Instance variables that are only available to particular instances of a class.
  2. Class variables that are members of a certain class, and
  3. Global variables that are available everywhere,
  4. Local variables that are only available inside certain methods,

The same goes for methods:

  1. Global methods are available everywhere,
  2. Class methods are only available to members of a certain class, and
  3. Instance methods some are only available to particular instance objects.

Variable types

  1. Instance variable @

    This variable is attached to the instance of the class, e.g.

    This isn’t just a Ruby convention—it’s part of the syntax! Always start your instance variables with @.

     class Person
       def initialize(name)
         @name = name
       end
     end
    

    This tells Ruby that whenever it creates a Person, it has to have a name, and each instance of Person will have its own @name.

  2. Class variables

    Class variables are like instance variables, but instead of belonging to an instance of a class, they belong to the class itself. Class variables always start with two @s, like so:

     class MyClass
       @@class_variable
     end
    

    Because there’s only one copy of a class variable shared by all instances of a class, we can use them to pull off some cool Ruby tricks. For example, we can use a class variable to keep track of the number of instances of that class we’ve created.

     class Person
       @@people_count = 0
    
       def initialize(name)
         @name = name
         @@people_count += 1
       end
    
       def self.number_of_instances
         return @@people_count
       end
     end
    
     matz = Person.new("Yukihiro")
     dhh = Person.new("David")
    
     puts "Number of Person instances: #{Person.number_of_instances}"
    
  3. Global variables

    Can be divided in two ways.

    • You just define the variable outside of any method or class
    • If you want to make a variable global from inside a method or class, just start it with a $, like so: $matz
     class MyClass
     $my_variable = "Hello!"
     end
    
     puts $my_variable
     # Remember to access the variable with `$`
    

    A caveat, though: global variables can be changed from anywhere in your program, and they are generally not a very good idea. It’s much better to create variables with limited scope that can only be changed from a few places!

Method types

  1. Global methods defined outside a class
  2. Instance methods defined inside a class, are called from within an object, e.g

    "hello".length
    

    .length is an instance method belonging to the object "hello"

  3. Global methods

Public vs Private

If you’re the only one writing and using your software, it’s okay for every part of your Ruby program to know what every other part is doing. But when other people are working on or using your programs, they may attempt to muck around with the way different parts of your program do their jobs. You don’t want that!.

For this reason, Ruby allows you to explicitly make some method public and others private.

  • Public methods allows for an interface with the rest of the program; they say, “Hey, Ask me if you need to know something about my class or its instances”.

    Methods are public by default in Ruby. If you don’t specify public or private your methods will be public.

  • Private methods, on the other hand, are for your classes to do their own work undisturbed. They don’t want anyone asking them anything, so they make themselves unreachable.

    Private methods are private to the object where they are defined, you can only call these methods from other code inside the object.

    In order to access private information, we have to create public methods that know how to get it. This separates the private implementation from the public interface.

attr_reader, attr_writer

It’s a good practice in any Object Oriented Programming Language to use methods to access attributes. For instance, if we want to access a @name instance variable, we have to write something like:

def name
  @name
end

def name=(value)
  @name = value
end

Fortunately Ruby’s motto is to make the programmer happier, and it comes with the attr_reader and attr_writer which are public instance methods of the class module. If we write

attr_reader :name
attr_writer :name

inside a class, Ruby does something like this for us automatically:

def name
  @name
end

def name=(value)
  @name = value
end

If we want to both read and write a particular variable, there’s an even shorter shortcut, we can use attr_accessor to make a variable readable and writable in one fell swoop.

Inheritance

Inheritance is the process by which one class takes on the attributes and methods of another, and it’s used to express an is-a relationship.

In Ruby, the inheritance works like this (you can read < as “inherits from”):

class Application
  def initialize(name)
    @name = name
  end
end

class MyApp < Application
end

Override

Sometimes you’ll want one class that inherits from another to not only take on the methods and attributes of its parent, but to override one or more of them.

You can explicitly create a method inside your derived class with the same name of the parent class, this new version of the method will override the inherited version for any object that is an instance of the derived class.

class Creature
  def initialize(name)
    @name = name
  end

  def fight
    return "Punch to the chops!"
  end
end

class Dragon < Creature
  def fight
    puts "Breathes fire!!"
  end
end

Super

Sometimes you’ll be working with a derived class (or subclass) and realize that you’ve overwritten a method or attribute defined in that class’ base class (also called a parent or superclass) that you actually need. Have no fear! You can directly access the attributes or methods of a superclass with Ruby’s built-in super keyword.

class Creature
  def initialize(name)
    @name = name
  end

  def fight
    return "Punch to the chops!"
  end
end

# Add your code below!
class Dragon < Creature
  def fight
    puts "Instead of breathing fire..."
    super
  end
end

When you call super from inside a method, that tells Ruby to look in the superclass of the current class and find a method with the same name as the one from which super is called. If it finds it, Ruby will use the superclass’ version of the method.

Modules

Modules are like a toolbox that contains a set of methods and constants.

There are lots of Ruby tools you might want to use, but it would clutter the interpreter to keep them around all the time. For that reason, we keep a bunch of them in modules and only pull in those module toolboxes when we need the constants and methods inside.

Modules are easy to make! You just use the module keyword.

module Circle

  PI = 3.141592653589793

  def Circle.area(radius)
    PI * radius**2
  end

  def Circle.circumference(radius)
    2 * PI * radius
  end
end

Like class names, module names are written in CapitalizedCamelCase rather than lowercase_with_underscores.

It doesn’t make sense to include variables in modules, since variables (by definition) change. Constants, however, are supposed to always remain the same, so including helpful constants in modules is a great idea.

Ruby doesn’t make you keep the same value for a constant once it’s initialized, but it will warn you if you try to change it.

Ruby constants are written in ALL_CAPS and are separated with underscores if there’s more than one word, an example is the PI constant that was defined in the last example.

Score resolution operator

One of the main purposes of modules is to separate methods and constants into named spaces. This is called namespacing, and it’s how Ruby doesn’t confuse Math:PI and Circle::PI.

The :: is called the scope resolution operator, which is the way we tell Ruby where to look for a specific bit of code.

Requiring a Module

Some modules, like Math, are already present in the interpreter. Others need to be explicitly brought in, and we can do this by using the require keyword, e.g if we want to get today’s date we can use the Date module.

require 'date'

puts Date.today

Including a Module

Any class that includes a certain module can use those module’s methods and constants without having to prepend the module’s name.

class Angle
  include Math
  attr_accessor :radians

  def initialize(radians)
    @radians = radians
  end

  def cosine
    cos(@radians)
  end
end

acute = Angle.new(1)
acute.cosine

Mixins

Any given Ruby class can have only one superclass. Some languages allow a class to have more than one parent, which is a model called multiple inheritance. This can get really ugly really fast, which is why Ruby disallows it.

However, there are instances where you want to incorporate data or behavior from several classes into a single class, and Ruby allows this through the use of mixins.

include a module

When a module is used to mix additional behavior and information into a class, it’s called a mixin. Mixins allow us to customize a class without having to rewrite code!

module Action
  def jump
    @distance = rand(4) + 2
    puts "I jumped forward #{@distance} feet!"
  end
end

class Rabbit
  include Action
  attr_reader :name
  def initialize(name)
    @name = name
  end
end

class Cricket
  include Action
  attr_reader :name
  def initialize(name)
    @name = name
  end
end

peter = Rabbit.new("Peter")
jiminy = Cricket.new("Jiminy")

peter.jump
jiminy.jump

This is how we can mimic multiple inheritance in Ruby: by mixing in traits from various modules as needed, we can add any combination of behaviors to our classes as we like.

extend a class

Whereas include mixes a module’s methods in at the instance level (allowing instances of a particular class to use the methods), the extend keyword mixes a module’s methods at the class level. This means that the class itself can use the methods, as opposed to instances of the class.

module ThePresent
  def now
    puts "It's #{Time.new.hour > 12 ? Time.new.hour - 12 : Time.new.hour}:#{Time.new.min} #{Time.new.hour > 12 ? 'PM' : 'AM'} (GMT)."
  end
end

class TheHereAnd
  extend ThePresent
end

TheHereAnd.now

Here the TheHereAnd class was extended with ThePresent module, allowing it to use the now method.