commandable 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rbenv-gemsets +2 -0
 - data/{README.markdown → README.md} +64 -64
 - data/Rakefile +2 -6
 - data/bin/commandable +1 -0
 - data/commandable.gemspec +1 -3
 - data/lib/commandable.rb +15 -2
 - data/lib/commandable/app_controller.rb +21 -39
 - data/lib/commandable/argument_parser.rb +45 -0
 - data/lib/commandable/coloring.rb +100 -0
 - data/lib/commandable/command_parser.rb +139 -0
 - data/lib/commandable/controller.rb +24 -0
 - data/lib/commandable/exceptions.rb +1 -1
 - data/lib/commandable/execution_controller.rb +122 -0
 - data/lib/commandable/help_text.rb +73 -0
 - data/lib/commandable/version.rb +4 -3
 - data/spec/commandable/app_controller_spec.rb +2 -30
 - data/spec/commandable/coloring_spec.rb +96 -0
 - data/spec/commandable/command_line_execution_spec.rb +7 -1
 - data/spec/commandable/execution_controller_spec.rb +17 -0
 - data/spec/commandable/help_generator_spec.rb +5 -66
 - data/spec/source_code_examples/parameter_class.rb +4 -0
 - data/spec/source_code_for_errors/private_methods_bad.rb +1 -1
 - data/spec/spec_helper.rb +1 -1
 - metadata +20 -38
 - data/features/ansicolor.feature +0 -11
 - data/features/copy_examples.feature +0 -13
 - data/features/download_widget.feature +0 -18
 - data/features/setup/env.rb +0 -5
 - data/features/step-definitions/step-definitions.rb +0 -15
 - data/lib/commandable/commandable.rb +0 -447
 - data/lib/monkey_patch/file_utils.rb +0 -11
 
| 
         @@ -0,0 +1,139 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Commandable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                # An array of methods that can be executed from the command line
         
     | 
| 
      
 8 
     | 
    
         
            +
                def commands
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @@commands.dup
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
                
         
     | 
| 
      
 12 
     | 
    
         
            +
                # A hash of instances created when calling instance methods
         
     | 
| 
      
 13 
     | 
    
         
            +
                # It's keyed using the class name: {"ClassName"=>#<ClassName:0x00000100b1f188>}
         
     | 
| 
      
 14 
     | 
    
         
            +
                def class_cache
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @@class_cache
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
                
         
     | 
| 
      
 18 
     | 
    
         
            +
                # Access the command array using the method name (symbol or string)
         
     | 
| 
      
 19 
     | 
    
         
            +
                def [](index)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  raise AccessorError unless index.is_a? String or index.is_a? Symbol
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @@commands[index.to_sym]
         
     | 
| 
      
 22 
     | 
    
         
            +
                end 
         
     | 
| 
      
 23 
     | 
    
         
            +
                
         
     | 
| 
      
 24 
     | 
    
         
            +
                # Clears all methods from the list of available commands
         
     | 
| 
      
 25 
     | 
    
         
            +
                # This is mostly useful for testing.
         
     | 
| 
      
 26 
     | 
    
         
            +
                def clear_commands
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @@command_options = nil
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @@commands = HELP_COMMAND.dup
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
                
         
     | 
| 
      
 31 
     | 
    
         
            +
                # Convenience method to iterate over the array of commands using the Commandable module
         
     | 
| 
      
 32 
     | 
    
         
            +
                def each(&block)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @@commands.each do |key, value|
         
     | 
| 
      
 34 
     | 
    
         
            +
                    yield key => value
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              private 
         
     | 
| 
      
 41 
     | 
    
         
            +
              
         
     | 
| 
      
 42 
     | 
    
         
            +
              # This is where the magic happens!
         
     | 
| 
      
 43 
     | 
    
         
            +
              # It lets you add a method to the list of command line methods
         
     | 
| 
      
 44 
     | 
    
         
            +
              def command(*cmd_parameters)
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                @@attribute = nil
         
     | 
| 
      
 47 
     | 
    
         
            +
                @@method_file = nil
         
     | 
| 
      
 48 
     | 
    
         
            +
                @@method_line = nil
         
     | 
| 
      
 49 
     | 
    
         
            +
                @@command_options = {}
         
     | 
| 
      
 50 
     | 
    
         
            +
                
         
     | 
| 
      
 51 
     | 
    
         
            +
                # Include Commandable in singleton classes so class level methods work
         
     | 
| 
      
 52 
     | 
    
         
            +
                include Commandable unless self.include? Commandable
         
     | 
