Learning Ruby 2 – Getting your feet wet

Review and introduction

By now you’ve hopefully read through my original post on what ruby is, what it does, and why you’d want to install it. If you’re reading this you’ve probably already installed it. If not, you should probably do that if you’re going to take on this thing for serious.

Command-line tools

By now you’ve seen me use ruby to run ruby files. It turns out if you’ve installed ruby you will also have your own little interactive ruby shell, named irb. If you type irb into your terminal, you should be able to enter ruby code and have it execute right in front of your eyes. For example, here’s a little output from mine:

irb(main):001:0> a = 3
=> 3
irb(main):002:0> b = 2
=> 2
irb(main):003:0> puts 3+2
5
=> nil
irb(main):004:0> exit

The ruby basics

In order to examine some basic commands, I’m going to make a little program here. If you want, copy and paste this code into your own file and run it. Then we’ll talk about some of the things we see in here.

# This program will convert fahrenheit to celcius, or vice-versa

puts "Current temperature:"
current_temperature = gets.strip.to_f

puts "Is this in celsius or Fahrenheit? [C/F]"
original_temperature = gets.strip[0].downcase

if original_temperature == 'c'
    new_temperature = (current_temperature * 5 / 9) + 32
    puts "New temperature: #{new_temperature} F"
elsif original_temperature == 'f'
    new_temperature = (current_temperature - 32) * 9 / 5
    puts "New temperature: #{new_temperature} C"
else  # Can't process anything else
    puts "Don't recognise temperature scale: #{original_temperature}"
end

Just looking at this, you might be able to pick out how it goes about everything already. However, I’m going to go through all of this nice and slow, to make sure we’re all on the same page.

Comments

