The Standard Library

Digging into those helpful libraries that ship with Ruby.

4

JAN
2006

Pathname and Enumerator

I'm always digging around in Ruby's standard library looking for new toys. It's one-stop-shopping for geeks. I love it.

There really are some great tools in there that can help you do your work easier. Let me tell you a little about two I am using on a current application.

Pathname

Pop Quiz: Which would you rather work with?

if File.exist? some_dir
  data = File.read( File.join( File.dirname(some_dir),
                               "another_dir",
                               File.basename(some_file) ) )
  # ...
end

Or:

if some_dir.exist?
  data = (some_dir.dirname + "another_dir" + some_file.basename).read
  # ...
end

If you prefer the second version, the Pathname library is for you.

Ruby's file manipulations are usually fine for normal work, but anytime I start messing with directories I really start feeling the pain. For that kind of work, I find Pathname to be a superior interface. My current project makes great use of the methods in the above example as well as Pathname#entries, Pathname#relative_path_from, and more.

Look up the documentation if the above has you curious and you won't be sorry. You're only one require away from a smooth directory interface.

Enumerator

Pop Quiz: Ever wish Ruby's Strings iterated over characters instead of numbers?

unless "TEAM".enum_for(:each_byte).find { |c| c == ?I }
  puts "There's no I in T-E-A-M!"
end

Pop Quiz: Ever needed map_with_index()?

table_rows.enum_for(:each_with_index).map do |row, i|
  [row, i % 2 == 0 ? :even : :odd]
end

The magic enum_for() returns an Enumerator, which is a standard Enumerable object using the passed method as each(). This gives you the power of all the other iterators (including find() and map()), but on the data of your choosing.

This is so handy, Enumerator has been moved from the standard library to the language core, as of Ruby 1.9. Not only does this allow you to drop the require statement, the core version of Enumerator has been enhanced. All of the standard iterators will now return an Enumerator, if called without a block. That makes map_with_index() even easier to create:

table_rows.each_with_index.map do |row, i|
  # ...
end

As an added bonus, the Enumerator library adds two more handy iterators to Enumerable:

tic_tac_toe = "XO  X  OX".split("")
tic_tac_toe.each_slice(3) do |row|
  puts " " + row.join(" | ")
  puts "-" * 11 unless row == tic_tac_toe[-3..-1]
end

As you can see, each_slice() let's you grab a passed number of elements at a time. The other method, each_cons(), is similar, but will keep consecutive elements together:

"by James Gray".split.each_cons(2) do |words|
  if words.all? { |word| word =~ /^[A-Z]/ }
    puts "#{words.join(' ')} is an author."
  end
end

Note that each_slice() wouldn't have found my name, since it would have tried ["by", "James"] and ["Gray"], but not ["James", "Gray"].

Again, see the documentation for more details. Advanced Enumerator usage is Ruby Voodoo and it will help you impress your friends and coworkers.

Comments (1)
  1. Daniel Berger
    Daniel Berger February 5th, 2006 Reply Link

    Regarding Pathname, see pathname2 if you want a Pathname library that works properly on Windows (i.e. handles backslashes and UNC paths).

    Come to think of it, I think I forgot to implement relative_path_from. I'll add that to my TODO list. :)

    1. Reply (using GitHub Flavored Markdown)

      Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

      Ajax loader
Leave a Comment (using GitHub Flavored Markdown)

Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

Ajax loader