| 
      
 53 
     | 
    
         
            +
                
         
     | 
| 
      
 54 
     | 
    
         
            +
                # parse command parameters
         
     | 
| 
      
 55 
     | 
    
         
            +
                while (param = cmd_parameters.shift)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  case param
         
     | 
| 
      
 57 
     | 
    
         
            +
                    when Symbol
         
     | 
| 
      
 58 
     | 
    
         
            +
                      if param == :xor
         
     | 
| 
      
 59 
     | 
    
         
            +
                        @@command_options.merge!(param=>:xor)
         
     | 
| 
      
 60 
     | 
    
         
            +
                      else
         
     | 
| 
      
 61 
     | 
    
         
            +
                        @@command_options.merge!(param=>true)
         
     | 
| 
      
 62 
     | 
    
         
            +
                      end
         
     | 
| 
      
 63 
     | 
    
         
            +
                    when Hash
         
     | 
| 
      
 64 
     | 
    
         
            +
                      @@command_options.merge!(param)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    when String
         
     | 
| 
      
 66 
     | 
    
         
            +
                      @@command_options.merge!(:description=>param)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
                @@command_options[:priority] ||= 0
         
     | 
| 
      
 70 
     | 
    
         
            +
                
         
     | 
| 
      
 71 
     | 
    
         
            +
                # only one default allowed
         
     | 
| 
      
 72 
     | 
    
         
            +
                raise ConfigurationError, "Only one default method is allowed."  if @@default_method and @@command_options[:default]
         
     | 
| 
      
 73 
     | 
    
         
            +
                
         
     | 
| 
      
 74 
     | 
    
         
            +
                set_trace_func proc { |event, file, line, id, binding, classname|
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  @@attribute = id if [:attr_accessor, :attr_writer].include?(id)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # Traps the line where the method is defined so we can look up 
         
     | 
| 
      
 79 
     | 
    
         
            +
                  # the method source code later if there are optional parameters
         
     | 
| 
      
 80 
     | 
    
         
            +
                  if event == "line" and !@@method_file
         
     | 
| 
      
 81 
     | 
    
         
            +
                    @@method_file = file
         
     | 
| 
      
 82 
     | 
    
         
            +
                    @@method_line = line
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # Raise an error if there is no method following a command definition
         
     | 
| 
      
 86 
     | 
    
         
            +
                  if event == "end"
         
     | 
| 
      
 87 
     | 
    
         
            +
                    set_trace_func(nil)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    raise SyntaxError, "A command was specified but no method follows"
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
                }
         
     | 
| 
      
 91 
     | 
    
         
            +
              end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
              # Add a method to the list of available command line methods
         
     | 
| 
      
 94 
     | 
    
         
            +
              def add_command(meth)
         
     | 
| 
      
 95 
     | 
    
         
            +
                @@commands.delete(:help)
         
     | 
| 
      
 96 
     | 
    
         
            +
                
         
     | 
| 
      
 97 
     | 
    
         
            +
                if @@attribute
         
     | 
| 
      
 98 
     | 
    
         
            +
                  argument_list = "value"
         
     | 
| 
      
 99 
     | 
    
         
            +
                  meth = meth.to_s.delete("=").to_sym if @@attribute == :attr_writer
         
     | 
| 
      
 100 
     | 
    
         
            +
                else
         
     | 
| 
      
 101 
     | 
    
         
            +
                  argument_list = parse_arguments(@@command_options[:parameters])
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
                @@command_options.merge!(:argument_list=>argument_list,:class => self.name)
         
     | 
| 
      
 104 
     | 
    
         
            +
                
         
     | 
| 
      
 105 
     | 
    
         
            +
                @@commands.merge!(meth => @@command_options)
         
     | 
| 
      
 106 
     | 
    
         
            +
                @@default_method = {meth => @@command_options} if @@command_options[:default]
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                @@commands.sort.each {|com| @@commands.merge!(com[0]=>@@commands.delete(com[0]))}
         
     | 
| 
      
 109 
     | 
    
         
            +
                
         
     | 
| 
      
 110 
     | 
    
         
            +
                @@commands.merge!(HELP_COMMAND.dup) # makes sure the help command is always last
         
     | 
| 
      
 111 
     | 
    
         
            +
                @@command_options = nil
         
     | 
| 
      
 112 
     | 
    
         
            +
                @@attribute = nil
         
     | 
| 
      
 113 
     | 
    
         
            +
              end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
              # Trap method creation after a command call 
         
     | 
