Class: Mothership

Inherits:
Object
  • Object
show all
Defined in:
lib/mothership.rb,
lib/mothership/base.rb,
lib/mothership/errors.rb,
lib/mothership/inputs.rb,
lib/mothership/parser.rb,
lib/mothership/command.rb,
lib/mothership/version.rb,
lib/mothership/callbacks.rb,
lib/mothership/help/commands.rb

Defined Under Namespace

Modules: Help Classes: Command, Error, ExtraArguments, Inputs, MissingArgument, Parser, TypeMismatch

Constant Summary collapse

VERSION =
"0.5.1"
@@global =
Mothership::Command

global options

Command.new(self, "(global options)")
@@exit_status =
Fixnum

exit status; reassign as appropriate error code (e.g. 1)

0
@@commands =

all commands

{}
@@filters =

temporary filters via #with_filters

command => { tag => [callbacks] }

Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = [] } }

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command = nil, input = nil) ⇒ Mothership

Initialize with the command being executed.



11
12
13
14
# File 'lib/mothership/base.rb', line 11

def initialize(command = nil, input = nil)
  @command = command
  @input = input
end

Instance Attribute Details

#inputObject

Returns the value of attribute input.



8
9
10
# File 'lib/mothership/base.rb', line 8

def input
  @input
end

Class Method Details

.add_input(cmd, name, options = {}, &interact) ⇒ Object

add an input/flag to a command



45
46
47
# File 'lib/mothership/callbacks.rb', line 45

def add_input(cmd, name, options = {}, &interact)
  @@commands[cmd].add_input(name, options, &interact)
end

.after(name, &callback) ⇒ Object

register a callback that’s evaluated after a command is run



14
15
16
# File 'lib/mothership/callbacks.rb', line 14

def after(name, &callback)
  @@commands[name].after << [callback, self]
end

.alias_command(new, orig = nil) ⇒ Object



49
50
51
# File 'lib/mothership/base.rb', line 49

def alias_command(new, orig = nil)
  @@commands[new] = orig ? @@commands[orig] : @command
end

.around(name, &callback) ⇒ Object

register a callback that’s evaluated around a command, controlling its evaluation (i.e. inputs)



20
21
22
# File 'lib/mothership/callbacks.rb', line 20

def around(name, &callback)
  @@commands[name].around << [callback, self]
end

.before(name, &callback) ⇒ Object

register a callback that’s evaluated before a command is run



9
10
11
# File 'lib/mothership/callbacks.rb', line 9

def before(name, &callback)
  @@commands[name].before << [callback, self]
end

.change_argument(name, arg, to) ⇒ Object

change an argument’s status, i.e. optional, splat, or required



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/mothership/callbacks.rb', line 31

def change_argument(name, arg, to)
  changed = false

  @@commands[name].arguments.each do |a|
    if a[:name] == arg
      a[:type] = to
      changed = true
    end
  end

  changed
end

.commandsObject

all of the defined commands



18
19
20
# File 'lib/mothership/base.rb', line 18

def commands
  @@commands
end

.desc(description) ⇒ Object

start defining a new command with the given description



23
24
25
# File 'lib/mothership/base.rb', line 23

def desc(description)
  @command = Command.new(self, description)
end

.filter(name, tag, &callback) ⇒ Object

register a callback that’s evaluated when a command uses the given filter



26
27
28
# File 'lib/mothership/callbacks.rb', line 26

def filter(name, tag, &callback)
  @@commands[name].filters[tag] << [callback, self]
end

.global_option(name) ⇒ Object



55
56
57
# File 'lib/mothership.rb', line 55

def global_option(name)
  @@global.inputs[name]
end

.group(*names) ⇒ Object

add command to help group



6
7
8
9
10
11
12
13
14
15
# File 'lib/mothership/help/commands.rb', line 6

def group(*names)
  options =
    if names.last.is_a? Hash
      names.pop
    else
      {}
    end

  Mothership::Help.add_to_group(@command, names, options)
end

.input(name, options = {}, &interact) ⇒ Object

define an input for the current command or the global command



28
29
30
31
32
# File 'lib/mothership/base.rb', line 28

def input(name, options = {}, &interact)
  raise "no current command" unless @command

  @command.add_input(name, options, &interact)
end

.interactions(mod) ⇒ Object

specify a module that defines interactions for each input



35
36
37
# File 'lib/mothership/base.rb', line 35

def interactions(mod)
  @command.interactions = mod
end

.method_added(name) ⇒ Object

register a command



40
41
42
43
44
45
46
47
# File 'lib/mothership/base.rb', line 40

def method_added(name)
  return unless @command

  @command.name = name
  @@commands[name] = @command

  @command = nil
end

.option(name, options = {}, &interact) ⇒ Object

define a global option



17
18
19
# File 'lib/mothership.rb', line 17

