Tuesday, September 12, 2006

Ruby Stub Variations: OpenStruct

Ruby Stub Variations: Introduction
Ruby Stub Variations: OpenStruct
Ruby Stub Variations: TestStub
Ruby Stub Variations: Struct
Ruby Stub Variations: Stubba


Shortly after I begin to program full time using Ruby I was exposed to OpenStruct. I was quite pleased with the simplicty OpenStruct offered. OpenStruct allows you to create objects with their attributes initialized. For example:
require 'ostruct'
record = OpenStruct.new(:name=>'jay')
puts record.name # -> jay
OpenStruct provided value by allowing me to easily create stubs in one line of code. The code below represents a more robust version of the example test. OpenStruct helps reduce the fragility of the test by removing the dependency on the Select class.
def test_values_are_appened_to_insert_statement
statement = Insert.into[:table_name].values do
OpenStruct.new(:to_sql=>'select column1, column2 from table2')
end
assert_equal "insert into table_name select column1, column2 from table2", statement.to_sql
end
Despite the value of OpenStruct, it also has some limitations. The first issue we encountered was that it did not respond as expected when the OpenStruct instance was frozen.

Another issue with OpenStruct is that it does not respond as expected if you specify a value for an already defined method. My team noticed this behavior while using an OpenStruct instance in place of a ActiveRecord::Base subclass instance. The test required the OpenStruct instance to respond to id; however, the number that was returned did not match the value passed in the constructor. The following failing test should demonstrate the described issue.
require 'test/unit'
require 'ostruct'

class OpenStructTest < Test::Unit::TestCase
def test_id
assert_equal 2, OpenStruct.new(:id=>2).id
end
end
This issue stems from the current implementation of OpenStruct. OpenStruct stores it's attribute values in a Hash. When you attempt to access an attribute the call is actually delegated to method_missing. OpenStruct's implementation of method_missing returns the value from the Hash if it finds a matching key, otherwise nil. Unfortunately, method_missing will never be called if a method, such as id, is previously defined.
Post a Comment