Using Ruby to Process m3u Playlists

Written By: Nathan Baker

- 06 Apr 2006 -
















Description: Programming in Ruby is fun! In this tutorial, we will use Ruby to process the m3u playlists used by a number of media players.

  1. Introduction
  2. Part 1: Getting our feet wet
  3. Part 2: Slowly getting there
  4. Part 3: I/O, I/O, it's off to work we go
  5. Part 4: Cleaning up our mess
  6. Part 5: The extra mile

Part 2: Slowly getting there

So now we have a class that allows you to use the [] operator to access and assign to an array, but that's pretty lame. If the user wanted that, he or she would just use an array. Well, what else would you want to do with a playlist? How about play through it? Yeah, let's do that. In Ruby, you enumerate over things by using the each message, so let's implement each:

class Playlist
        def each
                @playlist.each{|item| yield item }
        end
end

If you're using IRB, the interactive Ruby shell, you can just type that right there and it will add it on to the Playlist class. If you're editing a file, you don't have to re-type the class Playlist stuff.

So what's up with that? The C people are scratching their heads, and even the PHP and Perl people are lost. The LISP and Smalltalk crowds (assuming they're still around) are probably right with me, though. Iteration and enumeration work completely differently in Ruby from most other languages. For instance, here's a for loop in C:

for(unsigned int x = 0; x < 43; ++x){
        printf("%u",x);
}

And here is a comparable loop in Ruby:

0.upto(42) {|x| puts x}

Alternatively, you could write:

42.times {|x| puts x}

This is kind of bizarre...but it also makes sense, once you get used to it. So once you've comprehended this, it shouldn't be too surprising that iteration over an array looks like

arr.each {|x| puts x}

"OK, Nathan, I get the point with the iteration and enumeration," you're probably saying. "But what's that funky vertical bar thing?" Well, I'm glad you asked. You see, you're probably looking at the curly braces and thinking code block. And you're right. But at the same time, you are completely wrong.

Blabbing about blocks

In LISP, you can represent an arbitrary bit of code using the lambda keyword.

(map (lambda (x) (+ x 1)) '(1 2 3 4 5))

Will add one to each element in the list '(1 2 3 4 5). If that line of code looks completely foreign to you, just take my word for it. The lambda expression there passes the function "add one to x" to the function map, which applies its first argument to each element of the second argument.

Blocks of code in Ruby work this same way. You can either use curly braces or the do..end keywords. Since I myself also come from the C tradition, I am more comfortable with curly braces. Pascal-type people will probably find the following more familiar:

arr.each do |x|
        puts x
end

This is equivalent to the previous enumeration, above. You are passing the code block (in CS terms we call this a closure, but you won't be tested on that) to the each function. The argument in the vertical bars, then, is the parameter of the code block. For each item in the array, x will take that value for one run through the loop.

So now that code above is starting to make sense. But if you've really been thinking about what I've been saying, you will be wondering "how does the called function then execute the code it is given?" and that is an excellent question. The answer is the yield statement.

As you see above, we have

@playlist.each{|item| yield item }

What yield does is it says "stop my execution and start executing the block of code I was passed". You can give parameters to yield--in this case, we are passing item into the block of code that the user gave us. Pretty darn fancy, I say.

<< Previous

Next >>