Here’s a nice quick one – on any line, the use of a hash symbol (#) will comment out the rest of the line – this means that the interpreter will ignore the rest of the line. This lets us put comments in our code, which lets us tell other people what we’re doing. Remember that you might be looking at this code six months from now – will you remember what that horrid line of code meant?

Assignment

The first thing we should look at is assignment:

an_integer = 3
a_string = 'foo'
a_float = 2.4

In all cases for assignment, you have a variable on the left, and a value on the right. You’re giving your variable a value so that when you refer to it later on, it will spit that value right back out.

There are several different types of value that you can store in a variable. These are called classes. You can find out what class a variable is in the following way:

an_integer.class    # => Fixnum
a_string.class      # => String
2.4.class               # => Float

These aren’t the only possible classes, but we’ll talk about that more later.

Variable names have to start with a lowercase letter (a-z), and may only contain letters, numbers and underscores in them. Most ruby programmers keep their variable names to lowercase letters and underscores. This is sometimes referred to as “snake-case“. It’s generally a good idea to keep your variable names pretty explanatory – if you start naming your vairables ‘i’ and ‘x’ you’ll soon forget what they all mean.

Methods

Everything in ruby knows how to do some things, but not everything knows how to do everything. We tell them how to do things by sending them messages, and trying to get them to run certain methods, which are basically sets of instructions. For example, a string may know how to make itself upper-case (i.e. it has a method for making itself upper-case), but won’t know anything about absolute values (i.e. won’t have a method for this).

a_string.upcase # => 'FOO'
-3.abs                  # => 3
a_string.abs        # => NoMethodError: undefined method `abs' for "a_string":String

Of course, some things will respond differently to different messages – i.e., not every method is the same. In general, all objects of a certain class will respond in the same way to the same message, but you can’t say the same for objects of other classes:

3 * 3           # => 9
2 * 6           # => 12
"foo" * 3 # => "foofoofoo"

3 + 3                   # => 6
2 + 6                   # => 8
"Foo" + "bar" # => "Foobar"

There’s two important things to remember about methods: first, you generally call them with the syntax object.method, and second, they will always give you back (or return) a value. For example, the method abs of the Fixnum class will return the absolute value of the number.

There are a couple of exceptions to the first rule – methods that are made up of symbols – +, -, *, /, <, >, <<, [], etc. – don’t need that fullstop between the object and the method. This is because we’re almost always referring to variables, and as we stated above, variables can only use letters, numbers and underscores in their names. In fact, we can put whitespace (spaces, tabs, whatever) in between the object and the method, since ruby will interpret it correctly. This means that even phrases such as 3 + 3 are method calls – we’re calling the + method of the 3 object.

One last thing – sometimes when we call methods, we want to give them an argument. An argument is another value that the method will play around with. For example, we can’t just call 3 + – we have to hand it another number to add to it. The standard syntax is:

object.method(argument1, argument2, argument3...)

However, we can always ditch the parentheses if we don’t want them in place. Sometimes, of course, things get confusing if you don’t keep the parentheses in place, but a lot of the time you can just get rid of them.

So what does this mean? These two lines are exatly equivalent:

3.+(3)
3 + 3

The first line is a lot more explicit than the second – we can see we’re calling the + method on the object 3, and passing an object 3. The second line, however, looks nicer.

Incidentally, if you ever want to see what methods an object knows, call the methods method and have a look:

3.methods # => [:to_s, :-@, :+, :-, :*, :/, :div, :%, :modulo...

This is basically a list of methods – ignore the colons in front of everything: we’ll confront them later on.

Input and output

Input and output are done through two different methods – puts and gets. These methods output and input strings to and from the terminal window.

These methods are acutally methods of Kernel, which is sort of the space you’re working in in ruby. Because you’re inside Kernel and so are they, you don’t need to put an object and dot in front of them – ruby just knows they’re methods.

puts accepts any number of arguments, and will output them all. It always returns nil, which is what ruby calls a complete absense of anything; this is as close as you’ll get to a method not returning anything. Note that in our example above we use the optional, “no-parentheses” approach – losing the parentheses doesn’t make the line any more confusing, and it looks nicer (to my mind at least).

gets doesn’t take any arguments, but will return a string. In the code example above, I actually chained a number of methods together – each method runs, returns a string, and passes it onto the next method:

original_temperature = gets.strip[0].downcase

The methods I call are, in order:

  • gets – returns a string from the terminal
  • strip – removes any whitespace (spaces and tabs) from the start or end of the string
  • .[] – returns the character at this position in the string. Position 0 is the first character in the string – so "Celsius"[0] will give you “C”
  • downcase – makes the string all lowercase

So if we type “Celsius” into the input, we’ll get “c” coming out the end. Isn’t that nice?

Flow control

So far we can input, output and call methods, but our program is simply going down the lines, executing them one after another. There’s a few things we can do to mix it up a bit – in this project, we’ve used an if statement.

Ruby has two boolean values, called true and false. An if statement simply checks to see if a logical test returns true – if it does, it executes a block of code that we pass to it:

if condition
    # This is our conditional block
end

We finish of that block with an end, which is our way of saying to ruby “this is the end of that if statement.” The indenting I’m doing there is purely for looks, but believe me – after you nest a few of these (an if statement inside an if statement inside an if statement), you’ll want to have everything nicely lined up.

So what stuff can we use in conditions? First up, we can use variables or values – anything that is not nil or false will be considered true. We can also use a wide number of operators that you’ll recognise from year 9 maths – <, >, <=, >=, == and !=. < and > are your standard less-than and greater-than methods; <= and >= are less-than-or-equal-to and greater-than-or-equal-to; and == and != are equal and not-equal operators. Note that there’s a big difference between == (comparison) and = (assignment).

Let’s look at some examples:

if 4 > 2
    puts "Math is working right"
end

if 2 > 4
    puts "Math isn't working right"
end

if a == 3
    puts "Looks like a is 3"
end

There’s a few other tricks we can pull with the if statement. First, we can add a couple of other tests that will only work if the first one fails, using the elsif statement. Each of these has another logical test and another code block, which only get tested if all ifs and elsifs before it fail. The other thing we can use is an else statement, which will only run if the if and all the elsifs fail.

if a.class == Fixnum
    puts "a is a Fixnum"
elsif a.class == Float
    puts "a is a Float"
else
    puts "a is something else"
end

Note you only need the one end per if statement.

Extra for experts: Remember how I said every method must return something? It turns out that every if statement also returns something – the last thing returned from the block you end up running. What does this mean for us? We can rewrite that statement from above a bit nicer:

puts "a is " + if a.class = Fixnum
    "a Fixnum"
elsif a.class == Float
    "a Float"
else
    "something else"
end

Re-visiting our program

# This program will convert fahrenheit to celcius, or vice-versa

puts "Current temperature:"
current_temperature = gets.strip.to_f

puts "Is this in celsius or Fahrenheit? [C/F]"
original_temperature = gets.strip[0].downcase

if original_temperature == 'c'
    new_temperature = (current_temperature * 5 / 9) + 32
    puts "New temperature: #{new_temperature} F"
elsif original_temperature == 'f'
    new_temperature = (current_temperature - 32) * 9 / 5
    puts "New temperature: #{new_temperature} C"
else  # Can't process anything else
    puts "Don't recognise temperature scale: #{original_temperature}"
end

Let’s got through this, line-by-line, with our new knowledge, and see if we can work out what’s going on.

# This program will convert fahrenheit to celcius, or vice-versa

This is just a comment so I know what’s happening.

puts "Current temperature:"
current_temperature = gets.strip.to_f

Here we ask the user to input the current temperature. We take the value, run it through the strip method to remove whitepsace, then convert it to a Float (i.e. a decimal) with the to_f method.

puts "Is this in celsius or Fahrenheit? [C/F]"
original_temperature = gets.strip[0].downcase

Here we ask the user whether this is in celcius or fahrenheit. We only use the first letter from the answer, and hope it’s either ‘c’ or ‘f’. We will check this later on, though.

if original_temperature == 'c'
    new_temperature = (current_temperature * 5 / 9) + 32
    puts "New temperature: #{new_temperature} F"

This code runs if the user input ‘c’ before. We find the new temperature than then output it. There’s one piece I didn’t cover here, and that’s the use of the #{} sequence in the string. For most strings, if you use this little sequence, it will evaluate the expression in the braces and insert it into the string. It’s a handy little shorthand that comes in useful in situations like these.

elsif original_temperature == 'f'
    new_temperature = (current_temperature - 32) * 9 / 5
    puts "New temperature: #{new_temperature} C"

Nothing new here – it’s almost exactly the same as above.

else  # Can't process anything else
    puts "Don't recognise temperature scale: #{original_temperature}"
end

And if they didn’t input ‘c’ or ‘f’, we tell them about it.

Conclusion

Well, that was a fair bit of information to take in. Don’t worry, though – as you start to program in ruby almost everything mentioned in this tutorial will become second nature to you. If you understood everything in this tutorial, you’re doing well!

This has been pretty full-on with information, so the next tutorial will be re-covering ground and examining various aspects of classes, methods, and control statements. In the meantime, your challenge is to write some little programming snippets to convert values. They don’t need to be big – just enough to make sure you understand what you’re doing. Hopefully, that will keep you entertained until next time.

Leave a Reply

Your email address will not be published. Required fields are marked *