def option(name, options = {}, &interact)
  @@global.add_input(name, options, &interact)
end

.start(argv) ⇒ Object

parse argv, by taking the first arg as the command, and the rest as arguments and flags

arguments and flags can be in any order; all flags will be parsed out first, and the bits left over will be treated as arguments



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/mothership.rb', line 26

def start(argv)
  global_parser = Parser.new(@@global)
  name, *argv = global_parser.parse_flags(argv, @@commands)

  global = new(@@global)
  global.input = Inputs.new(@@global, global, {}, global_parser.given)

  return global.default_action unless name

  cmdname = name.gsub("-", "_").to_sym

  cmd = @@commands[cmdname]
  return global.unknown_command(cmdname) unless cmd

  ctx = cmd.context.new

  # make global input available for those wrapping execute
  ctx.input = global.input

  ctx.execute(cmd, argv, global.input)

  code = @@exit_status

  # reset exit status
  @@exit_status = 0

  exit code
end

Instance Method Details

#default_actionObject



18
19
20
# File 'lib/mothership/help/commands.rb', line 18

def default_action
  invoke :help
end

#execute(cmd, argv, global = {}) ⇒ Object



54
55
56
57
58
59
60
61
62
# File 'lib/mothership/base.rb', line 54

def execute(cmd, argv, global = {})
  cmd.invoke({}, Parser.new(cmd).parse_argv(argv), global)
rescue Mothership::Error => e
  $stderr.puts e
  $stderr.puts ""
  Mothership::Help.command_usage(cmd, $stderr)

  exit_status 1
end

#exit_status(num) ⇒ Object

set the exit status



61
62
63
# File 'lib/mothership.rb', line 61

def exit_status(num)
  @@exit_status = num
end

#filter(tag, val) ⇒ Object

filter a value through any plugins



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/mothership/callbacks.rb', line 51

def filter(tag, val)
  if @@filters.key?(@command.name) &&
       @@filters[@command.name].key?(tag)
    @@filters[@command.name][tag].each do |f, ctx|
      val = ctx.instance_exec(val, &f)
    end
  end

  @command.filters[tag].each do |f, c|
    val = c.new.instance_exec(val, &f)
  end

  val
end

#helpObject



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/mothership/help/commands.rb', line 31

def help
  if name = input[:command]
    if cmd = @@commands[name.gsub("-", "_").to_sym]
      Mothership::Help.command_help(cmd)
    else
      unknown_command(name)
    end
  elsif Help.has_groups?
    unless input[:all]
      puts "Showing basic command set. Run with 'help --all' to list all commands."
      puts ""
    end

    Mothership::Help.print_help_groups(@@global, input[:all])
  else
    Mothership::Help.basic_help(@@commands, @@global)
  end
end

#interact(input = nil, *args) ⇒ Object

explicitly perform the interaction of an input

if no input specified, assume current input and reuse the arguments

example:

input(:foo, :default => proc { |foos|
        foos.find { |f| f.name == "XYZ" } ||
          interact
      }) { |foos|
  ask("Foo?", :choices => foos, :display => proc(&:name))
}


90
91
92
93
94
95
96
97
98
# File 'lib/mothership/base.rb', line 90

def interact(input = nil, *args)
  unless input
    input, args = @input.current_input
  end

  raise "no active input" unless input

  @input.interact(input, self, *args)
end

#invoke(name, inputs = {}, given = {}, global = @input ? @input.global : {}) ⇒ Object

invoke a command with inputs



70
71
72
73
74
75
76
77
# File 'lib/mothership/base.rb', line 70

def invoke(
    name, inputs = {}, given = {}, global = @input ? @input.global : {})
  if cmd = @@commands[name]
    cmd.invoke(inputs, given, global)
  else
    unknown_command(name)
  end
end

#run(name) ⇒ Object

wrap this with your error handling/etc.



65
66
67
# File 'lib/mothership/base.rb', line 65

def run(name)
  send(name)
end

#unknown_command(name) ⇒ Object



22
23
24
25
26
# File 'lib/mothership/help/commands.rb', line 22

def unknown_command(name)
  $stderr.print "Unknown command '#{name.to_s.gsub("_", "-")}'. "
  $stderr.puts "See 'help' for available commands."
  exit_status 1
end

#with_filters(filters) ⇒ Object

temporary dynamically-scoped filters



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/mothership/callbacks.rb', line 67

def with_filters(filters)
  filters.each do |cmd, callbacks|
    callbacks.each do |tag, callback|
      @@filters[cmd][tag] << [callback, self]
    end
  end

  yield
ensure
  filters.each do |cmd, callbacks|
    callbacks.each do |tag, callback|
      @@filters[cmd][tag].pop
      @@filters[cmd].delete(tag) if @@filters[cmd][tag].empty?
    end

    @@filters.delete(cmd) if @@filters[cmd].empty?
  end
end