Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Saturday, March 3, 2007

Splat!!

Oh the things you can learn by reading _why's code. It really is like being in the mind of a genius.

Over the past few days I've been playing around with the Camping “micro-framework.” Like Rails, Camping a web framework built on the MVC concept. While Rails aims to be feature rich, however, camping blazes off in the direction of keeping small and light weight. Oh, and did I mention exceedingly clever? Take the time to read through the source—the unabbridged version is better for this—there's lots of goodies in there.

Anyway. One of the cool tidbits in there is defining a to_a method for using with the splat operator. The splat "*" is used to explode an array into the parameter list of a method. For example, assume the following function:

def foo a,b
  puts "You gave me a(n) #{a}, and also a(n) #{b}.  How very generous of you!"
end

Normally the function would be called passing it two parameters:

foo "Apple", "Banana"

If you use the splat you can break up an array into the method:

fruit = %w|Kiwi Grape|
foo *fruit

Now, none of this is all too earth shattering; however, it does lead to some clever uses when paired with a suitable to_a class method.

class ContinentalBreakfast
  def initialize(*offerings)
    @offerings = offerings
  end
  def to_a # Pick some random fruit...
    plate = []
    table = @offerings.dup
    2.times do
      f = table[rand(table.length)]
      table.delete(f)
      plate << f
    end
    plate
  end
end

Cool. Now we can ask somebody to bring us back a bit of a snack:

foo *ContinentalBreakfast.new('grape','pear','melon','strawberry')

Pretty neat, eh?

Thursday, February 22, 2007

Ruby Lib Path Manipulations

Mucking around with the library search path in ruby isn't exactly the hardest thing in the world. It's just a matter of fiddling around with a horrible looking perlish global variable and using __FILE__ to munge the right path together. Again, nothing too difficult but it's annoying and ugly—totally out of place next to all that beautiful ruby code.

For the most part, this isn't much of a problem for libraries. In fact, you don't normally want to hard code paths anyway. Typically a flag is passed to the ruby interpreter:

$ ruby -Isome/path foo.rb

However, in some cases you still need to change the path in the code. Likely the most common place this shows up is within test/spec files. Does this look familiar?

# some_cool_spec.rb
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')

require 'lib_under_test'

context '...' do
# snip
end

It’s so ugly, right? To me it's quite the eyesore so I wrote the following really simple library (rubylib.rb) to fix it up a bit:

module LibCommands
def lib(*path_segs)
  $:.unshift File.join(*path_segs)
end

def lib_rel(*path_segs)
  file = caller.first.split(':').first
  lib File.dirname(file), *path_segs
end
end

Object.send :include, LibCommands

Now when you want to add something to the load path, you can just use the lib method:

lib 'path', 'to', 'library'

It's even better when dealing with relative paths. lib_rel specifies the directory to add relative to the file it's called from—rather than the current working directory, which is probably different. That makes the above rspec:

# some_cool_spec.rb
lib_rel '..', 'lib'

require ‘lib_under_test’

context '...' do # snip end

Nice, eh? Sure, but you still need to require rubylib.rb first. You can fix that up with a few little tricks used by rubygems. First, create aubylib.rb that just requires rubylib.rb. That way it looks nicer when you add it in as a flag to ruby.

$ ruby -rubylib foo.rb

The next level of laziness is to add it you your RUBYOPT. With the environment variable set the two commands are available without any explicit requires:

$ export RUBYOPT="${RUBYOPT} -rubylib"
$ ruby foo.rb