| 
      
 116 
     | 
    
         
            +
              def method_added(meth)
         
     | 
| 
      
 117 
     | 
    
         
            +
                set_trace_func(nil)
         
     | 
| 
      
 118 
     | 
    
         
            +
                return super(meth) if meth == :initialize || @@command_options == nil
         
     | 
| 
      
 119 
     | 
    
         
            +
                
         
     | 
| 
      
 120 
     | 
    
         
            +
                if @@attribute
         
     | 
| 
      
 121 
     | 
    
         
            +
                  #synthesize parameter
         
     | 
| 
      
 122 
     | 
    
         
            +
                  @@command_options.merge!(:parameters=>[[:writer, :value]],:class_method=>false)
         
     | 
| 
      
 123 
     | 
    
         
            +
                else
         
     | 
| 
      
 124 
     | 
    
         
            +
                  # create parameter
         
     | 
| 
      
 125 
     | 
    
         
            +
                  @@command_options.merge!(:parameters=>self.instance_method(meth).parameters,:class_method=>false)
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
                
         
     | 
| 
      
 128 
     | 
    
         
            +
                add_command(meth)
         
     | 
| 
      
 129 
     | 
    
         
            +
              end
         
     | 
| 
      
 130 
     | 
    
         
            +
              
         
     | 
| 
      
 131 
     | 
    
         
            +
              # Trap class methods too
         
     | 
| 
      
 132 
     | 
    
         
            +
              def singleton_method_added(meth)
         
     | 
| 
      
 133 
     | 
    
         
            +
                set_trace_func(nil)
         
     | 
| 
      
 134 
     | 
    
         
            +
                return super(meth) if meth == :initialize || @@command_options == nil
         
     | 
| 
      
 135 
     | 
    
         
            +
                @@command_options.merge!(:parameters=>method(meth).parameters, :class_method=>true)
         
     | 
| 
      
 136 
     | 
    
         
            +
                add_command(meth)
         
     | 
| 
      
 137 
     | 
    
         
            +
              end
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Commandable
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 4 
     | 
    
         
            +
                
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Resets the class to default values clearing any commands
         
     | 
| 
      
 6 
     | 
    
         
            +
                # and setting the colors back to their default values.
         
     | 
| 
      
 7 
     | 
    
         
            +
                def reset_all
         
     | 
| 
      
 8 
     | 
    
         
            +
                  clear_commands
         
     | 
| 
      
 9 
     | 
    
         
            +
                  reset_colors
         
     | 
| 
      
 10 
     | 
    
         
            +
                  reset_screen_clearing
         
     | 
| 
      
 11 
     | 
    
         
            +
                  
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @app_info = nil
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @app_exe = nil
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @verbose_parameters = true
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @@default_method = nil
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @@class_cache = {}
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
              # Inititializes Commandable's settings when it's first loaded
         
     | 
| 
      
 22 
     | 
    
         
            +
              reset_all
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,122 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Extending your class with this module allows you to 
         
     | 
| 
      
 4 
     | 
    
         
            +
            # use the #command method above your method. 
         
     | 
| 
      
 5 
     | 
    
         
            +
            # This makes them executable from the command line.
         
     | 
| 
      
 6 
     | 
    
         
            +
            module Commandable
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # A wrapper for the execution_queue that runs the queue and traps errors. 
         
     | 
| 
      
 11 
     | 
    
         
            +
                # If an error occurs inside this method it will print out a complete list
         
     | 
| 
      
 12 
     | 
    
         
            +
                # of availavle methods with usage instructions and exit gracefully.
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # If you do not want the output from your methods to be printed out automatically
         
     | 
| 
      
 15 
     | 
    
         
            +
                # run the execute command with silent set to anything other than false or nil.
         
     | 
| 
      
 16 
     | 
    
         
            +
                def execute(argv=ARGV.clone, silent=false)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 18 
     | 
    
         
            +
                    command_queue = execution_queue(argv)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    command_queue.each do |com|
         
     | 
| 
      
 20 
     | 
    
         
            +
                      return_value = com[:proc].call
         
     | 
| 
      
 21 
     | 
    
         
            +
                      puts return_value if !silent || com[:method] == :help
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  rescue SystemExit => kernel_exit
         
     | 
| 
      
 24 
     | 
    
         
            +
                    Kernel.exit kernel_exit.status
         
     | 
| 
      
 25 
     | 
    
         
            +
                  rescue Exception => exception
         
     | 
