Class: Amp::Command

Inherits:
Object show all
Includes:
CommandSupport
Defined in:
lib/amp/commands/command.rb

Overview

Represents a command within the Amp system. Simply instantiating a new command will make it available to the Amp executable. You configure the command by filling out a block in the command’s initializer.

Examples:

Command.new "add" do |c|
  c.workflow :hg
  c.opt :include, "Paths to include", 
                      :options => {:short => "-I", :multi => true}
  c.opt :print_names, :desc => "Print the filenames", 
            :options => {:short => "-p"   , 
                         :default => false, 
                         :type => :boolean}
  c.on_run do |options, arguments|
    if options[:print_names]
      arguments.each do |filename|
        puts filename
      end
    end
  end
  c.help "This is the help text when the user runs `amp help add`"
end

Constant Summary collapse

GLOBAL_OPTIONS =

These are options that all commands support. Allows the user to put them after the subcommand.

[]
NO_REPO_ALLOWED =
{}
MAYBE_REPO_ALLOWED =
{}

Constants included from CommandSupport

Amp::CommandSupport::REV_SEP

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CommandSupport

#expand_path, #local_path, #log_message, #parse_url, #print_update_stats, #revision_lookup, #revision_pair

Constructor Details

#initialize(name, require_new = false) {|The| ... } ⇒ Command

Creates a command in the Amp system. Simply instantiating a new command will make it available to the Amp executable. You configure the command by filling out a block in the command’s initializer.

the command

Examples:

Command.new("add") do |c|
  c.opt :include, "Paths to include", 
            :options => {:short => "-I", :multi => true}
  c.opt :print_names, :desc => "Print the filenames", 
            :options => {:short => "-p", :default => false, 
                         :type => :boolean}
  c.on_run do |options, arguments|
    puts "silly!"
  end
end

Parameters:

  • name

    the name of the command that the user will use to call

  • &block

    a block of code where you can configure the command

Yields:

  • This block configures the command. Set up options, add an on_run handler, and so on.

Yield Parameters:

  • The

    command itself - it is yielded so you can modify it.



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/amp/commands/command.rb', line 175

def initialize(name, require_new = false)
  # so that you can do additions to commands, just like ammending rake tasks
  full_name = (self.class.current_namespaces + [name]).join(":")
  name = full_name.to_sym
  if self.class.all_commands[name]
    yield self.class.all_commands[name] if block_given?
    return self.class.all_commands[name]
  end
  
  @name                = name
  @help                = ""
  @options             = []
  self.class.all_commands[name] = self
  @before = []
  @after  = []

  @workflows = []
  @synonyms  = []
  yield(self) if block_given?
  workflow :all if @workflows.empty?
  @options += GLOBAL_OPTIONS
end

Class Attribute Details

.all_commandsHash<Symbol => Amp::Command> (readonly)

Returns all of the commands registered in the system.

Returns:



63
64
65
# File 'lib/amp/commands/command.rb', line 63

def all_commands
  @all_commands
end

.all_synonymsHash<Symbol => Amp::Command> (readonly)

Returns all the synonyms registered in the system.

Returns:



69
70
71
# File 'lib/amp/commands/command.rb', line 69

def all_synonyms
  @all_synonyms
end

.current_namespacesObject (readonly)

Returns the value of attribute current_namespaces.



57
58
59
# File 'lib/amp/commands/command.rb', line 57

def current_namespaces
  @current_namespaces
end

.workflowsObject (readonly)

Returns the value of attribute workflows.



70
71
72
# File 'lib/amp/commands/command.rb', line 70

def workflows
  @workflows
end

Instance Attribute Details

#descriptionObject

Short, 1-line description of the command



149
150
151
# File 'lib/amp/commands/command.rb', line 149

def description
  @description
end

#nameObject

The name of the command (eg ‘add’, ‘init’)



147
148
149
# File 'lib/amp/commands/command.rb', line 147

def name
  @name
end

#optionsObject

Command-specific command-line options



145
146
147
# File 'lib/amp/commands/command.rb', line 145

def options
  @options
end

#parserObject

The Trollop parser



