# # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com) # # This file is part of Ronin. # # Ronin is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ronin 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. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ronin. If not, see . # require 'ronin/ui/output' require 'ronin/ui/output/terminal' require 'ronin/support/inflector' require 'thor' require 'thor/group' module Ronin module UI module CLI # # The {Command} class inherits `Thor::Group` to provide a base-class # for defining commands for the {CLI}. # # # Extending # # To create a new command one can inherit the {Command} class. # The new command can define multiple `class_options` and `arguments` # which `Thor::Group` will use to parse command-line arguments. # # require 'ronin/ui/cli/command' # # module Ronin # module UI # module CLI # module Commands # class MyCommand < Command # # desc 'My command' # # # command options # class_option :stuff, :type => :boolean # class_option :syntax, :type => :string # class_option :includes, :type => :array # # # command arguments # argument :path # # # # # Executes the command. # # # def execute # print_info "Stuff enabled" if options.stuff? # # if options[:syntax] # print_info "Using syntax #{options[:syntax]}" # end # # if options[:includes] # print_info "Including:" # print_array options[:includes] # end # end # # end # end # end # end # end # # # Running # # To run the command from Ruby, one can call the {run} class method # with the options and arguments to run the command with: # # MyCommand.run( # {:stuff => true, :syntax => 'bla', :includes => ['other']}, # ['some/file.txt'] # ) # # To run the command from Ruby, with raw command-line options, one # can call the `start` class method: # # MyCommand.start([ # '--stuff', 'true', '--syntax', 'bla', '--includes', 'other', # 'some/file.txt' # ]) # # Note: If `MyCommand.start` is not given any arguments, it will use # `ARGV` instead. # # To ensure that your command is accessible to the `ronin` command, # make sure that the ruby file the command is defined within is in # the `ronin/ui/cli/commands` directory of a Ronin library. # If the command class is named 'MyCommand' it's ruby file must also # be named 'my_command.rb'. # # To run the command using the `ronin` command, simply specify it's # underscored name: # # ronin my_command some/file.txt --stuff --syntax bla \ # --includes one two # class Command < Thor::Group include Thor::Actions include Output::Helpers class_option :verbose, :type => :boolean, :default => false, :aliases => '-v' class_option :quiet, :type => :boolean, :default => false, :aliases => '-q' class_option :silent, :type => :boolean, :default => false, :aliases => '-Q' class_option :color, :type => :boolean, :default => true # # Sets the namespace of a new {Command} class. # # @param [Class] super_class # The new {Command} class. # # @api private # def self.inherited(super_class) super_class.namespace(super_class.command_name) end # # Returns the name of the command. # # @api semipublic # def self.command_name Support::Inflector.underscore(self.name.split('::').last) end # # Runs the command. # # @param [Hash{String,Symbol => Object}] options # Option values for the command. # # @param [Array] arguments # Additional arguments for the command. # # @return [Command] # The executed command. # # @since 1.0.0 # # @api public # def self.run(options={},arguments=[]) command = self.new(arguments,options) command.invoke_all() return command end # # Creates a new Command object. # # @param [Array] arguments # Command-line arguments. # # @param [Array] opts # Additional command-line options. # # @param [Hash] config # Additional configuration. # # @api semipublic # def initialize(arguments=[],opts={},config={}) @indent = 0 super(arguments,opts,config) setup end # # Default method to call after the options have been parsed. # # @api semipublic # def execute end protected # # The banner for the command. # # @return [String] # The banner string. # # @since 1.0.0 # # @api private # def self.banner "ronin #{self_task.formatted_usage(self,false,true)}" end # # Default method to call before {#execute}. # # @since 1.1.0 # # @api semipublic # def setup Output.verbose! if self.options.verbose? Output.quiet! if self.options.quiet? Output.silent! if self.options.silent? Output.handler = if self.options.color? Output::Terminal::Color else Output::Terminal::Raw end end # # Increases the indentation out output temporarily. # # @param [Integer] n # The number of spaces to increase the indentation by. # # @yield [] # The block will be called after the indentation has been # increased. After the block has returned, the indentation will # be returned to normal. # # @return [nil] # # @api semipublic # def indent(n=2) @indent += n yield @indent -= n return nil end # # Print the given messages with indentation. # # @param [Array] messages # The messages to print, one per-line. # # @api semipublic # def puts(*messages) super(*(messages.map { |mesg| (' ' * @indent) + mesg.to_s })) end # # Prints a given title. # # @param [String] title # The title to print. # # @api semipublic # def print_title(title) puts "[ #{title} ]\n" end # # Prints a section with a title. # # @yield [] # The block will be called after the title has been printed # and indentation increased. # # @since 1.0.0 # # @api semipublic # def print_section(title,&block) print_title(title) indent(&block) end # # Prints a given Array. # # @param [Array] array # The Array to print. # # @param [Hash] options # Additional options. # # @option options [String] :title # The optional title to print before the contents of the Array. # # @return [nil] # # @api semipublic # def print_array(array,options={}) print_title(options[:title]) if options[:title] indent do array.each { |value| puts value } end puts if options[:title] return nil end # # Prints a given Hash. # # @param [Hash] hash # The Hash to print. # # @param [Hash] options # Additional options. # # @option options [String] :title # The optional title to print before the contents of the Hash. # # @return [nil] # # @api semipublic # def print_hash(hash,options={}) align = hash.keys.map { |name| name.to_s.length }.max print_title(options[:title]) if options[:title] indent do hash.each do |name,value| name = "#{name}:".ljust(align) puts "#{name}\t#{value}" end end puts if options[:title] return nil end # # Prints an exception and a shortened backtrace. # # @param [Exception] exception # The exception to print. # # @since 1.0.0 # # @api semipublic # def print_exception(exception) print_error exception.message (0..5).each do |i| print_error ' ' + exception.backtrace[i] end end end end end end