| 
      
 26 
     | 
    
         
            +
                    if exception.respond_to?(:friendly_name)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      set_colors
         
     | 
| 
      
 28 
     | 
    
         
            +
                      puts help("\n  #{@c_error_word}Error:#{@c_reset} #{@c_error_name}#{exception.friendly_name}#{@c_reset}\n  #{@c_error_description}#{exception.message}#{@c_reset}\n\n")
         
     | 
| 
      
 29 
     | 
    
         
            +
                    else
         
     | 
| 
      
 30 
     | 
    
         
            +
                      puts exception.inspect
         
     | 
| 
      
 31 
     | 
    
         
            +
                      puts exception.backtrace.collect{|line| " #{line}"}
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
                
         
     | 
| 
      
 36 
     | 
    
         
            +
                # Returns an array of executable procs based on the given array of commands and parameters
         
     | 
| 
      
 37 
     | 
    
         
            +
                # Normally this would come from the command line parameters in the ARGV array.
         
     | 
| 
      
 38 
     | 
    
         
            +
                def execution_queue(argv)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  arguments = argv.dup
         
     | 
| 
      
 40 
     | 
    
         
            +
                  method_hash = {}
         
     | 
| 
      
 41 
     | 
    
         
            +
                  last_method = nil
         
     | 
| 
      
 42 
     | 
    
         
            +
                  
         
     | 
| 
      
 43 
     | 
    
         
            +
                  if arguments.empty?
         
     | 
| 
      
 44 
     | 
    
         
            +
                    arguments << @@default_method.keys[0] if @@default_method and !@@default_method.values[0][:parameters].flatten.include?(:req)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  arguments << "help" if arguments.empty?
         
     | 
| 
      
 47 
     | 
    
         
            +
                  
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # Parse the command line into methods and their parameters
         
     | 
| 
      
 49 
     | 
    
         
            +
                  
         
     | 
| 
      
 50 
     | 
    
         
            +
                  arguments.each do |arg|
         
     | 
| 
      
 51 
     | 
    
         
            +
                    if Commandable[arg]
         
     | 
| 
      
 52 
     | 
    
         
            +
                      last_method = arg.to_sym
         
     | 
| 
      
 53 
     | 
    
         
            +
                      method_hash.merge!(last_method=>[])
         
     | 
| 
      
 54 
     | 
    
         
            +
                    else
         
     | 
| 
      
 55 
     | 
    
         
            +
                      unless last_method
         
     | 
| 
      
 56 
     | 
    
         
            +
                        default = find_by_subkey(:default)
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                        # Raise an error if there is no default method and the first item isn't a method
         
     | 
| 
      
 59 
     | 
    
         
            +
                        raise UnknownCommandError, arguments.first if default.empty?
         
     | 
| 
      
 60 
     | 
    
         
            +
                        
         
     | 
| 
      
 61 
     | 
    
         
            +
                        # Raise an error if there is a default method but it doesn't take any parameters
         
     | 
| 
      
 62 
     | 
    
         
            +
                        raise UnknownCommandError, (arguments.first) if default.values[0][:argument_list] == ""
         
     | 
| 
      
 63 
     | 
    
         
            +
                        
         
     | 
| 
      
 64 
     | 
    
         
            +
                        last_method = default.keys.first
         
     | 
| 
      
 65 
     | 
    
         
            +
                        method_hash.merge!(last_method=>[])
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end
         
     | 
| 
      
 67 
     | 
    
         
            +
                      method_hash[last_method] << arg
         
     | 
| 
      
 68 
     | 
    
         
            +
                    end
         
     | 
| 
      
 69 
     | 
    
         
            +
                    # Test for missing required switches
         
     | 
| 
      
 70 
     | 
    
         
            +
                    @@commands.select do |key, value|
         
     | 
| 
      
 71 
     | 
    
         
            +
                      if value[:required] and method_hash[key].nil?
         
     | 
| 
      
 72 
     | 
    
         
            +
                        # If the required switch is also a default have the error be a missing parameter instead of a missing command
         
     | 
| 
      
 73 
     | 
    
         
            +
                        if value[:default]
         
     | 
| 
      
 74 
     | 
    
         
            +
                          method_hash.merge!(key=>[])
         
     | 
| 
      
 75 
     | 
    
         
            +
                        else
         
     | 
| 
      
 76 
     | 
    
         
            +
                          raise MissingRequiredCommandError, key 
         
     | 
| 
      
 77 
     | 
    
         
            +
                        end
         
     | 
