Class: Drydock::Command

Inherits:
Object show all
Defined in:
lib/drydock.rb

Overview

The base class for all command objects. There is an instance of this class for every command defined. Global and command-specific options are added as attributes to this class dynamically.

i.e. "example -v select --location kumamoto"

global :v, :verbose, "I want mooooore!"
option :l, :location, String, "Source location"
command :select do |obj|
  puts obj.global.verbose   #=> true
  puts obj.option.location  #=> "kumamoto"
end

You can sub-class it to create your own:

class Malpeque < Drydock::Command
  # ... sea to it
end

And then specify your class in the command definition:

command :eat => Malpeque do |obj|
  # ... do stuff with your obj
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cmd, &blk) ⇒ Command

The default constructor sets the short name of the command and stores a reference to the block (if supplied). You don't need to override this method to add functionality to your custom Command classes. Define an init method instead. It will be called just before the block is executed. cmd is the short name of this command. b is the block associated to this command.



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/drydock.rb', line 130

def initialize(cmd, &blk)
  @cmd = cmd.is_a?(Symbol) ? cmd : cmd.to_sym
  @b = blk
  @actions = []
  @argv = Drydock::FancyArray.new # an array with field names
  @stdin = $stdin
  @option = OpenStruct.new
  @global = OpenStruct.new
  @executable = File.basename($PROGRAM_NAME)
  @global.verbose = 0
  @global.quiet = false
end

Instance Attribute Details

#actionsObject

An array of action names specified in the command definition



114
115
116
# File 'lib/drydock.rb', line 114

def actions
  @actions
end

#aliasObject (readonly)

