Resummit my solution to the Madlabs Quiz and the unit tests.

Best Regards,
Steve Li.

--------------------------------------------------------------------------------------------------------------------
Madlibs.rb
class Story
  attr_accessor :placeholders
 
  def initialize(base)
    @placeholders = []
 
    story_parts = []
    match = Placeholder.getPattern().match(base)
    reuseMap = {}
    while(match != nil)
      story_parts << match.pre_match
      placeholderString = match[1]
      placeholder = Placeholder.new(placeholderString, story_parts.size)
 
      # if name is reused
      if reuseMap[placeholder.name] == nil
        @placeholders << placeholder

        # if the name is reusable, add it to the reuse table
        if placeholder.reusable()
          reuseMap[placeholder.name] = placeholder
        end
 
        # replace the placeholder with the system generated position 
string
        story_parts << get_position_string(story_parts.size.to_s)
      else
        # for reuse placeholder, 
        # replace the placeholder with the system generated position 
string for the referenced placeholder
        story_parts << 
get_position_string(reuseMap[placeholder.name].position.to_s)
      end
 
      remaind = match.post_match
      match = Placeholder.getPattern().match(match.post_match)
      if (match == nil)
        story_parts << remaind
      end
    end
 
    @base = story_parts.join("")
  end

  def to_s
    result = @base
    @placeholders.each do |placeholder|
 result.gsub!(Regexp.new(get_position_string(placeholder.position.to_s)), 
placeholder.value)
    end
    return result
  end
 
  def get_position_string(position)
    "%%" + position.to_s + "%%"
  end
end

class Placeholder
  attr_accessor :name, :display_name, :position, :value
 
  def initialize(placeholderString, position)
    @value = ""
    @position = position
 
    if placeholderString.include?(":")
      @name = placeholderString.split(":")[0]
      @display_name = placeholderString.split(":")[1]
    else
      @name = placeholderString
      @display_name = placeholderString
    end
  end
 
  def getTemplate()
    Regexp.new(
"\\(\\(\\s*(#{name}|#{name}\\s*:\\s*#{display_name})\\s*\\)\\)")
  end
 
  def Placeholder.getPattern()
    /\(\(([^)]*)\)\)/
  end
 
  def getValueQuestion()
    "Give me #{display_name}: "
  end
 
  def reusable()
    name != display_name
  end
end

if $0 == __FILE__
  # read story from standard input
  story_string = ""
  ARGF.each_line do |line|
    story_string += line
  end
 
  # create story 
  story = Story.new(story_string)
 
  # request uesr to enter the corresponding value for each placeholder
  print "There are #{story.placeholders.size} placeholders.\n"
  story.placeholders.each do |placeholder|
    print placeholder.getValueQuestion()
    placeholder.value = gets().chop()
  end
 
  # display the story
  print story.to_s, "\n"
end

--------------------------------------------------------------------------------------------------------------------
UnitTest;
MadlibsTest.rb
require 'runit/testcase'
require 'Madlibs'

class TestMadlibs < RUNIT::TestCase
  def testStoryTemplate()
    # parse simple story
    # e.g. "Our favorite language is ((a gemstone))."
    template = "Our favorite language is ((a gemstone))."
    story = Story.new(template)
 
    # should return a Story with  a symbol name='a gemstome' and alias=nil
    assert_equals(1, story.placeholders.size)
    assert_not_nil(story.placeholders[0])
    assert_equals("a gemstone", story.placeholders[0].display_name)
  end
 
  def testStoryTemplateWithAlias()
    # parse story with name alias
    # e.g. "Our favorite language is ((gem:a gemstone)). We think ((gem)) 
is
    #         better than ((a gemstone))."
    template = "Our favorite language is ((gem:a gemstone)). "
    template += "We think ((gem)) is better then ((a gemstone))."
    story = Story.new(template)
 
    # should return a Story with 2 symbole
    # Symbol 1: name = 'gem' alias='a gemstome'
    # Symbol 2: name = 'a gemstome'
    assert_equals(2, story.placeholders.size)
    assert_not_nil(story.placeholders[0])
    assert_equals("gem", story.placeholders[0].name)
    assert_equals("a gemstone", story.placeholders[0].display_name)
    assert_not_nil(story.placeholders[1])
    assert_equals("a gemstone", story.placeholders[1].name)
    assert_equals("a gemstone", story.placeholders[1].display_name)
  end
 
  def testStoryGeneration()
    # give:    "Our favorite language is ((a gemstone))."
    # input:   gemstone = Ruby
    # result:  Our favorite language is Ruby."
    String template = "Our favorite language is ((a gemstone))."
    story = Story.new(template)
    story.placeholders[0].value = "Ruby"
    assert_equals("Our favorite language is Ruby.", story.to_s())
  end
 
  def testStoryGenerationWithAlias()
    # given:    "Our favorite language is ((gem:a gemstone)). 
    #                  We think ((gem)) is better than ((a gemstone))."    
 
    # input:    a gemstone = Ruby, a genstone = Emerald
    # given:    "Our favorite language is Ruby. 
    #                  We think Ruby is better than Emerald." 
    template = "Our favorite language is ((gem:a gemstone)). "
    template += "We think ((gem)) is better then ((a gemstone))."
    story = Story.new(template)
    story.placeholders[0].value = "Ruby"
    story.placeholders[1].value = "Emerald"
    assert_equals("Our favorite language is Ruby. We think Ruby is better 
then Emerald.", story.to_s())
  end
end

if $0 == __FILE__
  require 'runit/cui/testrunner'
  RUNIT::CUI::TestRunner.run(TestMadlibs.suite)
end