Pathname and Enumerator

Posted over 6 years ago in The Standard Library.

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 (development version). Most people will see this change when Ruby 2.0 releases. 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.

Daniel Berger added about 1 month later:

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. :)

Dan

Daniel Berger added about 1 month later:

Hey, what happened to my underscores?!

James Edward Gray II added about 1 month later:

They got markdownified. I'm using it for formatting.

Daniel Berger added about 1 month later:

I'd like to make two feature requests then. First, a help link explaining how the markup works for your particular site. Second, a preview button. :)

I'm enjoying your blog, btw.

Thanks,

Dan

James Edward Gray II added about 1 month later:

(I fixed your underscores, escaping them with a backslash.)

Here's a link to Markdown documentation, the formatting language available in these comments.

As for the preview feature, this blog is the famous Typo: patch away. :)

James Edward Gray II added about 1 year later:

My comment above is now out of date. This blog no longer runs on Typo.

Add Your Thoughts

You can use Markdown in the body of your comment to format text and make links.

Note that I reserve the right to edit any content you post here. I typically exercise this right to fix formatting issues. All posts must be approved so spam will never be seen on these pages.

Author:
URL or Email (optional):
Body: