Flow Control#

if-then-else#

if-then-else statement in Ruby is quite similar to the other programming languages and to the Shell Script. The syntax is following:

if contitional_expression [then]
  expressions ...
elsif contitional_expression [then]
  expressions ...
else
  expressions ...
end

Just to remind - in Ruby, conditional expression is true unless it evaluates to false or nil. All except false and nil is true. Number zero, empty array, zero-length string - all of this object means true in Ruby conditionals.

The keyword elsif means else if - the next conditional expression which is checked when all the expressions before evaluates to false. The code after else is running if all the coditionals evaluate to false or nil.

The word then is optional and it have to be use only if you want to put the code in the same line. It is not very common, because Ruby have the better way to describe such expressions in one line - see conditional modifier below.

if []
  puts 'Empty array is not nil or false!'
end

if 3>2 then
  puts 'it is indeed'
end

if 3>2 then puts 'it is indeed' end   # using 'then' to create one line expression

Conditional Modifier#

Conditional modifier is a very interesting and popular way to write conditional expression in the one line. The syntax is reversed - first goes the code, then the condition: statement if conditional_expression. The statement will be executed only when conditional_expression evaluates to true. This is simpler and better readable for humans.

array.push 'foo' if not array.include? 'foo'  # push string 'foo' if array does not include it
#=> ["foo"]

array.push 'foo' if not array.include? 'foo'  # trying again
#=> nil
array                                         # only one object in the array
#=> ["foo"]

Unless#

For better human readability Ruby provides the keyword unless. It is the oposite for if and means simply if not. It can be used both in unless-then-else expressions (but there is no equivalent to elsif) and conditional modifiers. Especially for this one it provides pretty code, which everyone can easy read, even without the knowledge of the language.

array.push 'foo' unless array.include? 'foo'      # push string 'foo' unless array includes it

Conditional Expressions Returning Values#

In Ruby, everything returns value. That means that if-then-else does it as well - returns the value of last processed statement.
Also, we have a c-style conditional operator: condition ? true_expression : false_expression, evaluates and returns true_expression only if condition evaluates to true or false_expression if it evaluates to false or nil.

if a > b      # procedural way to find out the maximum value of two variables
  max = a
else
  max = b
end

max = if a > b   # functional-style way to do the same
  a
else
  b
end

# one-liner in this case doesn't look so bad:
max = if a > b then a else b end

# c-style conditional expression:
max = a > b ? a : b

Case Statement#

Case statement in Ruby is similar to case/esac in Unix Shell. It evaluates the expression and, depends on the value, runs the specific code parts. The syntax:

case expression
when value then statement  # for one-liners use 'then'
when value, value          # if any of this values fits
  statements
else                       # in case no values fits the expression
  statements
end

Identical to if-then-else, case expression returns value . The example below shows both ways to use case statement in Ruby:

case netmask            # netmask is a string with network mask
when '255.0.0.0'
  address_class = 'A'   # this is Class A subnet
when '255.255.0.0'
  address_class = 'B'
when '255.255.255.0'
  address_class = 'C'
else
  puts "[ERROR] unknown Netmask #netmask"
  address_class = nil   # nil in this case means 'unknown class'
end

address_class = case netmask
when '255.0.0.0'     then 'A'
when '255.255.0.0'   then 'B'
when '255.255.255.0' then 'C'
else
  puts "[ERROR] unknown Netmask #netmask"
  nil    # this is unnecessary, because puts returns 'nil'; done for better readability
end

Case Equality Operator in Case Statement#

When discussing ranges and type system we mentioned about case equality (triple equal sign) operator. In most cases, this operator is identical to normal equality (==) - so, for example, 1 === 1.0 is true. But in some cases it is a kind of extention. For example in Range you can check if the object is within the specific range: (1..100) === 3.14, or you can check if the object is kind of the class: Fixnum === 42. In case statement, the expression after keyword ‘case’ is compared with the values (coming after ‘when’) using this operator (so that is why it is called the ‘case equality operator’). In the example below we assume that http_return_code variable contains some return code from http request (like 404 - not found):

case http_return_code
when 100..199
  puts "Informational"
when 200..299
  puts "Successfull"
when 300..399
  puts "Redirection"
when 404                      # if return code is 404, it will match here
  puts "*** Page not found"   # and finish the case statement
when 400..499                 # so in this case
  puts "Client Error"         # this code will not be evaluated
when 500..599
  puts "Server Error"
else
  puts "*** Unknown"
end

Case statement is often used to determine the object type in functions which can have object of many types as the argument. The good example is the [] method for selecting substrings from string: it can take an integer (s[3]) and then return the single character, the range (s[1..3]) and return the substring, or you can use Regular Expression or even String as an argument. The other example is to pass an object or array of objects as the argument, and when the argument is array, process with all elements.

case argument
when NilClass
  puts "Server can't be nil!"
when WindowsServer
  puts "Can't ssh to Windows, delete everything manually"
when UnixServer, MacOSXServer
  argument.ssh "root", "root123", "rm -rf /"  # this classes should have defined 'ssh' method
when Array
  # below is the ruby way to do the loop, see next chapter
  for machine in argument
    machine.ssh "root", "root123", "rm -rf /"
  end
end