# TITLE: # # Command # # COPYRIGHT: # # Copyright (c) 2005 Thomas Sawyer # # LICENSE: # # Ruby License # # This module is free software. You may use, modify, and/or # redistribute this software under the same terms as Ruby. # # This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. # # AUTHORS: # - Thomas Sawyer # - Tyler Rick # # TODOs: # - Add help/documentation features. # - Problem wiht exit -1 when testing. See IMPORTANT!!! remark below. require 'shellwords' require 'facets/command_options' #require 'facets/annotations' # for help ? module Console # Base command class. # # class MyCommand < Command # # class JumpOptions < Command::Options # attr_accessor :height # end # # help :jump, "this is the jump command" # # opts :jump, JumpOptions # # def jump # puts "jump #{options.height} feet!" # end # end # # A little more conveniently: # # class MyCommand < Command # # help :jump, "this is the jump command" # # opts :jump do # attr_accessor :height # end # # def jump # puts "jump #{options.height} feet!" # end # end # class Command # Command Syntax DSL module Syntax # Starts the command execution. def execute( *args ) cmd = new() #cmd.instance_variable_set("@global_options",global_options) cmd.execute( *args ) end alias_method :start, :execute # Change the option mode. def global_option( *names ) names.each{ |name| global_options << name.to_sym } end alias_method :global_options, :global_option # Options. def options(name, klass=nil, &block) raise ArgumentError if klass && block if block command_options[name.to_sym] = Class.new(CommandOptions, &block) else command_options[name.to_sym] = klass end end alias_method :opts, :options alias_method :opt, :options # def command_options @_command_options ||= {} end # def global_options @_global_options ||= [] end end extend Syntax #def initialize #(global_options=nil) # #@global_options = global_options || [] #end # def execute(line) argv = line g_opts = CommandOptions.new(self) g_keys = self.class.global_options # Deal with global options. if g_keys && ! g_keys.empty? argv = g_opts.parse(argv, :only => g_keys) end # Sole main command or has subcommands? if respond_to?(:main) argv = g_opts.parse(argv, :pass => true) cmd = :main else argv = g_opts.parse(argv, :stop => true) cmd = argv.find{ |s| s !~ /^-/ } argv.delete_at(argv.index(cmd)) if cmd cmd = :default unless cmd cmd = cmd.to_sym end keys = self.class.command_options if keys.key?(cmd) opts = keys[cmd].new argv = opts.parse(argv) end argv = g_opts.parse_missing(argv) call(cmd, argv, opts) end # def call(subcmd, argv, opts) @options = opts # should we use this it fill in instance vars? # This is a little tricky. The method has to be defined by a subclass. if self.respond_to?(subcmd) and not Console::Command.public_instance_methods.include?(subcmd.to_s) puts "# call: #{subcmd}(*#{argv.inspect})" if $debug __send__(subcmd, *argv) else begin puts "# call: method_missing(#{subcmd.inspect}, #{argv.inspect})" if $debug method_missing(subcmd.to_sym, *argv) rescue NoMethodError => e #if self.private_methods.include?( "no_command_error" ) # no_command_error( *args ) #else $stderr << "Unrecognized subcommand -- #{subcmd}\n" exit -1 #end end end end #def global_options # self.class.global_options #end def option_missing(opt, arg=nil) raise InvalidOptionError.new(opt) end end =begin # We include a module here so you can define your own help # command and call #super to utilize this one. module Help def help opts = help_options s = "" s << "#{File.basename($0)}\n\n" unless opts.empty? s << "OPTIONS\n" s << help_options s << "\n" end s << "COMMANDS\n" s << help_commands puts s end private def help_commands help = self.class.help bufs = help.keys.collect{ |a| a.to_s.size }.max + 3 lines = [] help.each { |cmd, str| cmd = cmd.to_s if cmd !~ /^_/ lines << " " + cmd + (" " * (bufs - cmd.size)) + str end } lines.join("\n") end def help_options help = self.class.help bufs = help.keys.collect{ |a| a.to_s.size }.max + 3 lines = [] help.each { |cmd, str| cmd = cmd.to_s if cmd =~ /^_/ lines << " " + cmd.gsub(/_/,'-') + (" " * (bufs - cmd.size)) + str end } lines.join("\n") end module ClassMethods def help( str=nil ) return (@help ||= {}) unless str @current_help = str end def method_added( meth ) if @current_help @help ||= {} @help[meth] = @current_help @current_help = nil end end end end include Help extend Help::ClassMethods =end end