151
152
153
# File 'lib/amp/commands/command.rb', line 151

def parser
  @parser
end

Class Method Details

.[](arg) ⇒ Hash<Symbol => Amp::Command>, NilClass

Returns all of the commands registered in the system.

Returns:

  • (Hash<Symbol => Amp::Command>, NilClass)

    the commands, keyed by command name as a symbol. returns nil if nothing is found



139
140
141
# File 'lib/amp/commands/command.rb', line 139

def [](arg)
  all[arg.to_sym]
end

.allHash<Symbol => Amp::Command>

Returns all commands and synonyms registered in the system. The commands are merged into the synonyms so that any synonym with the same name as a command will be overwritten in the hash.

Returns:



93
94
95
# File 'lib/amp/commands/command.rb', line 93

def all
  all_synonyms.merge all_commands
end

.all_for_workflow(flow, synonyms = true) ⇒ Hash<Symbol => Amp::Command>

Returns all commands and synonyms registered in the system for a given workflow. The “:all” workflow is automatically merged in, as well.

Parameters:

  • workflow (Symbol)

    the workflow whose commands we need

Returns:

  • (Hash<Symbol => Amp::Command>)

    the commands and synonyms for the workflow (and all global commands), keyed by command name as a string



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/amp/commands/command.rb', line 104

def all_for_workflow(flow, synonyms=true)
  flow = flow.to_sym
  
  cmds = workflows[flow].merge workflows[:all]
  
  if synonyms
    # because there is no way to view all synonyms with workflow +flow+,
    # we have to work bottom up (hence the doubly linked aspect for commands,
    # which is reduced to singly linked)
    syns = all_synonyms.select {|k, v| v.workflows.include? flow }
    syns = syns.to_hash
  else
    syns = {}
  end
  
  syns.merge cmds
end

.command_for_workflow(cmd, flow) ⇒ Amp::Command

Gets a specific command, for a given workflow. This is necessary because it will be expected that 2 different workflows have a command with the same name (such as “move”).

Parameters:

  • cmd (String, Symbol)

    the command to look up

  • the (Symbol)

    workflow to use for the lookup

Returns:

  • (Amp::Command)

    the command for the given name and workflow



130
131
132
# File 'lib/amp/commands/command.rb', line 130

def command_for_workflow(cmd, flow)
  all_for_workflow(flow)[cmd.to_sym]
end

.pop_namespaceObject

Removes one namespace from the active namespaces for new commands



82
83
84
# File 'lib/amp/commands/command.rb', line 82

def pop_namespace
  current_namespaces.pop
end

.use_namespace(namespace) ⇒ Object

Appends the given namespace to the active namespace for new commands.

