lib/yard/cli/doctest.rb
module YARD module CLI class Doctest < Command def description 'Doctests from @example tags' end # # Runs the command line, parsing arguments # and generating tests. # # @param [Array<String>] args Switches are passed to minitest, # everything else is treated as the list of directories/files or glob # def run(*args) files = args.select { |arg| arg !~ /^-/ } files = parse_files(files) examples = parse_examples(files) add_pwd_to_path generate_tests(examples) end private def parse_files(globs) globs = %w(app lib) if globs.empty? files = globs.map do |glob| if glob !~ /.rb$/ glob = "#{glob}/**/*.rb" end Dir[glob] end files.flatten end def parse_examples(files) YARD.parse(files, excluded_files) registry = Registry.load_all registry.all.map { |object| object.tags(:example) }.flatten end def excluded_files excluded = [] args = YARD::Config.with_yardopts { YARD::Config.arguments.dup } args.each_with_index do |arg, i| next unless arg == '--exclude' excluded << args[i + 1] end excluded end def generate_tests(examples) examples.each do |example| text = example.text text = text.gsub('# =>', '#=>') text = text.gsub('#=>', "\n#=>") lines = text.split("\n").map(&:strip).reject(&:empty?) asserts = [].tap do |arr| until lines.empty? actual = lines.take_while { |l| l !~ /^#=>/ } expected = lines[actual.size] || '' lines.slice! 0..actual.size arr << { expected: expected.sub('#=>', '').strip, actual: actual.join("\n"), } end end spec = YARD::Doctest::Example.new(example.name) spec.definition = example.object.path spec.filepath = "#{Dir.pwd}/#{example.object.files.first.join(':')}" spec.asserts = asserts spec.generate end end def add_pwd_to_path $LOAD_PATH.unshift(Dir.pwd) unless $LOAD_PATH.include?(Dir.pwd) end end # Doctest end # CLI end # YARD