Click here to view the entire IronRuby via C# series
In the previous part of my series about learning Ruby using C# we discussed constructors, instance methods, parameters, and then touched on blocks a bit at the end. Well, in this post we are going to delve a bit further into classes by talking about static methods (class methods in Ruby) , a bit more info about parameters, and then finish it up with some tidbits about conditionals and loops.
To start it off, lets look at some quick static methods in our Person class. Lets say we want to define a method that returns the total count of Person objects that have been instantiated. In C#, our class would look like this (notice that I have left out the rest of the class for brevity):
public class Person { private static int instanceCount = 0; public string FirstName { get; set; } public string LastName { get; set; } public Person(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; instanceCount++; } public static int InstanceCount() { return instanceCount; } }
So, as you see, we just have a static variable named instanceCount that tracks the number of instances created. We then have a static number that allows us to retrieve this number. In Ruby we actually have several ways to define a method like this. One is to use “self” and one other (there are several) is to use the class name (in our case “Person”). We are going to use “self” in our example, but you can just replace that with the class name if you like:
class Person @@instance_count = 0 attr_accessor :first_name attr_accessor :last_name def initialize(first_name, last_name) self.first_name = first_name self.last_name = last_name @@instance_count += 1 end def self.instance_count return @@instance_count end end
Here you will notice the static variable from the previous post, and we have our corresponding static method. Pretty cool, huh? Looks extremely similar to C# so far. You would consume this just as you would in C#:
puts Person.instance_count
Okay, so we discussed static methods, and now I have a few more quick things that I want to show you about parameter passing in Ruby. The first thing is that Ruby supports default parameters! I have always wanted default paramters in C#, and I have heard all of the arguments about just using overloads, and I just don’t buy it. I want me some default variables, and I want them now! So, in Ruby if I wanted to default the parameters in my constructor, I could just do this:
def initialize(first_name = "John", last_name = "Doe") self.first_name = first_name self.last_name = last_name @@instance_count += 1 end
So great! Now, I can call the “new” method without any parameters and it will default one or both parameter values for me. So doing this…
person = Person.new puts person.first_and_last_name
Results in “John Doe” being written to the console. In order to get the same effect with C#, we’d have to define three constructors:
public Person(): this("John", "Doe") { } public Person(string firstName) : this(firstName, "Doe") { } public Person(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; instanceCount++; }
Not too terrible, but defaults would have been nice!
Now, there is one other thing that Ruby does with parameters and that is to allow hashes to be passed as parameter lists. Since we have yet to discuss hashes, I am going to skip this for now, but we will come back to it later on!
Lets take a look at a boolean in Ruby. In C# we just declare our boolean like this:
bool isValid = false;
And in Ruby, since types are implicit, we would just do this:
is_valid = true
So, what about an if statement? In C#, our if statement would look like this:
if (isValid){ //do something }
And our Ruby if would look almost the same:
if (is_valid) #do something end
But Ruby supports the unless statement… unless?
unless (is_valid) #do something end
Yep, unless. It is the negation of “if”. This would run the code block only if “is_valid” is false (aka not true). We would just do this in C# (which also works in Ruby):
if (!isValid){ //do something }
But the fun doesn’t stop there! Ruby also less us put the “if” and “unless” statements at the end of a line! For example, I could do this:
puts "It is valid!" if is_valid
Very interesting. It now reads almost like an English sentence, and does it exactly what it sounds like. It writes out “It is valid!” if “is_valid” is true. We can also do the same with “unless”:
puts "It is not valid!" unless is_valid
Pretty sweet. So now that you have seen these statements, you can also be aware that Ruby supports pretty much all of the normal boolean operators that C# does, and so I’m not going to go through them one by one. We will use them in our next section though.
On with the loops! Ruby supports many different looping mechanisms like “while”, “until”, “do..while”, “do..until”, and “for”. A C# for loop that looks like this:
for (int i = 1; i <= 10; i++){ Console.WriteLine(i); }
Would look like this in Ruby:
for number in 1..10 puts number end
The biggest difference is that in Ruby, these classic looping constructs are almost never used! I’m not even going to bother going through them! In Ruby the idea that you most often see is that objects themselves should be responsible for their own looping. This is evident in the use of the “each” method.
You have probably seen C#’s “foreach” statement quite a bit:
foreach (string name in names){ Console.WriteLine(name); }
Ruby has a similar construct:
names.each do |name| puts name end
But if you combine that with Ruby’s excellent support for ranges (yes, C# has a Range class as well, Ruby’s is just easier to use) then you can do the above for loop like this:
(1..10).each do |num| puts num end
And that is the way that you will see it done quite a bit. In fact, if you had done “(1…10)” then it does 1 to 10 exclusive (meaning only 1 to 9). Since we are starting at 1, you could also write it like this:
10.times do |num| puts num + 1 end
We had to add in the “+ 1” since the “times” method starts at 0. But it does execute it 10 times. Again, we are starting to see a recurring pattern with Ruby. And that pattern is that there are 18 million ways to do everything! If you don’t like choice, then Ruby may not be the language for you!
And there you have it folks. I am going to quit there for now and let you soak it all up. We have covered some static methods, default parameter goodness, boolean madness, and finally a little bit of looping. In the next post I’ll talk a bit more about Ranges, and then delve into the hash tables (or associative arrays) that we mentioned earlier. I will also discuss arrays in Ruby and why they are different from C# arrays.
I hope you enjoyed the post, and please let me know if there is anything that you have questions about or would like to see!
Loved the article? Hated it? Didn’t even read it?
We’d love to hear from you.
This is good stuff man. One cool thing about the Ruby language as well is that symbols are allowed in method names.
For example, a couple of the idioms that I’ve seen is that person.is_valid would normally be written as person.is_valid? Which actually improves the readability that much more. I’ve also seen !’s used for alternate implementations of methods that are critical or will throw exceptions… like person.destroy! or person.validate!
Keep up the good posts!
@Joey Yeah, it is a shame that Ruby doesn’t support putting question marks at the end of variables. But I will try to work in the question mark and exclamation marks into a future post. Thanks for the tip!
Testing comment from home computer; why I am not successful posting comment to Justin’s blog? I like the spell check in the comment window.
I think it’s worth noting that, as of .NET 4.0 (VS2010), C# also supports optional parameters… and the name of the parmeter name may be used in the invocation… either to identify the parameter, or just to make the code more self-documenting (which I find especially good for booleans and magic-numbers.
public Person(string firstName=”John”, string lastName=”Doe”) {
this.FirstName = firstName;
this.LastName = lastName;
}
…..
new Person(lastName:”Deer”);
…..
// This is pretty cryptic. I’d have to look at the API to see what those parameters mean…
doc.Print(297, 210, true, true, true);
// whereas this is self-explanatory
doc.Print(paperHeightInMillimetres:297, paperWidthInMillimetres:210, shrinkToFitPage:true, doubleSided:true);