Work In Progress

Work on this post is underway... It will be done "soon..."


The basics

Variables

Variables are named in snake_case. Variables are not typed.

puts is your goto method to print to the console.

my_first_variable = "Hello world!"
puts my_first_variable

Note that there is a print as well, it is similar to puts except that it doesn’t add a new line to the output.

Comments

Single line comments starts with #. Multi-line comments are surrounded by =begin ... =end

# single line comment

=begin
Line 1
Line 2
=end

Naming Convention

  • Variable names snake_case
  • Class names PascalCase
  • Constants UPPER_SNAKE_CASE or PascalCase (be consistent and check your team conventions)
  • Global variables starts with $
  • Instance variables start with @
  • Class variables start with @@
  • Mutating method names ends with !. It is common to have two method method and method! the first returns a changed copy and the latter mutates its receiver.
  • Methods that return boolean ends with ?

Parentheses

Optional, generally if you have a method with multiple arguments it is a good idea to keep them for readability purposes. Omit them for Control statements conditions. For commonly used methods are left by convention. puts is a good example, but you’ll need to pick that up from the community. In general, leave them out except for method arguments.

Multi-Statement Lines

You can have multiple statements in one line separated by ;. Use that only in simple cases where a class definition is three lines or for an abstract method.

Code Blocks

You can use either {}, begin ... end or do ... end to surround code blocks. The convention is to use {} if the block consists of a single statement (one liner), and begin ... end or do ... end otherwise.

begin ... end vs. do ... end

begin “starts an exception handling block” according to the language documentation. You can also use it to group multiple expressions and use it with guard clauses, but in that case it might be better to extract that into a method.
Another difference is that you can not pass begin ... end block to a method. In most cases, you will be using do ... end, you can check more details on begin ... end in the exception handling section Note that a method body, is in effect, a begin ... end block with begin being omitted.

Readability… Use Your Judgement

The purpose of the coding convention is readability, so if following one of the above breaks readability, use your judgement… sparingly! Remember consistency is important.

Expressions not statements

Almost everything in Ruby is an expression. For example an if statement is an expression that has the value returned by the last expression in that if

some_value = if true then 4 else 6 end
puts some_value # This puts `4`

Conditionals and Control Structures

  • Only false and nil are treated as false

if, else, elseif and unless

There is the if statement similar to any other language you came from

something_evil_lurking_in_the_dark = false

if something_evil_lurking_in_the_dark
  puts "Run away!"
end

Now, let’s negate that

if not something_evil_lurking_in_the_dark
  puts "We are safe!"
end

Ruby has an alternative to if not which is unless

unless something_evil_lurking_in_the_dark
  puts "We are safe!"
end

We can also use the good old else and elseif. While we can use those with unless, it doesn’t make much sense because you can simply reverse your if statement

if something_evil_lurking_in_the_dark
  puts "Run away..."
else
  puts  "We are safe!"
end

This last example can be written as a one liner, but we will need a new keyword then

if something_evil_lurking_in_the_dark then puts "Run away..." else  puts "We are safe!" end

Ternary operator

THe last example above can be turned to one liner

something_evil_lurking_in_the_dark ? (puts "Run away..." ): (puts "We are safe!")

Modifier form

Ruby has a modifier form of its conditionals. What that does is reversing the order of the statement to make it more “readable” or rather look like the common English language

puts "We are safe!" unless something_evil_lurking_in_the_dark

Even further, we can use the same form with assignment

message = "We are safe"
message = "Run away!!" if something_evil_lurking_in_the_dark

The value of message will change only if the value of something_evil_lurking_in_the_dark is true.

Case

case the_thing_lurking_in_the_dark
when 'monster'
    puts 'Run away'
when 'cat'
    puts 'sigh of relief'
else # default case
    puts 'Do not know what that is'

this is the same as

response = case the_thing_lurking_in_the_dark
    when 'monster'
        'Run away'
    when 'cat'
        'sigh of relief'
    else
        'Do not know what that is?'
puts response       

Which is also the same as

response = case title
    when 'monster' then 'Run away'
    when 'cat' then 'sigh of relief'
    else 'Do not know what that is?'
puts response        

We can use case to match on types

case some_data
when Monster
    puts "this is a string"
when Cat
    puts "this is a cat"
else
    puts "I don't know what is this!"

This will result in a nil if no when clause is matched and there is no else

Looping mechanisms

while and until

  • We should iterate with each instead of while and until.
  • until acts like while not. Both, similarly, can be used in the modifier form.
    begin
    # something to be done
    end while some_condition
    
  • There is also loop construct that doesn’t take a condition, we can break out of it though
    loop do
    # something to be done
    break if some_condition 
    end
    

break, redo and next

  • break break out of the loop entirely
  • redo jump to the beginning of the current iteration
  • next jump to the end of the current iteration

Iterating with each

When iterating over loops we can use each instead of for loop to go through the elements of a collection. Actually, Ruby under the hood would turn for statement into an each statement.

array.each do |element|
  puts element
end