| 
      
 78 
     | 
    
         
            +
                      end
         
     | 
| 
      
 79 
     | 
    
         
            +
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                  
         
     | 
| 
      
 82 
     | 
    
         
            +
                  # Build an array of procs to be called for each method and its given parameters
         
     | 
| 
      
 83 
     | 
    
         
            +
                  proc_array = []
         
     | 
| 
      
 84 
     | 
    
         
            +
                  method_hash.each do |meth, params|
         
     | 
| 
      
 85 
     | 
    
         
            +
                    command = @@commands[meth]
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    if command[:parameters] && !command[:parameters].empty?
         
     | 
| 
      
 88 
     | 
    
         
            +
                      
         
     | 
| 
      
 89 
     | 
    
         
            +
                      #Change the method name for attr_writers
         
     | 
| 
      
 90 
     | 
    
         
            +
                      meth = "#{meth}=" if command[:parameters][0][0] == :writer
         
     | 
| 
      
 91 
     | 
    
         
            +
                    
         
     | 
| 
      
 92 
     | 
    
         
            +
                      # Get a list of required parameters and make sure all of them were provided
         
     | 
| 
      
 93 
     | 
    
         
            +
                      required = command[:parameters].select{|param| [:req, :writer].include?(param[0])}
         
     | 
| 
      
 94 
     | 
    
         
            +
                      required.shift(params.count)
         
     | 
| 
      
 95 
     | 
    
         
            +
                      raise MissingRequiredParameterError, {:method=>meth, :parameters=>required.collect!{|meth| meth[1]}.to_s[1...-1].gsub(":",""), :default=>command[:default]} unless required.empty?
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
                    
         
     | 
| 
      
 98 
     | 
    
         
            +
                    # Test for duplicate XORs
         
     | 
| 
      
 99 
     | 
    
         
            +
                    proc_array.select{|x| x[:xor] and x[:xor]==command[:xor] }.each {|bad| raise ExclusiveMethodClashError, "#{meth}, #{bad[:method]}"}
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                    klass = Object
         
     | 
| 
      
 102 
     | 
    
         
            +
                    command[:class].split(/::/).each { |name| klass = klass.const_get(name) }
         
     | 
| 
      
 103 
     | 
    
         
            +
                    ## Look for class in class cache
         
     | 
| 
      
 104 
     | 
    
         
            +
                    unless command[:class_method]
         
     | 
| 
      
 105 
     | 
    
         
            +
                      klass = (@@class_cache[klass.name] ||= klass.new)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
      
 107 
     | 
    
         
            +
                    proc_array << {:method=>meth, :xor=>command[:xor], :parameters=>params, :priority=>command[:priority], :proc=>lambda{klass.send(meth, *params)}}
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
                  proc_array.sort{|a,b| a[:priority] <=> b[:priority]}.reverse
         
     | 
| 
      
 110 
     | 
    
         
            +
                
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                private 
         
     | 
| 
      
 114 
     | 
    
         
            +
                
         
     | 
| 
      
 115 
     | 
    
         
            +
                # Look through commands for a specific subkey
         
     | 
| 
      
 116 
     | 
    
         
            +
                def find_by_subkey(key, value=true)
         
     | 
| 
      
 117 
     | 
    
         
            +
                  @@commands.select {|meth, meth_value| meth_value[key]==value}
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
              end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,73 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Commandable
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              # Default command that always gets added to end of the command list
         
     | 
| 
      
 4 
     | 
    
         
            +
              HELP_COMMAND = {:help => {:description => "you're looking at it now", :argument_list => "", :class=>"Commandable", :class_method=>true}}
         
     | 
| 
      
 5 
     | 
    
         
            +
              
         
     | 
| 
      
 6 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 7 
     | 
    
         
            +
                
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Describes your application, printed at the top of help/usage messages
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_accessor :app_info
         
     | 
| 
      
 10 
     | 
    
         
            +
              
         
     | 
| 
      
 11 
     | 
    
         
            +
                # Used when building the usage line, e.g. Usage: app_exe [command] [parameters]
         
     | 
| 
      
 12 
     | 
    
         
            +
                attr_accessor :app_exe
         
     | 
| 
      
 13 
     | 
    
         
            +
              
         
     | 
| 
      
 14 
     | 
    
         
            +
                # If optional parameters show default values, true by default
         
     | 
| 
      
 15 
     | 
    
         
            +
                attr_accessor :verbose_parameters
         
     | 
| 
      
 16 
     | 
    
         
            +
                
         
     | 
