Friday, June 22, 2007

Mocha stubs with multiple parameters/result sets

I've just decided to give a try to some mock framework for Ruby. Actually a mock framework, since I never used one seriously before. Among other options (FlexMock, RSpec and ruby-mock) I came across Mocha, which looked like a rather good framework. It's not trivial to decide on the best one because, aside from ruby-mock, which is pretty old, all of them perform equally well (or bad) in the opinions I found on the Web.

My immediate need was for a quick way to define stubs. The class I wanted to test would call some methods in another object which needed a specific behaviour. One method of that other object needed to provide the class under test with results which depended upon the parameters passed in. A pretty common scenario, I believe. However, the documentation does not state how to stub a method like that, when no particular order of calls is required. (It says you can set an expectation on the order parameters will be passed in and results returned, but that's beyond my needs.)

Fortunately, the Mocha::Mock::stubs method may be invoked multiple times with the same method name, as I discovered trying it out. For each expectation returned you can set different parameters and return values, hence simulating an object that acts based on its parameters, without tying it to any particular order. Here's a test-case which creates a mock for a calculator object:

require 'mocha'

class CalculatorTestCase < Test::Unit::TestCase
  
  def test_add
    calculator = mock
    calculator.stubs(:add).with(1, 2).returns(3)
    calculator.stubs(:add).with(3, 7).returns(10)
    
    assert_equal(3, calculator.add(1, 2))
    assert_equal(10, calculator.add(3, 7))
  end
  
end

I still need to better explore the world of a mock framework, but am already feeling ashamed of not having tried it before. I'm sure from now on I'll write a lot less code inside test-case files.

Wednesday, June 20, 2007

Inside a class method "self" points to the class!

I often find this kind of construct in Ruby source files (Ruby's stdlib included):

class Parser

  # ...

  def Parser.parse_file(filename)
    parser = Parser.new(File.open(filename, 'rb'))
    parser.parse
  end

end

However, when you are inside a class method, the self variable already points to the class of the method. Thus, prepending the call to new with the class' name is unnecessary repetition. Moreover, although either self or the class name is still required before the name of a class method definition, I find the former form a better option. In the event of a refactoring involving a change in the class name, you'd be less prone to mistakes.

So, the code above could have been written as:

class Parser

  # ...

  def self.parse_file(filename)
    parser = new(File.open(filename, 'rb'))
    parser.parse
  end
  
end

There's a small caveat regarding inheritance, though. In the first construct, parse_file will always create an instance of Parser, while the second will create an instance of the class specified in the call to parse_file. That might be desirable or not.

Tuesday, June 19, 2007

E-mail address leaked from RubyCorner to spammers?

A few minutes ago I received a spam message at my Inbox. A snippet follows:

Subject: Information
From: INFINITY LOTTERY <infinitylottery@yahoo.de>
To: Administrator <"#{'q'.succ}ubycorner@romulo.e#{2+2}ward.com">

INFINITY LOTTERY PROMOTIONS THE NETHERLANDS
FROM:THE DESK OF THE PRESIDENT
INFINITY LOTTERY PRIMOTION/PRIZE AWARD DEPT
Ref. Number:WRH/12726/PZP
Batch Number:29/064701/904HM

ATTENTION.

Sir/ Ma/ Miss,

We are pleased to inform you of the result of the winners of the Infinity
Lottery Promotions
programs held on 14th June,2007.Your e-mail address attached to ticket
number 085---2356789--789
with serial number 8567--46 drew lucky numbers 9- -01-23455-34 which
consequently won in the
category A.You have therefore been approved for a lump sum pay of US
$1.200,000.00(one million two hundred
thousand United States Dollars)in cash credited to file Ref.
Number:WRH/12726/PZP.This is from a total
cash prize of US $16.800,000.00usd international winners in this category.
CONGRATULATIONS!!!

(...)

Most interesting is the address to which the message was sent (mangled here by me), a unique one I generated on June 3, 2007 using E4ward to sign up for RubyCorner. I didn't give it to anyone else. That leaves only two possibilities:

  1. It somehow leaked from RubyCorner. I don't see anyway to get the e-mail of other users or set a specific option on my profile regarding visibility. Thus, if not intentionally leaked, it was stolen.
  2. The spammer used a dictionary attack against the sub-domain romulo.e4ward.com. That seems unlikely. What kind of idiot would perform such an onerous attack to find addresses among no more than a few dozens which are known to be from an e-mail forwarding service?

To be really sure I need to generate a random address and sign up again for RubyCorner. Meanwhile, if you also suspect the address you have registered at RubyCorner was abused, please, let me know.

Wednesday, June 13, 2007

Creating an object from the class name

How do you create an object in Ruby given its class name as a symbol or string?

It turns out that that's not as obvious as using send to call methods in an already existing object. You need to use the Kernel.const_get method to convert your symbol or string into a class reference (in Ruby class names are constants):

def create_object(klass)
  Kernel.const_get(klass).new
end

my_hash = create_object(:Hash)

There's no good reason to use that trick in such a simplistic example, but it could be really helpful if you were reading the class names from a file or when out of the class' scope (perhaps violating encapsulation!).

Friday, June 1, 2007

Quick mapping between XML and Ruby objects

Often you'll find yourself reading an XML file and writing boilerplate code with REXML.

I was in the middle of a tool that'd generate Ruby code from an XML Schema when I started feeling the pain of working directly with REXML. From that point it was only a matter of hours to come up with this simple and elegant solution:

require 'rexml/document'

class XMLDef
  attr_accessor :text

  def self.xml_attribute(attr_name, xml_elem)
    attr_accessor attr_name

    @attributes = Hash.new unless defined?(@attributes)
    @attributes[attr_name] = xml_elem
  end
  
  def self.xml_array(array_name, xml_elem, xml_class)
    attr_reader array_name
    
    @arrays = Hash.new unless defined?(@arrays)
    @arrays[array_name] = [xml_elem, xml_class]
  end
  
  def self.xml_object(object_ref_name, xml_elem, xml_class)
    attr_reader object_ref_name

    @objects = Hash.new unless defined?(@objects)
    @objects[object_ref_name] = [xml_elem, xml_class]
  end
  
  def self.load_element(elem)
    result = new
    
    result.text = elem.text
    
    if @attributes
      @attributes.each do |k, v|
        result.send(k.to_s + '=', elem.attributes[v])
      end
    end
    
    if @arrays
      @arrays.each do |k, v|
        result.instance_variable_set('@' + k.to_s, Array.new)
        
        elem.each_element(v[0]) do |x|
          result.send(k).send('<<', v[1].load_element(x))
        end
      end
    end
    
    if @objects
      @objects.each do |k, v|
        if x = elem.elements[v[0]]
          result.instance_variable_set('@' + k.to_s, v[1].load_element(x))
        end
      end
    end
    
    return result
  end
  
  def self.load_xml(xml)
    return load_element(REXML::Document.new(xml).root)
  end
end

To create a mapping you simply inherit from XMLDef. xml_attribute, xml_array and xml_object create the accessors. load_element recursively reads the XML into the objects you define. load_xml is the method you call on the root of your mapping to read the actual XML.

For example, suppose you want a mapping for the following XML:

<planetSystem name='Solar System'>
  <dimensions diameter='100000 AU' mass='2e30 kg'/>
  <planet name='Earth' lifeFriendly='1'>
    <dimensions diameter='12745 km' mass='5.97e24 kg'/>
    <description>The planet were we live</description>
  </planet>
  <planet name='Mars' lifeFriendly='0'>
    <dimensions diameter='6805 km' mass='6.42e23 kg'/>
  </planet>
  <planet name='Jupiter'>
    <description>Biggest planet in the solar system</description>
  </planet>
</planetSystem>

You'll write:
class Description < XMLDef
end

class Dimensions < XMLDef
  xml_attribute :diameter, "diameter"
  xml_attribute :mass, "mass"
end

class Planet < XMLDef
  xml_attribute :name, "name"
  xml_attribute :life_friendly, "lifeFriendly"
  xml_object :dimensions, "dimensions", Dimensions
  xml_object :description, "description", Description
end

class PlanetSystem < XMLDef
  xml_attribute :name, "name"
  xml_array :planets, "planet", Planet
  xml_object :dimensions, "dimensions", Dimensions
end

I don't think you could write anything more obvious. The annotations make the code pretty straightforward to understand and maintain. I was coding a lot of test-cases for my XML Schema reader. The mapper rendered them completely useless.

This is how you'd read the XML above:

x = PlanetSystem.load_xml(File.open('planets.xml'))

puts x.name
puts "Diameter: " + x.dimensions.diameter                     
puts "Mass: " + x.dimensions.mass

puts ""

p = x.planets[0]
puts p.name + ': ' + p.description.text
puts "Life friendly: " + p.life_friendly
puts "Diameter: " + p.dimensions.diameter
puts "Mass: " + p.dimensions.mass

That's not hard to extend the class to save data back to XML or create a new one from scratch.

Today, after spending a few hours coding the class above, I found ROXML, which follows the same idea as mine. Some of the similarities are interesting, by the way. ROXML also has the methods xml_attribute and xml_object. And an example included in the source code references the Pickaxe book, which I did in the test-cases for XMLDef. I guess the author also had the book over the table when thinking out an example.