The name used to evoke this command (it's either the canonical name or the alias used).



103
104
105
# File 'lib/drydock.rb', line 103

def alias
  @alias
end

#argvObject

An instance of Drydock::FancyArray. Acts like an array of unnamed arguments but also allows field names if supplied.



117
118
119
# File 'lib/drydock.rb', line 117

def argv
  @argv
end

#bObject (readonly)

The block that will be executed when this command is evoked. If the block is nil it will check if there is a method named cmd. If so, that will be executed.



106
107
108
# File 'lib/drydock.rb', line 106

def b
  @b
end

#cmdObject (readonly)

The canonical name of the command (the one used in the command definition). If you inherit from this class and add a method named cmd, you can leave omit the block in the command definition. That method will be called instead. See bin/examples.



101
102
103
# File 'lib/drydock.rb', line 101

def cmd
  @cmd
end

#descObject

A friendly description of the command.



112
113
114
# File 'lib/drydock.rb', line 112

def desc
  @desc
end

#executableObject (readonly)

The basename of the executable or script: File.basename($0)



121
122
123
# File 'lib/drydock.rb', line 121

def executable
  @executable
end

#globalObject (readonly)

An OpenStruct object containing the global options specified at run-time.



110
111
112
# File 'lib/drydock.rb', line 110

def global
  @global
end

#optionObject (readonly)

An OpenStruct object containing the command options specified at run-time.



108
109
110
# File 'lib/drydock.rb', line 108

def option
  @option
end

#stdinObject (readonly)

Either an IO handle to STDIN or the output of the Drydock#stdin handler.



119
120
121
# File 'lib/drydock.rb', line 119

def stdin
  @stdin
end

Instance Method Details

#callObject

Calls the command in the following order:

  • print_header
  • validation (if methodname_valid? exists)
  • command block (@b)
  • print_footer


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/drydock.rb', line 187

def call
  print_header if respond_to? :print_header

  # Execute the command block if it exists
  if @b
    run_validation
    @b.call(self)

  # Otherwise check to see if an action was specified
  elsif !(chosen = find_action(option)).empty?
    raise "Only one action at a time please! I can't #{chosen.join(' AND ')}." if chosen.size > 1
    criteria = [[@cmd, chosen.first], [chosen.first, @cmd]]
    meth = name = nil
    # Try command_action, then action_command
    criteria.each do |tuple|
      name = tuple.join('_')
      meth = name if respond_to?(name)
    end

    raise "#{self.class} needs a #{name} method!" unless meth

    run_validation(meth)
    send(meth)

  # No block and no action. We'll try for the method name in the Drydock::Command class.
  elsif respond_to? @cmd.to_sym
    run_validation(@cmd)
    send(@cmd)

  # Well, then I have no idea what you want me to do!
  else
    raise "The command #{@alias} has no block and #{self.class} has no #{@cmd} method!"
  end

  print_footer if respond_to? :print_footer
end

#nameObject

Returns the command name (not the alias)



144
145
146
# File 'lib/drydock.rb', line 144

def name
  @cmd
end

#prepare(cmd_str = nil, argv = [], stdin = [], global_options = {}, options = {}) ⇒ Object

Prepare this command object to be called.

Calls self.init after setting attributes (if the method exists). You can implement an init method in your subclasses of Drydock::Command to handle your own initialization stuff.

  • +cmd_str+ is the short name used to evoke this command. It will equal @cmd unless an alias was used used to evoke this command.
  • +argv+ an array of unnamed arguments. If ignore :options was declared this
  • will contain the arguments exactly as they were defined on the command-line.
  • +stdin+ contains the output of stdin do; ...; end otherwise it's a STDIN IO handle.
  • +global_options+ a hash of the global options specified on the command-line
  • +options+ a hash of the command-specific options specific on the command-line.
  • 
    
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    # File 'lib/drydock.rb', line 161
    
    def prepare(cmd_str=nil, argv=[], stdin=[], global_options={}, options={})
      @alias = cmd_str.nil? ? @cmd : cmd_str
    
      global_options.each_pair do |n,v|
        global.send("#{n}=", v)    # Populate the object's globals
      end
    
      options.each_pair do |n,v|
        option.send("#{n}=", v)    # ... and also the command options
      end
    
      @argv << argv    # TODO: Using += returns an Array instead of FancyArray
      @argv.flatten!   # NOTE: << creates @argv[[]]
      @stdin = stdin
    
      init if respond_to? :init # Must be called first!
    
    end

    #show_commandsObject

    Print the list of available commands to STDOUT. This is used as the "default" command unless another default commands is supplied. You can also write your own Drydock::Command#show_commands to override this default behaviour.

    The output was worked on here: http://etherpad.com/SXjqQGRr8M

    
    
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    # File 'lib/drydock.rb', line 269
    
    def show_commands
      project = " for #{Drydock.project}" if Drydock.project?
      cmds = {}
      Drydock.commands.keys.each do |cmd|
        next if cmd == :show_commands
        pretty = Drydock.decanonize(cmd)
        # Out to sea
        cmds[Drydock.commands[cmd].cmd] ||= {}
        unless cmd === Drydock.commands[cmd].cmd
          (cmds[Drydock.commands[cmd].cmd][:aliases] ||= []) << pretty
          next
        end
        cmds[cmd][:desc] = Drydock.commands[cmd].desc
        cmds[cmd][:desc] = nil if cmds[cmd][:desc] && cmds[cmd][:desc].empty?
        cmds[cmd][:pretty] = pretty
      end
    
      cmd_names_sorted = cmds.keys.sort{ |a,b| a.to_s <=> b.to_s }
    
      if @global.quiet
        puts 'Commands: '
        line = []
        cmd_names_sorted.each_with_index do |cmd,i|
          line << cmd
          if (line.size % 4 == 0) || i == (cmd_names_sorted.size - 1)
            puts "  %s" % line.join(', ')
            line.clear
          end
        end
        return
      end
    
      puts '%5s: %s' % ['Usage', "#{@executable} [global options] COMMAND [command options]"]
      puts '%5s: %s' % ['Try', "#{@executable} -h"]
      puts '%5s  %s' % ['', "#{@executable} COMMAND -h"]
      puts
    
      puts 'Commands: '
      if @global.verbose.positive?
        puts # empty line
        cmd_names_sorted.each do |cmd|
          puts "$ %s" % [@executable] if Drydock.default?(cmd)
          puts "$ %s %s" % [@executable, cmds[cmd][:pretty]]
          puts "%10s: %s" % ["About", cmds[cmd][:desc]] if cmds[cmd][:desc]
          if cmds[cmd][:aliases]
            cmds[cmd][:aliases].sort!{ |a,b| a.size <=> b.size }
            puts "%10s: %s" % ["Aliases", cmds[cmd][:aliases].join(', ')]
          end
          puts
        end
    
      else
        cmd_names_sorted.each do |cmd|
          aliases = cmds[cmd][:aliases] || []
          aliases.sort!{ |a,b| a.size <=> b.size }
          aliases = aliases.empty? ? '' : "(aliases: #{aliases.join(', ')})"
          pattern = Drydock.default?(cmd) ? "* %-16s %s" : "  %-16s %s"
          puts pattern % [cmds[cmd][:pretty], aliases]
        end
      end
    end

    #to_sObject

    The name of the command

    
    
    332
    333
    334
    # File 'lib/drydock.rb', line 332
    
    def to_s
      @cmd.to_s
    end