| 
      
 17 
     | 
    
         
            +
                # Generates an array of the available commands with a
         
     | 
| 
      
 18 
     | 
    
         
            +
                # list of their parameters and the method's description.
         
     | 
| 
      
 19 
     | 
    
         
            +
                # This includes the applicaiton info and app name if given.
         
     | 
| 
      
 20 
     | 
    
         
            +
                # It's meant to be printed to the command line.
         
     | 
| 
      
 21 
     | 
    
         
            +
                def help(additional_info=nil)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  
         
     | 
| 
      
 23 
     | 
    
         
            +
                  set_colors
         
     | 
| 
      
 24 
     | 
    
         
            +
                  set_screen_clear
         
     | 
| 
      
 25 
     | 
    
         
            +
                  
         
     | 
| 
      
 26 
     | 
    
         
            +
                  cmd_length = "Command".length
         
     | 
| 
      
 27 
     | 
    
         
            +
                  parm_length = "Parameters".length
         
     | 
| 
      
 28 
     | 
    
         
            +
                  max_command = [(@@commands.keys.max_by{|key| key.to_s.length }).to_s.length, cmd_length].max
         
     | 
| 
      
 29 
     | 
    
         
            +
                  max_parameter = @@commands[@@commands.keys.max_by{|key| @@commands[key][:argument_list].length }][:argument_list].length
         
     | 
| 
      
 30 
     | 
    
         
            +
                  max_parameter = [parm_length, max_parameter].max if max_parameter > 0
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  usage_text = "  #{@c_usage}Usage:#{@c_reset} "
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  if Commandable.app_exe      
         
     | 
| 
      
 35 
     | 
    
         
            +
                    cmd_text = "<#{@c_command + @c_bold}command#{@c_reset}>"
         
     | 
| 
      
 36 
     | 
    
         
            +
                    parm_text = " [#{@c_parameter + @c_bold}parameters#{@c_reset}]" if max_parameter > 0
         
     | 
| 
      
 37 
     | 
    
         
            +
                    usage_text += "#{@c_app_exe + app_exe + @c_reset} #{cmd_text}#{parm_text} [#{cmd_text}#{parm_text}...]"
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  array =  [usage_text, ""]
         
     | 
| 
      
 41 
     | 
    
         
            +
                  
         
     | 
| 
      
 42 
     | 
    
         
            +
                  array.unshift additional_info if additional_info
         
     | 
| 
      
 43 
     | 
    
         
            +
                  array.unshift (@c_app_info + Commandable.app_info + @c_reset) if Commandable.app_info
         
     | 
| 
      
 44 
     | 
    
         
            +
                  array.unshift @s_clear_screen_code
         
     | 
| 
      
 45 
     | 
    
         
            +
                  
         
     | 
| 
      
 46 
     | 
    
         
            +
                  header_text = " #{" "*(max_command-cmd_length)}#{@c_command + @c_bold}Command#{@c_reset} "
         
     | 
| 
      
 47 
     | 
    
         
            +
                  header_text += "#{@c_parameter + @c_bold}Parameters #{@c_reset}#{" "*(max_parameter-parm_length)}" if max_parameter > 0
         
     | 
| 
      
 48 
     | 
    
         
            +
                  header_text += "#{@c_description + @c_bold}Description#{@c_reset}"
         
     | 
| 
      
 49 
     | 
    
         
            +
                  
         
     | 
| 
      
 50 
     | 
    
         
            +
                  array << header_text
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  array += @@commands.keys.collect do |key|
         
     | 
| 
      
 53 
     | 
    
         
            +
                    is_default = (@@default_method and key == @@default_method.keys[0])
         
     | 
| 
      
 54 
     | 
    
         
            +
                    default_color =  is_default ? @c_bold : ""
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    help_line  = " #{" "*(max_command-key.length)}#{@c_command + default_color + key.to_s + @c_reset}"+
         
     | 
| 
      
 57 
     | 
    
         
            +
                                 " #{default_color + @c_parameter + @@commands[key][:argument_list] + @c_reset}"
         
     | 
| 
      
 58 
     | 
    
         
            +
                    help_line += "#{" "*(max_parameter-@@commands[key][:argument_list].length)} " if max_parameter > 0
         
     | 
| 
      
 59 
     | 
    
         
            +
                    
         
     | 
| 
      
 60 
     | 
    
         
            +
                    # indent new lines
         
     | 