Parameters:

  • namespace (String, #to_s)

    the new namespace to add



76
77
78
# File 'lib/amp/commands/command.rb', line 76

def use_namespace(namespace)
  current_namespaces.push namespace
end

Instance Method Details

#add_namespace(ns) ⇒ Object

Adds a namespace to the name of the command. This extra method is needed because many class variables expect this command based on its name - if we don’t update these, then our entire program will expect a command with the old name. Not cool.

Parameters:

  • ns (String)

    the namespace to put in front of the command’s name



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/amp/commands/command.rb', line 429

def add_namespace(ns)
  to = "#{ns}:#{name}".to_sym
  if self.class.all_commands[name] == self
    self.class.all_commands[to] = self.class.all_commands.delete name
  end
  @workflows.each do |flow|
    if self.class.workflows[flow][name] == self
      self.class.workflows[flow][to] = self.class.workflows[flow].delete name
    end
  end
  @synonyms.each do |syn|
    if self.class.all_synonyms[syn] == self
      self.class.all_synonyms["#{ns}:#{syn}"] = self.class.all_synonyms.delete syn
    end
  end
  @name = to
end

#after(*args) {|options, arguments| ... } ⇒ Hash

This returns the list of actions to run after the command, in order (first ones are run first). You can modify this array in any way you choose, and it is run after the command is run.

Yields:

  • Extra code to run after the command is executed, after options are prepared.

Yield Parameters:

  • options

    The options that the dispatcher has prepared for the command. Includes global and local.

  • arguments

    All arguments passed to the command, after the options.

Returns:

  • (Hash)

    an array of strings and blocks. Strings are assumed to be command names and blocks are pieces of code to be run.



322
323
324
325
326
327
328
329
# File 'lib/amp/commands/command.rb', line 322

def after(*args, &block)
  args.each do |arg|
    @after << proc {|opts, args| Amp::Command[arg.to_sym].run(opts, args) }
  end
  
  @after << block if block
  @after
end

#before(*args) {|options, arguments| ... } ⇒ Hash

This returns the list of actions to run before the command, in order (first ones are run first). You can modify this array in any way you choose, and it is run before the command is run.

Yields:

  • Extra code to run before the command is executed, after options are prepared.

Yield Parameters:

  • options

    The options that the dispatcher has prepared for the command. Includes global and local.

  • arguments

    All arguments passed to the command, after the options.

Returns:

  • (Hash)

    an array of strings and blocks. Strings are assumed to be command names and blocks are pieces of code to be run.



303
304
305
306
307
308
309
310
# File 'lib/amp/commands/command.rb', line 303

def before(*args, &block)
  args.each do |arg|
    @before << proc {|opts, args| Amp::Command[arg.to_sym].run(opts, args) }
  end
  
  @before << block if block
  @before
end

#collect_optionsHash<Symbol => Object>

Parses the commands from the command line using Trollop. You probably shouldn’t override this method, but if you have good reason, go for it.

Returns:



400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/amp/commands/command.rb', line 400

def collect_options
  options = @options # hack to get around the fact that
  help    = @help    # Trollop uses instance eval
  
  ret = Trollop::options do
    banner help
    
    # we can't use @options here because Trollop::options uses instance_eval
    # therefore we have to use a local to cheat death^H^H^H^H^Hinstance_eval
    options.each do |option|
      opt option[:name], option[:desc], option[:options]
    end
  end
  
  @parser = ret.pop
  ret.first
end

#default(opt, value) ⇒ Object

Override a default value for a command option. Useful for user-provided ampfiles.

Examples:

This example will make ‘amp status` default to mercurial-style

output, instead of amp's colorful, easy-to-read output.
   command :status do |c|
     default :hg, true
     default :"no-color", true
   end

Parameters:

  • opt (Symbol, #to_sym)

    the option to modify. Can be symbol or string.

  • value

    the new default value for the option



229
230
231
232
233
234
235
# File 'lib/amp/commands/command.rb', line 229

def default(opt, value)
  opt = opt.to_sym
  the_opt = @options.select {|o| o[:name] == opt}.first
  if the_opt
    the_opt[:options][:default] = value
  end
end

#desc(str = nil) ⇒ Object

The one-line description of the command. This is the first line of the help text. If no argument is passed, then the desription is returned. If an argument is passed, it will be set to be the first line of the help text.

Examples:

cmd.desc # => “This command is useless.”

Parameters:

  • str (String, nil) (defaults to: nil)

    the help text to set



339
340
341
# File 'lib/amp/commands/command.rb', line 339

def desc(str=nil)
  str ? @help = "#{str}\n\n#{@help}" : @help.split("\n").first
end

#educateObject Also known as: education

Trollop’s help info for the command



345
346
347
# File 'lib/amp/commands/command.rb', line 345

def educate
  @parser ? @parser.educate : ''
end

#help(str = nil) ⇒ Object Also known as: help=

Sets the help text for the command. This can be a very long string, as it is what the user sees when they type ‘amp help name`

}

Examples:

cmd.help %Q{

Big help text!

Parameters:

  • str (defaults to: nil)

    the help text to set



384
385
386
# File 'lib/amp/commands/command.rb', line 384

def help(str=nil)
  str ? @help << str : @help
end

#inspectObject



418
419
420
# File 'lib/amp/commands/command.rb', line 418

def inspect
  "#<Amp::Command #{name}>"
end

#maybe_repoObject

Sets the command to not require a repository to run, but try to load one. Used, for example, for the templates command, which sometimes stores information in the local repository.



367
368
369
# File 'lib/amp/commands/command.rb', line 367

def maybe_repo
  MAYBE_REPO_ALLOWED[@name] = true
end

#maybe_repo=(value) ⇒ Object

See Also:



372
373
374
# File 'lib/amp/commands/command.rb', line 372

def maybe_repo=(value)
  MAYBE_REPO_ALLOWED[@name] = value
end

#no_repoObject

Sets the command to not laod a repository when run. Useful for purely informational commands (such as version) or initializing a new repository.



354
355
356
# File 'lib/amp/commands/command.rb', line 354

def no_repo
  NO_REPO_ALLOWED[@name] = true
end

#no_repo=(value) ⇒ Object

See Also:



359
360
361
# File 'lib/amp/commands/command.rb', line 359

def no_repo=(value)
  NO_REPO_ALLOWED[@name] = value
end

#on_run {|options, arguments| ... } ⇒ Object

This method is how you set what the command does when it is run.

Examples:

Command.new("email_news") do |c|
 c.on_run do |options, arguments|
   arguments.each do |email_address|
     send_some_email(options[:email_subject],email_address)
   end
 end
end

Parameters:

  • &block

    the code to run when the command runs

Yields:

  • The code to run when the command is executed, after options are prepared.

Yield Parameters:

  • options

    The options that the dispatcher has prepared for the command. Includes global and local.

  • arguments

    All arguments passed to the command, after the options.



255
256
257
# File 'lib/amp/commands/command.rb', line 255

def on_run(&block)
  @code = proc(&block) # this way we have the ability to do `return`
end

#opt(name, desc = '', options = {}) ⇒ Object Also known as: add_opt

Adds an command-line option to the command.

Parameters:

  • name

    the name of the command the user will type to run it

  • desc (defaults to: '')

    the short, one-line description of the command

  • options (defaults to: {})

    the options that configure the command-line option (too meta? sorry!)

  • [String] (Hash)

    a customizable set of options

  • [Symbol] (Hash)

    a customizable set of options

  • [Boolean] (Hash)

    a customizable set of options



212
213
214
# File 'lib/amp/commands/command.rb', line 212

def opt(name, desc='', options={})
  @options << {:name => name, :desc => desc, :options => options}
end

#run(options = {}, args = []) ⇒ Amp::Command

Called by the dispatcher to execute the command. You really don’t need to override this. The ‘$break` global can be set by anything, which will halt the chain.

Parameters:

  • options (Hash<Symbol => Object>) (defaults to: {})

    The global options, merged with the command-specific options, as decided by the dispatcher.

  • arguments (Array<String>)

    The list of arguments, passed after the options. Could be a filename, for example.

Returns:



457
458
459
460
461
462
463
464
465
466
467
# File 'lib/amp/commands/command.rb', line 457

def run(options={}, args=[])
  # run the before commands
  @before.each {|cmd| result = cmd.run options, args; return if !result || $break }
  
  @code[options, args] # and of course the actual command...
  
  # top it off with the after commands
  @after.each  {|cmd| result = cmd.run options, args; return if !result || $break }
  
  self
end

#synonym(*args) ⇒ Object Also known as: synonyms

This method lets you set a synonym (or synonyms) for this command. For example, the “remove” command has the synonym “rm”. Example:

command :remove do |c|
  c.synonym :rm, :destroy, :nuke
end

then you can do

amp nuke badfile.rb


267
268
269
270
271
272
# File 'lib/amp/commands/command.rb', line 267

def synonym(*args)
  args.each do |arg|
    @synonyms << arg
    self.class.all_synonyms[arg.to_sym] = self
  end
end

#workflow(*args) ⇒ Object Also known as: workflows

Specifies a workflow that may access this command. Workflows are groups of commands, pure and simple. If the user has no specified workflow, the mercurial workflow is used by default.



279
280
281
282
283
284
285
286
287
288
# File 'lib/amp/commands/command.rb', line 279

def workflow(*args)
  if args.any? # unless args.empty?
    args.each do |arg|
      self.class.workflows[arg][self.name.to_sym] = self # register globally
      @workflows << arg                  # register locally
    end
  else
    @workflows
  end
end