Randy said:
Florian said:
I've coded up test-extract which lets you embed some of your
test-cases cloaked as sample code into your RDoc strings.
It looks like this in practice:
# Creates a Regexp which matches a literal string. In this
# string any special regular expression meta-characters will
# be escaped automatically.
#
# # This creates a Regexp which will match 3 "foo"s.
# re = Regexp::English.literal("foo" * 3)
# re.match("foofoofoo")[0] # => "foofoofoo"
def literal(text); Node::Literal.new(text); end
(Sample from Regexp::English)
I very much like the idea of being able to test examples embedded in the
source file's documentation. A couple of suggestions:
1. Is there some way to allow set-up code that is not part of the
viewable documentation? This would make for clearer examples. Eg. the
above might be changed to something like:
#--
# Init:
# foo = Foo.new(blah, blah);
#++
#
# An example of using Foo.bar()
#
# foo.bar(blek)
def bar(); ...
Well, that's not real pretty, but... the idea is that the example is
more focused for documentation purposes, and all the setup-code is
hidden away.
This sounds like a quite good idea -- currently the setup needs to be
done in the example -- this is okay for most cases, but you don't want
to set up a new instance in every piece of sample code for Classes that
need other resources to be instantiated.
If support for this were to be added to RDoc it would be nice if there
were some kind of marker like "[Setup code hidden]" that could be
clicked to blend in the rest of the code. I think I've seen something
similar used in an online Ruby book already.
Can you think of a way to do this that would not need changes of the
RDoc Ruby code parser? I'll be willing to add support to test-extract,
but I think it is useless when the syntax doesn't work with RDoc. (If we
can't find a way to hide the setup code from RDoc we could maybe make
the syntax self-explanatory so that it can be understood even without
knowing about it.)
2. In addition to showing how something works, it is sometimes
instructive to show what doesn't work. Being able to create examples
that are expected to fail would be useful.
This is possible already: (Sorry for the long sample.)
# Causes a pattern which would otherwise match
# greedily to match non-greedily.
#
# This can only be applied to #count and #optional
# based constructs.
#
# re_greedy = Regexp::English.new do
# literal("<") + something + literal(">")
# end
# re_non_greedy = Regexp::English.new do
# literal("<") + something.minimal + literal(">")
# end
# str = "<html><body>"
# re_greedy.match(str)[0] # => "<html><body>"
# re_non_greedy.match(str)[0] # => "<html>"
#
# This method will do nothing when applied to a Node
# which is already minimal:
#
# re_minimal = Regexp::English.something.minimal
# re_minimal.minimal == re_minimal # => true
#
# However, it will raise a NameError Exception when
# you try to call it on a Regexp Node that can't be
# made non-greedy:
#
# re = Regexp::English.literal("foo")
# re.minimal # raises NameError
def minimal
if self.is_a?(Repeat)
if self.minimal_flag
return self
else
result = self.clone
result.minimal_flag = true
return result
end
else
raise(NameError, "Can't apply minimal")
end
end
You can use the following notations:
# obj.method # raises Exception
# obj.method # raise Exception
# obj.method # ~> Exception
I think the first one is the most clear one.
Another notation that I might add in the future is this one:
# puts "foo" # outputs "foo"
or maybe:
# puts "foo" # outputs foo
I'm not sure about it yet and it will need a bit more work than the
other specification methods, but I think it can be quite useful.
Thanks a lot for giving me feedback on this. It would be great if sample
code as test cases would be commonly adapted.
Regards,
Florian Gross