There’s a lot of these little pieces and dealing with all of them can be irritating. However, ignoring them results in crappy code filled with lots of while loops, “continues” and broken modularity.
In this article, we cover little tidbits in Ruby (often basic methods) that make you a lot more productive if you can use them properly.
Let’s dive in!
One mantra you’ll want to learn by heart: “while loops can be carcinogens”
On a serious note, overuse of while loops and increment variables (I’m looking at you, i) often makes Ruby code ugly and irritating to mentally step through.
Thus, I present to you “each”:
[1, 2, 3, 4, 5].each do |e|puts eend
I’m sure all of you have most likely seen “each” in action before, but it is important to know when to use it. As a rule of thumb, whenever you’re trying to use a while loop over an array, you can probably use each (or one of its cousins).
“each” can be called on a multitude of objects, arrays being the most popular (debatably). You can pass in a block, which will be executed over the elements of the array.
Similarly, the Array class has a ton of methods that take blocks and run them over the Array. A particularly interesting one is called “map”:
a = [1, 2, 3, 4, 5]b = a.map do |x|x = x + 1endp b
How it works is simple – it runs the block over the array and returns a new array. In the example, we should get an output of [2, 3, 4, 5, 6]
That example is in fact even more interesting, because we don’t use “puts”, instead, we just write “p”, which is short for “puts”. It is frowned upon to use “p” in real code, but, in debugging, it is a blessing.
Now, what if you didn’t want map want to return a new array, just edit the old one in place? Well, there’s map! for that:
a = [1, 2, 3, 4, 5]a.map! do |x|x = x + 1endp a
As we can see, it modifies the array in place.
Ruby veterans have used map and map! to get out of many a tough situation – use them to your advantage.
String processing is something that most developers do on a pretty regular basis, because there’s a lot of ground between “separated by spaces” an “need to write a compiler”.
Once again, looping can be a problem. If you find yourself iterating over the characters of a string, you’re probably doing something wrong (in Ruby; in C, this is what you have to do most of the time, because string processing facilities are basically nonexistant).
First of all, let us consider the all-important “split” method:
def get_words(sentence)sentence.split(" ")endputs get_words("This is pretty awesome")
As you can see, we have one method (get_words) that takes a sentence and returns the words. And, it does this using the
split can be called on a String, and it returns an array with the string split at the specified delimiter (in our case, a space character). Fairly simple. But, it gets a lot more interesting because we can use regular expressions to define the delimiter.
The split method becomes very powerful, and it has been used in a ton of creative ways (don’t be too creative though – too much regex can make your code a living hell for the guy who has to read it).
Let’s take a look at
gsub, another interesting method:
p "how you doing".gsub(/ /, '-')p "john is a great guy".gsub(/john/, 'dhaivat')
gsub takes a first argument of a pattern and a second argument of a string. Wherever that pattern matches in the string is replaced by the replacement text.
The first call to
gsub adds hyphens to the string, resulting in “how-are-you”. The second call gives “dhaivat is a great guy”.
gsub is used heavily and, if you are familiar with regex (and definitely should be!), is incredibly powerful.
Of course, the String class also offers the
gsub! method, which modifies the string in place.
Now, let’s bring up a pretty obvious one; concatenation:
a = "hello, how are you"a << " doing today"p a
That’s very clear.
<< appends one string at the end of another.
Finally, we have
"hello, world!".match(/o/) do |matched|p matchedend
match method is called on a string, you pass in a pattern, and it returns an iterator. So, in the block that we have added, the places where the pattern matches (in this case, /o/) are passed onto the block. The result should be two “o”‘s.
That’s a large portion of the string processing methods you’ll be using in Ruby.
What you can accomplish with this seemingly small set of tools is actually quite astonishing. In fact, I recently saw a complete HTML parser that used only these few methods for 90% of the code. Now, I’m not saying that its a good idea to parse HTML using regular expressions (in fact, in case you’re wondering, it is quite a horrible idea) but the fact that it is possible with just these functions is quite impressive.
To try your hand at these techniques, try each of the following:
- Write a CSV file parser
- Develop a function that splits a sentence into words, taking into account punctuation
- Parse HTTP requests, only using the functions explained above
The last one is a unique challenge, especially if you parse the URLs as well.
Marshal is really simple yet quite interesting.
Marshal.dump takes any Ruby object (except for a couple of edge cases like anonymous cases) and gives you a string representation.
Marshal.load takes any string and tries to make a Ruby object out of it. Of course, there’s an infinite number of combinations which cause it to choke, so you’ll want to ensure that the string you’re passing in was actually serialized earlier on with
Where would one use this? Of course, when writing servers (especially stuff dealing with RPC, or, remote procedure call) you can just push data by serializing it with
Marshal instead of coming up with your own special format. This assumes that both ends of communication agree on the method of serialization.
Marshal several times in some particularly sticky situations with Rails. Often times, when you need to get some pieces of your application talking, you can use
Marshal. It’s built right into Ruby, so you don’t need to worry about adding another dependency, which can be important if your app will be running on someone else’s server.
An exercise to try to implement (and then never use; it is not meant to be peformant) would be to build a simple database using only Marshal and a text file. Its an interesting problem – have a go at it!
Hopefully, that was an interesting assortment of topics from different places in Ruby. Initially, they seem quite trivial but Ruby uses these methods over and over again. As such, it’s important for all Ruby hackers to familiar with them.