#!/usr/bin/env ruby require 'qed' require 'optparse' require 'shellwords' require 'tilt' module QED # = QED Commandline Tool # class Command # Configuration directory. CONFDIR = "{.,}config/qed" # Default location of demonstrations if no # specific files or locations given. This # is use in Dir.glob. DEFAULT_DEMOS_LOCATION = '{qed}' # Initialize and execute. def self.execute new.execute end # Ouput format. attr :format # Make sure format is a symbol. def format=(type) @format = type.to_sym end # Trace execution? attr :trace # Options defined by selected profile. attr :profile # Command-line options. attr :options # Files to be run. attr :files # def files=(globs) @files = [globs].flatten end # attr_accessor :loadpath # attr_accessor :requires # attr_accessor :extension # TODO: Should extension and profile have a common reference? def initialize @format = :dotprogress @extension = :default @profile = :default @requires = [] @loadpath = [] @files = [] @options = {} end # Instance of OptionParser def opts @opts ||= OptionParser.new do |opt| opt.separator("Custom Profiles:") unless profiles.empty? profiles.each do |name, value| o = "--#{name}" opt.on(o, "#{name} custom profile") do @profile = name end end opt.separator("Report Formats (pick one):") opt.on('--dotprogress', '-d', "use dot-progress reporter [default]") do @options[:format] = :dotprogress end opt.on('--verbatim', '-v', "use verbatim reporter") do @options[:format] = :verbatim end opt.on('--bullet', '-b', "use bullet-point reporter") do @options[:format] = :bullet end opt.on('--html', '-h', "use underlying HTML reporter") do @options[:format] = :html end opt.on('--format', '-f FORMAT', "use custom reporter") do |format| @options[:format] = format end #opt.on('--script', "psuedo-reporter") do # @options[:format] = :script # psuedo-reporter #end opt.separator("Control Options:") opt.on('--ext', '-e [NAME]', "runtime extension [default]") do |name| @options[:extension] = name end opt.on('--loadpath', "-I PATH", "add paths to $LOAD_PATH") do |arg| @options[:loadpath] ||= [] @options[:loadpath].concat(arg.split(/[:;]/).map{ |dir| File.expand_path(dir) }) end opt.on('--require', "-r", "require library") do |arg| @options[:requires] ||= [] @options[:requires].concat(arg.split(/[:;]/)) #.map{ |dir| File.expand_path(dir) }) end opt.on('--trace', '-t', "show full backtraces for exceptions") do @options[:trace] = true end opt.on('--debug', "exit immediately upon raised exception") do $VERBOSE = true # wish this were called $WARN $DEBUG = true end opt.separator("Optional Commands:") opt.on_tail('--version', "display version") do puts "QED #{VERSION}" exit end opt.on_tail('--copyright', "display copyrights") do puts "Copyright (c) 2008, 2009 Thomas Sawyer, GPL License" exit end opt.on_tail('--help', '-h', "display this help message") do puts opt exit end end end # def demos files = self.files types = Tilt.mappings.keys if files.empty? files << DEFAULT_DEMOS_LOCATION end files = files.map do |pattern| Dir[pattern] end.flatten.uniq files = files.map do |file| if File.directory?(file) Dir[File.join(file,'**','*.{' + types.join(',') + '}')] else file end end files = files.flatten.uniq.sort #files = files.select do |file| # %w{.yml .yaml .rb}.include?(File.extname(file)) #end files end # Session instance. def session @session ||= Session.new(demos, :format=>format, :trace=>trace) end # Parse command-line options along with profile options. def parse @files = [] argv = ARGV.dup opts.parse!(argv) @files.concat(argv) #if profile if args = profiles[profile] argv = Shellwords.shellwords(args) opts.parse!(argv) @files.concat(argv) end #end options.each do |k,v| __send__("#{k}=", v) end end # Run demonstrations. def execute parse abort "No documents." if demos.empty? prepare_loadpath require_libraries require_profile session.run end # Profile configurations. def profiles @profiles ||= ( file = Dir["#{CONFDIR}/profile{,s}.{yml,yaml}"].first file ? YAML.load(File.new(file)) : {} ) end # Add to load path (from -I option). def prepare_loadpath loadpath.each{ |dir| $LOAD_PATH.unshift(dir) } end # Require libraries (from -r option). def require_libraries requires.each{ |file| require(file) } end # Require requirement file (from -e option). def require_profile return unless root # common environment, always loaded if present. #if file = Dir["#{root}/#{CONFDIR}/default.rb"].first # require(file) #end #env = env() || 'default' if file = Dir["#{root}/#{CONFDIR}/#{extension}.rb"].first require(file) end end # def root QED.root end end # Is there no perfect way to find root directory of a project? def self.root(path=nil) path ||= Dir.pwd path = File.dirname(path) unless File.directory?(path) until path == File.dirname(path) mark = Dir[File.join(path, 'README*')].first return path if mark path = File.dirname(path) end nil end end