| 
      
 61 
     | 
    
         
            +
                    description = @@commands[key][:description].gsub("\n", "\n" + (" "*(max_command + max_parameter + (max_parameter > 0 ? 1 : 0) + 4)))
         
     | 
| 
      
 62 
     | 
    
         
            +
                    
         
     | 
| 
      
 63 
     | 
    
         
            +
                    help_line += ": #{default_color + @c_description}#{"<#{@@commands[key][:xor]}> " if @@commands[key][:xor]}" +
         
     | 
| 
      
 64 
     | 
    
         
            +
                                 "#{description}" +
         
     | 
| 
      
 65 
     | 
    
         
            +
                                 "#{" (default)" if is_default}#{@c_reset}" 
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  array << nil
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
              
         
     | 
| 
      
 73 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/commandable/version.rb
    CHANGED
    
    
| 
         @@ -18,37 +18,9 @@ describe Commandable do 
     | 
|
| 
       18 
18 
     | 
    
         
             
                # or it won't be able to use the settings
         
     | 
| 
       19 
19 
     | 
    
         
             
                load 'commandable/app_controller.rb'
         
     | 
| 
       20 
20 
     | 
    
         
             
              }
         
     | 
| 
       21 
     | 
    
         
            -
              
         
     | 
| 
       22 
     | 
    
         
            -
              context "when running the widget command" do
         
     | 
| 
       23 
21 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                  
         
     | 
| 
       26 
     | 
    
         
            -
                  it "should inform them they need Git" do
         
     | 
| 
       27 
     | 
    
         
            -
                    Commandable::AppController.stub(:git_installed?){false}
         
     | 
| 
       28 
     | 
    
         
            -
                    execute_output_s(["widget"]).should match(/Git must be installed/)
         
     | 
| 
       29 
     | 
    
         
            -
                  end
         
     | 
| 
       30 
     | 
    
         
            -
                
         
     | 
| 
       31 
     | 
    
         
            -
                end
         
     | 
| 
       32 
     | 
    
         
            -
                context "when git is installed" do
         
     | 
| 
       33 
     | 
    
         
            -
                  
         
     | 
| 
       34 
     | 
    
         
            -
                  context "and it's able to install the files" do
         
     | 
| 
       35 
     | 
    
         
            -
                    it "should download Widget from github" do
         
     | 
| 
       36 
     | 
    
         
            -
                      Commandable::AppController.stub(:download_widget){0}
         
     | 
| 
       37 
     | 
    
         
            -
                      execute_output_s(["widget"]).should_not include("Unable to download")
         
     | 
| 
       38 
     | 
    
         
            -
                    end
         
     | 
| 
       39 
     | 
    
         
            -
                  end
         
     | 
| 
       40 
     | 
    
         
            -
                  
         
     | 
| 
       41 
     | 
    
         
            -
                  context "but it's not able to install the files" do
         
     | 
| 
       42 
     | 
    
         
            -
                    it "should download Widget from github" do
         
     | 
| 
       43 
     | 
    
         
            -
                      Commandable::AppController.stub(:download_widget){1}
         
     | 
| 
       44 
     | 
    
         
            -
                      execute_output_s(["widget"]).should include("Unable to download")
         
     | 
| 
       45 
     | 
    
         
            -
                    end
         
     | 
| 
       46 
     | 
    
         
            -
                  end
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                
         
     | 
| 
       49 
     | 
    
         
            -
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              it "should have a test for examples"
         
     | 
| 
       50 
23 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
              it "should have a test for the readme file"
         
     | 
| 
       53 
25 
     | 
    
         | 
| 
       54 
26 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,96 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Commandable do
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              before(:each) do 
         
     | 
| 
      
 6 
     | 
    
         
            +
                Commandable.reset_all
         
     | 
| 
      
 7 
     | 
    
         
            +
                load 'private_methods_bad.rb'
         
     | 
| 
      
 8 
     | 
    
         
            +
                Commandable.app_exe = "mycoolapp"
         
     | 
| 
      
 9 
     | 
    
         
            +
                Commandable.app_info = 
         
     | 
| 
      
 10 
     | 
    
         
            +
            """  My Cool App - It does stuff and things!
         
     | 
| 
      
 11 
     | 
    
         
            +
              Copyright (c) 2011 Acme Inc."""
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              let(:c) {Term::ANSIColor}
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              context "when screen clearing" do
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                it "should clear the screen if screen clearing is on" do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  Commandable.clear_screen = true
         
     | 
| 
      
 20 
     | 
    
         
            +
                  clear_screen_code = Regexp.escape("#{Commandable.clear_screen_code}")
         
     | 
