Ruby Voodoo

Deep dives into random corners of my favorite programming language.

3

OCT
2008

I'm Addicted to the Word Array

Continuing with my recent trend of showing of fun uses of Ruby syntax, I have a confession to make: I'm addicted to Ruby's "word Array." I really am.

I suspect most of you know this, but the word Array is a shortcut that can lessen the quote-comma-quote syndrome of simple a simple Array like:

["a", "wordy", "Array"]

You can create the same Array with the word Array syntax:

%w[a wordy Array]

That's essentially just a String that will automatically be split() on whitespace to build an Array. You can use any amount of space any place you like, so you can layout the data in whatever way makes the most sense for you:

require "pp"
pp %w[ one   two   three
       four  five  six
       seven eight nine
             zero        ]
# >> ["one",
# >>  "two",
# >>  "three",
# >>  "four",
# >>  "five",
# >>  "six",
# >>  "seven",
# >>  "eight",
# >>  "nine",
# >>  "zero"]

Note that you can chose the punctuation characters used at either ends of the Array, some of which are paired while others just repeat:

%w(a wordy Array)
%w{a wordy Array}
%w<a wordy Array>

%w!a wordy Array!
%w%a wordy Array%
%w#a wordy Array#

I tend to just stick with %w[ … ] though because I like how it parallels the [ … ] for a normal Array. It's pretty smart too and will allow nested brackets, as long as they are paired properly:

p %w[ [[]] abc[1] [start end] ]
# >> ["[[]]", "abc[1]", "[start", "end]"]

With just that much, this feature is crazy useful. Here are examples with Rails validations and filters, platform-safe relative path building, and simple iteration just to give you some ideas:

STATUSES = %w[Pending Requested Booked Declined Submitted]
validates_inclusion_of :status, :in => STATUSES

before_filter :find_user, :only => %w[show edit update destroy]

$LOAD_PATH << File.join(File.dirname(__FILE__), *%w[.. lib])

Card  = Struct.new(:face, :suit)
cards = [ ]
%w[heart spade club diamond].each do |suit|
  %w[A 2 3 4 5 6 7 8 9 T J Q K].each do |face|
    cards << Card.new(face, suit)
  end
end

Everyone of those examples was pulled out of real code I've written. I'm a serious 12 stepper on this issue.

That's the basics. However, like most delights in Rubyland, the word Array hides some lesser known surprises. First, did you know that you can embed entries with spaces in them?

SORT_OPTIONS = %w[ Default
                   Name
                   Price
                   Average\ Rating ]
p SORT_OPTIONS
# >> ["Default", "Name", "Price", "Average Rating"]

As you can see, a simple backslash escapes spaces. Now this is a technique you can definitely abuse and if you find yourself escaping every other space, you should probably just break down and build a normal Array. This is great for the occasional multiword entry though.

It doesn't stop there either. Remember when I said the word Array is essentially a String? Well, you can even mix in some interpolation, if you switch to the capital W:

MODE = Object.const_defined?(:Test) ? :test : :data
PATH = File.join(File.dirname(__FILE__), *%W[.. .. #{MODE} db.sqlite])

This is code from a trivial SQLite wrapper I made. It determines what MODE to run in with a simple test that will pass after Test::Unit has been loaded. It then builds a PATH to the database using the selected MODE. It's interpolation in the word Array that makes that easy to do.

It's probably worth mentioning that interpolated values are not subject to internal splitting:

var = "three four"
ary = %W[one two #{var}]
p ary
# >> ["one", "two", "three four"]

The capital W version supports some other String escapes as well, though I confess that I've never made use of this:

p %W[\t \x20 \n]
# >> ["\t", " ", "\n"]

Well, that's all the word Array goodness I can pack into a single blog post. Hopefully you are sold and I'll be seeing a lot more of you at the Word Array Anonymous User meetings.

Comments (1)
  1. Dan Manges
    Dan Manges October 5th, 2008 Reply Link

    I like %w for rake task dependencies.

    task :foo => %w[db:prepare some:other:task]
    
    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