.NET to Ruby: Methods and Variables
In the last post in our series on Switching from .NET to Ruby, we looked at Classes. Classes were a real in depth topic since there’s a large number of class differences between .NET and Ruby. This post is going to take an a look at Methods and Variables. First we’ll go through how to create and call methods, then we’ll look at Blocks and some application of blocks, and we’ll finish up by going through the types of variables that exist within Ruby.
As always we’ll actually be comparing Ruby to C#, because .NET is a framework not a language.
Methods
Ruby methods are quite easy – especially since there is no type system to worry about, which allows us to skip defining parameter types, and method return types. Methods in ruby are written using the def
keyword to start the method definition, and they are closed using the end
keyword, like this:
def a_method()
# this is a Ruby method
end
This is very different from a .NET method which would look like this:
public void AMethod() {
// this is a C# method
}
When using method parameters you write them inside the brackets and separate them with commas:
def a_method(a, b, c)
# this method has three parameters a, b, and c
end
As stated in the previous post on Ruby Classes, since Ruby is duck typed you don’t have to declare the data types of your parameters within your method definitions. Compare that with .NET:
private void AMethod(int a, int b, int c) {
// this method has three parameters:
// an integer a, an integer b, and an integer c
}
The difference between these two pieces of code is the amount of characters that need to be written. Ruby is known as a terse language, and small method definitions are clearly one of the things that make Ruby so terse.
Moving along, let’s look at one of Ruby’s more obscure syntaxes. Something you might see in code are methods defined without brackets – gasp!
def a_method a, b, c
# this method also has three parameters a, b, and c
end
This is perfectly valid syntax, and you can use it in your own code. Let’s back track and rewrite our first example using this syntax:
def a_method
# No brackets here
end
So you can see Ruby’s methods differ substantially from .NET’s. But before I release some cowboy coders onto the world it’s important that I tell you about accepted community practice, which is: when defining methods omit brackets for methods without parameters, and use the brackets when your methods have parameters.
Let’s move on to method calls. They look the same as .NET method calls too, and they can be done without brackets as well:
AMethod(1, 2, 3);
a_method(1, 2, 3)
a_method 1, 2, 3
All of the above lines of code call a_method
with the arguments 1, 2, 3.
Now that we’re done with creating, and calling methods, lets get into some of the interesting aspects of methods that C# lacks in, and where Ruby shines.
Blocks
We’ll start with blocks. Blocks are defined using either curly braces or between the do
and end
keywords:
{ puts "A Block" }
do
puts "also a block"
end
Conceptually blocks are like lambda’s in C#, they can be passed into a method and then executed from within that method’s context. This is done using yield:
def a_method
puts "begin"
yield
puts "end"
end
a_method { puts "block code" }
Which outputs:
begin
block code
end
And just like methods, blocks can accept variables into them. This is done using the pipe character to delimit the variable name like this:
def a_method
yield("George")
end
a_method { |name| puts name } #=> George
The equivalent code in C# would look like this:
public static void a_method(Action<string> block) {
block("George");
}
a_method((name) => System.Console.WriteLine(name));
Blocks are used heavily in Ruby, and they allow us to implement some neat features, such as C#’s using statement. For example here’s some code to write to a file in C#:
using (StreamWriter file = new StreamWriter(@"Output.txt")) {
file.WriteLine("Line 1");
}
This code opens the file Output.txt
, then writes “Line 1n” to the file, and finally closes the file handle. Ruby is capable of the same thing, but instead uses blocks to do it:
File.open("Output.txt", "w") do |file|
file.write "Line 1"
end
Pretty neat isn’t it!? In the above code, file
is a variable reference to the IO
object created by File.open
. I’ll admit that Ruby’s block syntax is a little more cryptic than C#’s syntax, since with C# you need to be explicit about your usage. This is will be a small hurdle for you to overcome when learning Ruby.
There are many more powerful applications for blocks. Take for example Sinatra. Sinatra uses blocks to create a DSL (Domain Specific Language) for it’s URL routing. Sinatra’s Hello World! example looks like this:
get '/hello' do
'hello world'
end
In the above example, get
is simply a method call with a block attached to the end of it. This makes routes super easy to write in Sinatra, and makes the code very easy to read, while still remaining expressive in it’s functionality. RubySource has a four part series on Sinatra that’s worth a look at too!
Ultimately blocks are really just an implementation of the strategy pattern that is built into the Ruby language. And because blocks are so easy to write they get used extensively when Rubyists write their code.
Variables
Moving on from blocks, let’s focus the rest of this post on the most basic item of a Ruby program, the variable. Local variables are variables that you’d write in your Ruby methods are written like this:
a_variable = 2
The equivalent code in C#:
var a_variable = 2;
Again there is no need to tell Ruby what your variable type is. Ruby figures it out on it’s own, which makes variable syntax just as terse as method syntax. Ruby variables use a naming convention called snake case. So instead of using capital letters like .NET does to distinguish between words in a variable you should use underscores. The variables below are valid variable names:
variable
a_variable
a_really_big_variable_name
The variables below are still valid Ruby variables, but they break Ruby’s naming convention, so they shouldn’t be used.
Variable
A_Variable
areallybigvariablename
When writing classes, Ruby has some special notation to distinguish between local variables and instance variables. Instance variables are prefixed with the at symbol: @
. This is significantly different that .NET so it’s worth comparing the two:
public class Example {
private int iv;
public Example() {
iv = 12;
}
}
class Example
def initialize
@iv = 12
end
end
In both Example classes above, an instance variable iv
is created within every instance of Example
. In Ruby there is no explicit declaration of the variable @iv
, instead by writing the assignment of 12
to @iv
Ruby is smart enough to determine that @iv
is your instance variable.
On top of instance variables, Ruby also has another type of variables known as class variables. For those of you coming from the .NET world, this is the same as a static variable. To create a class variable you use a double at symbol: @@
. The following code examples are equivalent:
public class Example {
private static int id = 42;
public static int GetId() {
return id;
}
}
class Example
@@id = 12
def self.get_id
@@id
end
end
As well, it’s worth mentioning that Ruby also has global variables. They are prefixed with a dollar sign: $
. Global variables aren’t something you’re likely to come across as you start to learn Ruby, so I won’t enumerate the variables that exist, however Ruby User’s Guide has a list of global variables.
And here is where we’ll draw this post to an end. By the end of this post you should be able to create methods, call methods, use blocks, and know of the 3 types of variables within Ruby. At this point if you’ve been following along in the series you have the basic building blocks to start coding your own Ruby programs. You’ve got an environment to work in, Classes to work with, and now Methods and Variables to build your routines.
For the next article we’ll do a bit of Ruby house keeping and go through the tools you can use to keep your Ruby programs readable and maintainable for other Rubyists. We’ll touch on topics like Naming Conventions, Comments, Documention, and Namespacing, so be sure to check back for that post!