| 
      
 21 
     | 
    
         
            +
                  Commandable.help.join.should match(clear_screen_code)
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                it "should not clear the screen if screen clearing is off" do
         
     | 
| 
      
 25 
     | 
    
         
            +
                  Commandable.clear_screen = true
         
     | 
| 
      
 26 
     | 
    
         
            +
                  clear_screen_code = Regexp.escape("#{Commandable.clear_screen_code}")
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Commandable.clear_screen = false
         
     | 
| 
      
 28 
     | 
    
         
            +
                  Commandable.help.join.should_not match(clear_screen_code)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                it "should change how the screen is cleared" do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  Commandable.clear_screen = true
         
     | 
| 
      
 33 
     | 
    
         
            +
                  clear_code = "FlabbityJibbity"
         
     | 
| 
      
 34 
     | 
    
         
            +
                  Commandable.clear_screen_code = clear_code
         
     | 
| 
      
 35 
     | 
    
         
            +
                  Commandable.help.join.should match(clear_code)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                it "should not be disabled when color output is disabled" do
         
     | 
| 
      
 39 
     | 
    
         
            +
                  Commandable.clear_screen = true
         
     | 
| 
      
 40 
     | 
    
         
            +
                  clear_screen_code = Regexp.escape("#{Commandable.clear_screen_code}")
         
     | 
| 
      
 41 
     | 
    
         
            +
                  Commandable.color_output = false
         
     | 
| 
      
 42 
     | 
    
         
            +
                  Commandable.help.join.should match(clear_screen_code)
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              context "when a setting color is changed" do
         
     | 
| 
      
 48 
     | 
    
         
            +
                
         
     | 
| 
      
 49 
     | 
    
         
            +
                before(:each) { Commandable.color_output = true } 
         
     | 
| 
      
 50 
     | 
    
         
            +
              
         
     | 
| 
      
 51 
     | 
    
         
            +
                it "should include colors if colored output is enabled" do
         
     | 
| 
      
 52 
     | 
    
         
            +
                  Commandable.color_output = true
         
     | 
| 
      
 53 
     | 
    
         
            +
                  Commandable.help.join.should match(Regexp.escape(Commandable.color_app_info))
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                it "should not include colors if colored output is disabled" do
         
     | 
| 
      
 57 
     | 
    
         
            +
                  Commandable.color_output = false
         
     | 
| 
      
 58 
     | 
    
         
            +
                  Commandable.help.join.should_not match(Regexp.escape(Commandable.color_app_info))
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                # This seems ripe for meta-zation
         
     | 
| 
      
 62 
     | 
    
         
            +
                context "and app_info is changed" do
         
     | 
| 
      
 63 
     | 
    
         
            +
                  specify {lambda {Commandable.color_app_info = c.black}.should change{Commandable.help}}
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                context "and app_exe is changed" do
         
     | 
| 
      
 67 
     | 
    
         
            +
                  specify {lambda {Commandable.color_app_exe = c.black}.should change{Commandable.help}}
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                context "and color_command is changed" do
         
     | 
| 
      
 71 
     | 
    
         
            +
                  specify {lambda {Commandable.color_command = c.black}.should change{Commandable.help}}
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                context "and color_description is changed" do
         
     | 
| 
      
 75 
     | 
    
         
            +
                  specify {lambda {Commandable.color_description = c.black}.should change{Commandable.help}}
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                context "and color_parameter is changed" do
         
     | 
| 
      
 79 
     | 
    
         
            +
                  specify {lambda {Commandable.color_parameter = c.black}.should change{Commandable.help}}
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                context "and color_usage is changed" do
         
     | 
| 
      
 83 
     | 
    
         
            +
                  specify {lambda {Commandable.color_usage = c.black}.should change{Commandable.help}}
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                context "and there is an error" do
         
     | 
| 
      
 87 
     | 
    
         
            +
                  
         
     | 
| 
      
 88 
     | 
    
         
            +
                  specify { lambda {Commandable.color_error_word = c.magenta}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
         
     | 
| 
      
 89 
     | 
    
         
            +
                  specify { lambda {Commandable.color_error_name = c.intense_red}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
         
     | 
| 
      
 90 
     | 
    
         
            +
                  specify { lambda {Commandable.color_error_description = c.black + c.bold}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
         
     | 
| 
      
 91 
     | 
    
         
            +
                  
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
                
         
     | 
| 
      
 94 
     | 
    
         
            +
              end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
            end
         
     |