Wednesday, July 18, 2007

Mocha

So my previous post talked about overriding delegation. Then I remembered, Mocha. An honest to goodness stubbing/mocking library. Rather obviously, when testing units, I really don't want to fiddle around with the interaction of objects in each and every instance.

So instead of my convoluted example of conditionally_overriding_delegation, I can do the following (but hey you may have already known this):


class AbstractUnit
extend Forwardable
def_delegate :foo, :name

def has_name?
!(name.nil? || name.blank?)
end
end

class AbstractUnitTest < Test::Unit::TestCase
def setup
@object = AbstractUnit.new
end

# Perhaps a reasonable test for delegation?
def test_should_delegate_name_to_foo
assert_equal @object.name, @object.foo.name
end

def test_has_name_should_be_false_if_name_is_empty
@object.stubs(:name).returns('')
assert ! @object.has_name?
end

def test_has_name_should_be_false_if_name_is_nil
@object.stubs(:name).returns(nil)
assert ! @object.has_name?
end

def test_has_name_should_be_false_if_name_is_blank
@object.stubs(:name).returns('hello world')
assert @object.has_name?
end

end


Even in this case, Mocha is rather useful, I don't have to worry about testing the coupling that occurs due to delegation, and can test the objects responsibilities. Of course, now its time to look more into Mocha's documentation. It looks like there is some rather amazing stuff that can be done.

Monday, July 16, 2007

assert_interface

I've been reading quite a bit on programming these days. And one of the things I'm liking more and more is the concept of programming for the interface.
Now I use Ruby and not Java or C# or other languages, so the idea of the interface is a bit different. I can implement them, kind of, via a module.
However, I want to make sure that my objects quack appropriately. Enter assert_interface(object, module). The object is assumed to be an
instance of a class. The module is assumed to be "well-formed" for interfaces

module WellFormedInterface
def self.included(base)
base.send(:include, InstanceMethods)
base.extend(ClassMethods)
end

module ClassMethods
end
module InstanceMethods
end
end

class AbstractUnit
include WellFormedInterface
end




class Test::Unit::TestCase
# Given an object (i.e. Foo.new), and a module (i.e. SomethingCool)
# assert that all methods explicitly defined in the module
# are present in the object.
#
# This isn't the end all test, as you are still responsible for
# testing the implementation of the interface
def assert_interface(object, mod)
methods = {object => [], object.class => []}

mod::InstanceMethods.instance_methods(false).each do |method|
methods[object] << method unless object.respond_to?(method)
end

mod::ClassMethods.instance_methods(false).each do |method|
methods[object.class] << method unless object.class.respond_to?(method)
end

msg = []
methods.each_pair do |key, value|
msg << "Expected #{key} to respond to the following :#{value.join(' :')}" unless value.empty?
end

if msg.empty?
assert true, 'Hey, it all worked'
else
flunk msg.join('. ')
end
end
end

Thursday, July 12, 2007

Overriding def_delegator

In the project I work on, I make heavy use of the def_delegator method (yeah composition). One of the intrinsic problems of the delegation, is that my tests can get rather convoluted (and tightly coupled to implementation). I'm sure I could look into mock methods, but I needed an immediate solution.

Enter the ConditionallyOverrideDelegation module. This is a rails specific solution as it makes use of alias_method_chain.


module ConditionallyOverrideDelegation
def self.included(base)
base.extend(Macro)
end
module Macro

# class Foo
# extend Forwardable
# def_delegator :bar, :name
# conditionally_override_delegation :name
# end
# class Bar
# def name
# 'Bar'
# end
# end
#
# @foo = Foo.new
# @foo.bar.name == @foo.name
# => true
#
# @foo.name_with_delegation_override = @foo.bar.name * 2
# @foo.bar.name == @foo.name
# => false
#
# Take a look at the tests to see how this truly behaves
#
# Really you probably only want to use this method in test
#
def conditionally_override_delegation(*targets)
targets.flatten.each do |target|
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
src = <<-EOV
def #{aliased_target}_with_delegation_override#{punctuation}(*args)
if @#{aliased_target}_with_delegation_override.nil?
#{aliased_target}_without_delegation_override#{punctuation}(*args)
else
@#{aliased_target}_with_delegation_override
end
end
EOV
module_eval src, __FILE__, __LINE__
alias_method_chain target, :delegation_override
attr_writer "#{aliased_target}_with_delegation_override"
end
end

end
end




And the obligatory tests


require File.dirname(__FILE__) + '/../test_helper'
require 'conditionally_override_delegation'
class ConditionallyOverrideDelegationTest < Test::Unit::TestCase
class AbstractUnit
extend Forwardable
include ConditionallyOverrideDelegation

def_delegators :related_object, :to_s, :string=, :string, :string?

conditionally_override_delegation :to_s, :string

def related_object
@related_object ||= RelatedObject.new
end
end

class RelatedObject
def to_s
'Hello World'
end
attr_accessor :string
def string?
!!string
end
end

def setup
@unit = AbstractUnit.new
end

def test_delegation_is_working
assert_equal @unit.related_object.to_s, @unit.to_s
end

def test_overridding_delegation_is_working
@unit.to_s_with_delegation_override = 'Turkey Sandwich'
assert_equal 'Turkey Sandwich', @unit.to_s
end

def test_overridding_delegation_is_working_when_override_is_false
@unit.to_s_with_delegation_override = false
assert_equal false, @unit.to_s
end

def test_overridding_delegation_is_working_when_override_is_nil
@unit.to_s_with_delegation_override = nil
assert_equal @unit.related_object.to_s, @unit.to_s
end

def test_overridding_delegation_is_working_with_punctuation
@unit.related_object.string = 'Troy Mclure'
assert_equal 'Troy Mclure', @unit.string

@unit.string_with_delegation_override = 'Pancake'
assert_equal 'Pancake', @unit.string
end
end

Saturday, July 07, 2007

Of Childhood Discoveries

Recently I've been running a Star Wars Saga edition campaign for my children. They really love it (who doesn't want to be a Jedi or a space-faring princess). The Star Wars Saga rules are very slick and take a lot of the obnoxious complexities out of the d20 system. This has sparked my interest in running a sci-fi campaign. And as such, I've been trolling (in the fishing manner not the forum-posting manner) the inter-tubes for inspiration. And that is when I stumbled onto Star Frontiers.

Twenty one years ago, I played my first role-playing game. It was Star Frontiers, published by the now defunct TSR. During the first session, I played a Dralasite that made use of tangler grenades. It was a blast, and to this day, Star Frontiers holds a special spot in my heart. Apparently, there are many others that also remember the good ole' days, and are keeping the torch burning.

Its not like I've forgotten about the system, its just that its old and not the cool new system with all the bells and whistles. Its just a well written game, with an excellent underlying system that doesn't have quite the same internal harmony of a more modern gaming system. But oddly enough, I find myself intrigued and interested in running a game of it. Why? Partly because of nostalgia, and partly because the rules seem to focus more on playing the game and not tweaking yourself out for the next level (an intrinsic problem in the current manifestation of D&D).

All told, I'll probably run a Star Wars Saga edition game (its got Jedi), but maybe I won't use the Star Wars universe.