It’s nice to do some Ruby once in a while. Lately I’ve been involved in a Silverlight project and haven’t touched Ruby in 2 months (hence the lack of activity on my Ruby blog). However, I’m making a build script in Ruby.

I have run into a weird exception when using my favorite IO gem RIO (btw, Noobkit page got beaten with the ugly stick and parser needs a spanking).

Exception occur when calling rio(...).rmtree or rio(...).mkpath or rio(...).mkdir. It reads as follows: “wrong number of arguments (0 for 1)”. I know for a fact that these methods don’t take any arguments, but just for kicks, passing a single random argument results in an ironic “wrong number of arguments (1 for 0)”.

Basically the problem came down to the fact that RIO doesn’t like Rake. I can live without Rake, but not without RIO.

I looked all over and couldn’t find a bash script which could check and if missing install a list of gems. I had to hack my own up:

GEMS=( `cat gems.txt` ) # gems.txt has one gem name per line

for gem in "${GEMS[@]}"; do
  found=`gem list $gem | grep -i $gem`
  if [ "$found" != "" ]; then
    parts=( $found )
    echo "Found ${parts[0]} ${parts[1]}"
    continue
  fi

  echo "---------------------------"
  echo "Installing $gem"
  sudo gem install $gem -y
  echo "---------------------------"
done

Sometimes you want to focus on just one example in your RSpec file. I had to do this quite often in the last 3 days and I got tired of commenting out stuff back and forth. So, I give you “it_only” example.

module Spec
  module DSL
    module BehaviourEval
      module ModuleMethods
        def it(description = :__generate_description, opts = {}, &block)
          return if @it_only_found
          examples << Example.new(description, opts, &block)
        end

        # Same as +it+ only blocks all other examples making this the
        # only example that would run.
        def it_only(description = :__generate_description, opts = {}, &block)
          @it_only_found = true
          @examples = [Example.new(description, opts, &block)]
        end
      end
    end
  end
end

Drop this into your spec_helper.rb and now if you want to focus on a single example, just make it “it_only” instead of “it“.

I’ve recently started experimenting with using Ubuntu for my Ruby development. Some of the Gems like JSON and HPricot have extension compilation step which doesn’t work on a default Ubuntu installation.

Here are a few steps that you need to get it to work.

  1. Open up Synaptic Package Manager from System > Preferences
  2. Search for “ruby1.8-dev”, right click and check “Mark for installation”
  3. Search for “build-essential” and mark it for installation as well
  4. Click “Apply” in the toolbar

After the update is finished, you should be able to install Gems which require native extensions compilation.

If I were to be asked what is faster, regular sub string operation or a regular expression, I would without hesitation answer that sub string is.

This was the assumption that I approached a simple task with - stripping slashes from beginning and end of a string. Here’s the code:

path = path[1..-1] if path[0, 1] == '/'
path = path[0..-2] if path[-1, 1] == '/'

I would naturally assume that the block above would be faster than path.gsub!(/^\/|\/$/, ''). But just in case, lets benchmark to be sure.

require 'benchmark'

original = '/hello/somewhat/long/path/here/'
max = 1_000_000

puts Benchmark.measure {
  1.upto(max) do
    path = original
    path = path[1..-1] if path[0, 1] == '/'
    path = path[0..-2] if path[-1, 1] == '/'
  end
}

puts Benchmark.measure {
  1.upto(max) do
    path = original
    path.gsub!(/^/|/$/, '')
  end
}

# prints out
  4.212000   0.000000   4.212000 (  4.270000)
  2.418000   0.000000   2.418000 (  2.435000)

I don’t understand why, but gsub is 57% faster. I find it hard to believe that a few extra Ruby statements introduce so much overhead that it becomes slower than entire regular expressions engine. Anyone has any explanation for this?

Nested hashes could be used to represent tree structures. Here’s a code to to find a node by key and get all of its parents:

def hash_history(hash, desired_key, &block)
  return false unless Hash === hash

  hash.each_pair do |key, value|
    if key == desired_key or hash_history(value, desired_key, &block)
      yield(key, value)
      return true
    end
  end

  return false
end

hash = {
  :level_1 => {
    :level_2 => {
      :level_3 => {
        :search => 'test'
      }
    }
  }
}

hash_history(hash, :search) { |key, value| puts key }

# prints out...
# search
# level_3
# level_2
# level_1

It would appear that RDoc Ruby parser in Ruby 1.8.6 has a bug which leads to attributes always being public.

Here’s the fix in parser_rb.rb:

...

def parse_attr(context, single, tk, comment)
  args = parse_symbol_arg(1)
  if args.size > 0
    name = args[0]
    rw = "R"
    skip_tkspace(false)
    tk = get_tk
    if tk.kind_of? TkCOMMA
      rw = "RW" if get_bool
    else
      unget_tk tk
    end
    att = Attr.new(get_tkread, name, rw, comment)
    att.top_level = @top_level
    att.visibility = context.visibility  ######### NEW LINE ##########
    read_documentation_modifiers(att, ATTR_MODIFIERS)
    if att.document_self
      context.add_attribute(att)
    end
  else
    warn("'attr' ignored - looks like a variable")
  end
end

...

def parse_attr_accessor(context, single, tk, comment)
  args = parse_symbol_arg
  read = get_tkread
  rw = "?"

  # If nodoc is given, don't document any of them

  tmp = CodeObject.new
  read_documentation_modifiers(tmp, ATTR_MODIFIERS)
  return unless tmp.document_self

  case tk.name
  when "attr_reader"   then rw = "R"
  when "attr_writer"   then rw = "W"
  when "attr_accessor" then rw = "RW"
  else
    rw = @options.extra_accessor_flags[tk.name]
  end

  for name in args
    att = Attr.new(get_tkread, name, rw, comment)
    att.top_level = @top_level
    att.visibility = context.visibility   ######### NEW LINE ##########
    context.add_attribute(att)
  end
