Skip to content

Instantly share code, notes, and snippets.

@thinkerbot
Created July 26, 2010 23:33
Show Gist options
  • Select an option

  • Save thinkerbot/491446 to your computer and use it in GitHub Desktop.

Select an option

Save thinkerbot/491446 to your computer and use it in GitHub Desktop.
# :startdoc::generator
class Doctest < Tap::Generator::Base
Utils = Lazydoc::Utils
Comment = Lazydoc::Comment
def manifest(m, *paths)
paths.each do |path|
lines = File.read(path).split("\n")
targets = scan(lines).collect do |comment|
code = code_lines(comment)
next if code.all? {|line| line.length == 0 }
code = outdent(code)
target = []
target << "def test_#{parse_test_name comment.subject}_doc"
target.concat indent(format(code))
target << "end"
indent(target).flatten.join("\n")
end
puts %Q{
class TestClass << Test::Unit::TestCase
#{targets.compact.join("\n\n")}
end
}
end
end
def scan(lines)
current = Comment.new
comments = [current]
lines.each do |line|
next if Utils.scan(line) {|fragment| current.push(fragment) }
unless current.empty?
current.subject = line
current = Comment.new
comments << current
end
end
comments
end
def code_lines(comment)
strip = true
comment.comment(" ", nil, true).collect! do |line|
if line =~ /\A(?:\s+).*\z/
strip = false
line
elsif strip
nil
else
strip = true
''
end
end.compact
end
def parse_test_name(str)
case str
when /\A\s*def\s+(\w+)/
$1
else
str.gsub(/\W/, '_')
end
end
def outdent(lines)
indent = lines.collect do |line|
line.empty? ? nil : (line =~ /\A(\s*)/ ? $1.length : 0)
end.compact.min
lines.collect do |line|
line.empty? ? line : line[indent, line.length - indent]
end
end
def indent(code_lines, indent=' ')
code_lines.collect do |line|
line.kind_of?(String) ? "#{indent}#{line}" : line
end
end
def format(code_lines)
result = []
while !code_lines.empty?
line = code_lines.shift
case line
when /\A\s*#\s*(.>)(.*)\z/
comparison = $1
actual = result.pop
result << "expected = #{$2}"
multiline = []
while code_lines[0] =~ /\A\s*#(.*)\z/
code_lines.shift
multiline << $1
end
result << outdent(multiline)
format_assertion(comparison, 'expected', actual) {|line| result << line }
when /\A(.*?)#\s*(.>)(.*)\z/
format_assertion($2, $3, $1) {|line| result << line }
else
result << line
end
end
result
end
def format_assertion(comparison, expected, actual)
actual.strip!
expected.strip!
case comparison
when "=>"
yield "assert_equal #{expected}, (#{actual})"
when "~>"
yield "assert_match #{expected}, (#{actual})"
when "!>"
error_class, message = expected.split(' ', 2)
yield "err = assert_raises(#{error_class}) { #{actual} }"
yield "assert_equal #{message}, err.message" if message
else
raise "unknown comparison: #{comparison.inspect}"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment