[SOLUTION] Madlibs (#28)

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

You might want to take a look at the standard "test/unit" library. I believe "runit" might be the old system of Ruby unit testing. Obviously, it works fine, but you might find "test/unit" a little more refined. Just FYI.

James Edward Gray II

···

On Apr 18, 2005, at 1:22 AM, Wai-Ming S. Li wrote:

UnitTest;
MadlibsTest.rb
require 'runit/testcase'