end

Code insight for a local class?

Here’s a quick 3rdRail overview in screenshots (flickr required).

Code insight for a local class?

CodeGear released 3rdRail this morning. A few highlights:

  • Full Rails project support.
  • Rails specific refactoring, ie renaming a method in a controller will update all references as well as link_to and rename the associated view file.
  • Console with command completion.
  • Integrated Gecko browser with request monitor, DOM source, CSS and JavaScript

You can watch a screen cast here and download a trial for Windows, Unix and OSX here.

Here’s a quick 3rdRail overview in screenshots (flickr required).

First distributed Ruby attempts

Noobkit is powered at its core by RDoc. Obviously it’s not real time, far from it. In fact, full refresh of Noobkit’s database which includes 26 Gems together with Rails and Ruby Core takes about 25 minutes on a single Core 2 Duo 2.4Ghz with 4GB ram box.

About 20 minutes of that time is taken by RDoc parsing the source code. This is what I’m currently working on making better. I have used this article as a starting point and wrote a client/server script on top of customized RDoc.

The end result is having 4 threads running across 2 boxes each parsing a separate source file fed to it by the server.

This being the first time I’ve worked with DRb or Ruby threads in general, one thing that kept annoying me was inability to break with Ctrl+C when using DRb.thread.join. I’ve devised a simple waiting loop instead:

def wait_for_it(&block)
  return if block.nil?
  loop do
    sleep(0.1)
    break unless yield
  end
end

# ...do DRb magic...

# This will stall the current thread until DRb thread is finished.
wait_for_it {  DRb.thread.alive? }

The same function can also be used to insure that all workers have finished their job before doing something else:

# When a worked is done, it's added back to the queue
wait_for_it { @available_workers.size != $workers.size }

RSpec is great:

it "should be its own root" do
  @node.root.should == @node
end

it "should add a child" do
  @node.children.size.should == 0

  child = @klass.new

  @node.add_child(child)
  @node.children.size.should == 1

  child.parent.should == @node
  child.root.should == @node
end

Looks beautiful. However, look at line #2. What would the output be if that test fails? The message would be something like this: "expected #{@target.inspect} to the same as #{@expected.inspect}".

Inspecting an object like a tree node could result in multiple pages worth of data and the output basically becomes unreadable if there’s an array of objects.

This problem could easily be fixed with a custom expectation matcher.

module BeTheSameAsMatcher
  class BeTheSameAs
    def initialize(expected)
      @expected = expected
    end

    def matches?(target)
      @target = target
      @target.eql?(@expected)
    end

    def failure_message
      "expected <#{to_string(@target)}> to " +
      "the same as <#{to_string(@expected)}>"
    end

    def negative_failure_message
      "expected <#{to_string(@target)}> not to " +
      "be the same as <#{to_string(@expected)}>"
    end

    # Returns string representation of an object.
    def to_string(value)
      # indicate a nil
      if value.nil?
        'nil'
      end

      # join arrays
      if value.class == Array
        return value.join(", ")
      end

      # otherwise return to_s() instead of inspect()
      return value.to_s
    end
  end

  # Actual matcher that is exposed.
  def be_the_same_as(expected)
    BeTheSameAs.new(expected)
  end
end

As you can see, methods failure_message and negative_failure_message define our error messages. Instead of default inspect call, I’m using a custom to_string method which will either return a join for an Array or to_s for any other object.

To make this available in all of your specs, the module needs to be added in your `spec_helper.rb` like so:

require 'spec/be_the_same_as'

Spec::Runner.configure do |config|
  config.include(BeTheSameAsMatcher)
end

After this, we can change our line #2 from the original script to this:

it "should be its own root" do
  @node.root.should be_the_same_as(@node)
end

After almost 8 months of working with Ruby, it still offers something new every day. Here’s a tricky part about overriding a constructor without any arguments:

class A
  def initialize
    @foo = 123
  end
end

class B < A
  def initialize(special_argument)
    # This call will result in "wrong number of arguments (1 for 0)"
    # exception because Ruby automatically passes all arguments.
    super
    # ... the same as ...
    super(*args)

    # Special case of calling super class' constructor without
    # argument - you MUST include parentheses to indicate
    # that you aren't passing any arguments.
    super()
  end
end

Obviously, I would’ve known that if I read the manual, but it would be too easy.

Converts a string to a URL style permalink.

class String
  def to_permalink
    s = self
    s = Iconv.iconv('ascii//ignore//translit', 'utf-8', s).to_s
    s.gsub!(/^W+|W+$/, '')
    s.gsub!(/W+/, '-')
    s.strip!
    s.downcase!
    s.squeeze!(' ')
    s.gsub(/ +/, '-')
  end
end

Unindenting a block of text involves finding a common minimum indent in every line and removing it. Here’s how I have done it. As with any text processing, the larger the text is, the slower it is.

def unindent_text(src)
  indents = []

  src.each_line do |line|
    indents < < $1.length if not line =~ /^s*$/ and line =~ /^(s+)/
  end

  indent = indents.min
  return src if indent == 0

  lines = []

  src.each_line do |line|
    lines << line.gsub(/^s{#{indent}}/, '').gsub(/(rn|n|r)$/, '')
  end

  lines.join("n")
end

If you are suddenly getting in `latest_partials': undefined method `[]' for nil:NilClass error when calling some gem methods like Gem.latest_load_paths or Gem.path, check your gems folder (on a PC it’s /Ruby/lib/ruby/gems/1.8/gems). Make sure there are no foreign folders or files in there. Rubygems expects all folders to be in <name>-<version> format.