If we want to look at the index as well, we can use each_with_index. each_index is another method that would let us iterate over indices.

array.each_with_index do |element, i|
  puts "#{i}: #{element}"
end
array.each_index do |i|
  puts i
end

Collections

Range of numbers

  • 0..n includes n, this is of type Range
  • 0.upto(n) includes n, this is of type Enumerator

Strings

  • String interpolation "Variable: #{variable}

Useful methods

  • split(delimiter) splits a string on delimiter
  • chomp removes trailing characters

Exception handling

raise is used to throw an exception

raise SomeError.new(), "some message"
  • beginend block is used to wrap the exception handling boundaries. rescue is used to catch exceptions within the block.
  • Method body is treated as a beginend block with begin being omitted.
  • rescue will catch anything that inherits StandardError.
  • We can also use rescue without specifying the error as a catch all
  • IF we want to something to run only if no exceptions occurred, we use else clause
  • If we want something to run at the end of the block every time, we use the keyword ensure
  • Note that rescue could be used without => err if we don’t care about the exception
begin
  # our code is here
rescue ArgumentError
  # Exception handling Code
rescue ZeroDivisionError
  # Exception handling here
rescue => err
  # Handling whatever other error here
else
  # This will be executed if no exceptions were raised
ensure
  # This code will run at the end of the `begin` - `end` block everytime 
end
  • We can rerun the beginend block using the keyword retry
    begin
    # our code is here
    rescue
      # Do some recovery
      retry # Re-execute the begin-end block 
    end
    
  • There is a modifier form for catching exceptions exception_raising_method() rescue puts("An error has occurred")

Classes, Objects and Modules

Objects

  • Everything in Ruby is an object
  • We can create a new object by calling new method on its class. ClassName.new()
  • objects can be made immutable by calling freeze methond on that object

Classes

Constructor

  • The class constructor is a method named initialize
class ClassName
  def initialize(arg1, arg2) # Constructor
    # Do your initialization stuff here
  end
end

Class (static) Methods

There are two ways to define class methods inside a class

Prefixing one method with self
def self.class_method
    # Do some class stuff
  end
Wrapping multiple methods in self
class << self
  def class_method
    # Do some class stuff
  end
end

Class Variables

  • Class variables and in other languages called static variables, are variables associated with the class itself
  • Class variables start with @@
  • Class variables are private
  • You can only interact with class variables via class or instance methods
class ClassName
  @@class_variable = "class variable"

  def self.class_variable
    @@class_variable
  end

  def class_variable
    @@class_variable
  end

  def class_variable=(value)
    @@class_variable = value
  end
end

puts ClassName.class_variable # class variable

instance = ClassName.new
puts instance.class_variable # class variable
instance.class_variable = "new class variable"
puts instance.class_variable # new class variable

Instance Variables

  • We use instance as an object of a given class
  • Instance variables start with @
  • Instance variables are private to the instance
  • To interact with an instance variables from outside of an instance, you need to do that through a method. Ruby have some methods that creates accessors (getters) and writers (setters) for you. Namely attr_reader, attr_writer and attr_accessor. The latter creates both a getter and a setter. For a method foo the getter will be named foo and the setter will be named foo=, yes the latter would be like putting an assignment = after the variable name.
  • You do not need to initialize the instance variable in the constructor, it will be initialized anywhere in the class where it is first used
class ClassName
  attr_reader   :instance_variable_with_reader_only             # Creates a reader (getter)
  attr_writer   :instance_variable_with_writer_only             # Creates a writer (setter)
  attr_accessor :instance_variable_with_both_reader_and_writer  # creates both a reader and a writer

  def initialize # Constructor
    @instance_variable_with_reader_only = "instance variable with reader"
    @instance_variable_with_writer_only = "instance variable with writer"
    @instance_variable_with_both_reader_and_writer = "instance variable with reader and writer"
  end
end
  • You can also define (or override) variable accessors manually by defining a method that has the same name as the variable name for readers and a method that have same name postfixed by = for writers ```ruby def instance_variable @instance_variable end

def instance_variable=(value) @instance_variable = value end ```

Scripting

Executing commands with back-tick

When sending a string to stdout, if that string is surrounded with backticks or %x the string will be run and the result will be printed in the output. For example puts ls Will result in print the list of files and directories in the current directory. This is equivelant to puts %x"ls".

exit vs. abort

  • exit(code) calls any registered at_exit handlers, exits and returns code to the OS
  • exit!(code) same as above, but doesn’t call at_exit handlers
  • abort(message) prints message to STDERR and exist with code 1 (error)

Caveats

  • defined? never returns true it either a string that describe the thing passed in or nil

Clever idioms

  • x ||= y will only set x to y if x is not defined. This is equivalent to x = x || y

Other Keywords

  • BEGIN: Takes a block that runs before any other code in the current file.
  • END : Takes a block that runs after any other code in the current file.
  • and : Short-circuit Boolean and with lower precedence than &&
  • or : Boolean or with lower precedence than ||
  • not : Inverts the following boolean expression. Has a lower precedence than !

Resources