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

Constant Summary collapse

VERSION =
"0.6.8"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cmd, &b) ⇒ 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.


115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/drydock.rb', line 115

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

Instance Attribute Details

#actionsObject

An array of action names specified in the command definition


99
100
101
# File 'lib/drydock.rb', line 99

def actions
  @actions
end

#aliasObject (readonly)

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


88
89
90
# File 'lib/drydock.rb', line 88

def alias
  @alias
end

#argvObject

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


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

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.


91
92
93
# File 'lib/drydock.rb', line 91

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.


86
87
88
# File 'lib/drydock.rb', line 86

def cmd
  @cmd
end

#descObject

A friendly description of the command.


97
98
99
# File 'lib/drydock.rb', line 97

def desc
  @desc
end

#executableObject (readonly)

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


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

def executable
  @executable
end

#globalObject (readonly)

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


95
96
97
# File 'lib/drydock.rb', line 95

def global
  @global
end

#optionObject (readonly)

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


93
94
95
# File 'lib/drydock.rb', line 93

def option
  @option
end

#stdinObject (readonly)

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


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

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


172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/drydock.rb', line 172

def call  
  self.print_header if self.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(self.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 self.respond_to?(name)  
    end
    
    raise "#{self.class} needs a #{name} method!" unless meth
    
    run_validation(meth)
    self.send(meth)
    
  # No block and no action. We'll try for the method name in the Drydock::Command class. 
  elsif self.respond_to? @cmd.to_sym
    run_validation(@cmd)
    self.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
  
  self.print_footer if respond_to? :print_footer
end

#nameObject

Returns the command name (not the alias)


129
130
131
# File 'lib/drydock.rb', line 129

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.

<li>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.</li> <li>argv an array of unnamed arguments. If ignore :options was declared this</li> will contain the arguments exactly as they were defined on the command-line.</li> <li>stdin contains the output of stdin do; …; end otherwise it's a STDIN IO handle.</li> <li>global_options a hash of the global options specified on the command-line</li> <li>options a hash of the command-specific options specific on the command-line.</li>


146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/drydock.rb', line 146

def prepare(cmd_str=nil, argv=[], stdin=[], global_options={}, options={})
  @alias = cmd_str.nil? ? @cmd : cmd_str

  global_options.each_pair do |n,v|
    self.global.send("#{n}=", v)    # Populate the object's globals
  end
  
  options.each_pair do |n,v|
    self.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
  
  self.init         if self.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: etherpad.com/SXjqQGRr8M


254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
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
# File 'lib/drydock.rb', line 254

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 > 0
    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


317
318
319
# File 'lib/drydock.rb', line 317

def to_s
  @cmd.to_s
end