Module: Clio::Commandable

Defined in:
lib/clio/commandable.rb

Overview

Commandable Mixin

The Commandable mixin is a very quick and and easy way to make almost any class usable via a command line interface. It simply uses writer methods as option setters, and the first command line argument as the method to call, with the subsequent arguments passed to the method.

The only limitation of this approach (besides the weak control of the process) is that required options must be specified with the key=value notation.

class X
  include Clio::Commandable

  attr_accessor :quiet

  def bread(*args)
    ["BREAD", quiet, *args]
  end

  def butter(*args)
    ["BUTTER", quiet, *args]
  end
end

x = X.new

x.execute_command("butter yum")
=> ["BUTTER", nil, "yum"]

x.execute_command("bread --quiet")
=> ["BUTTER", true]

Commandable also defines #command_missing and #option_missing, which you can override to provide suitable results.

TODO: Maybe command_missing is redundant, and method_missing would suffice?

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.parse(obj, argv) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/clio/commandable.rb', line 86

def parse(obj, argv)
  case argv
  when String
    require 'shellwords'
    argv = Shellwords.shellwords(argv)
  else
    argv = argv.dup
  end

  argv = argv.dup
  args, opts, i = [], {}, 0
  while argv.size > 0
    case opt = argv.shift
    when /=/
      parse_equal(obj, opt, argv)
    when /^--/
      parse_option(obj, opt, argv)
    when /^-/
      parse_flags(obj, opt, argv)
    else
      args << opt
    end
  end
  return args
end

.parse_equal(obj, opt, argv) ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/clio/commandable.rb', line 113

def parse_equal(obj, opt, argv)
  if md = /^[-]*(.*?)=(.*?)$/.match(opt)
    x, v = md[1], md[2]
  else
    raise ArgumentError, "#{x}"
  end
  if obj.respond_to?("#{x}=")
    # TODO: to_b if 'true' or 'false' ?
    obj.send("#{x}=",v)
  else
    obj.option_missing(x, v) # argv?
  end
end

.parse_flags(obj, opt, args) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/clio/commandable.rb', line 138

def parse_flags(obj, opt, args)
  x = opt[1..-1]
  c = 0
  x.split(//).each do |k|
    if obj.respond_to?("#{k}=")
      obj.send("#{k}=",true)
    else
      obj.option_missing(x, argv)
    end
  end
end

.parse_option(obj, opt, argv) ⇒ Object



128
129
130
131
132
133
134
135
# File 'lib/clio/commandable.rb', line 128

def parse_option(obj, opt, argv)
  x = opt[2..-1]
  if obj.respond_to?("#{x}=")
    obj.send("#{x}=",true)
  else
    obj.option_missing(x, argv)
  end
end

.run(obj, argv = ARGV) ⇒ Object



69
70
71
72
73
74
75
76
77
# File 'lib/clio/commandable.rb', line 69

def run(obj, argv=ARGV)
  args = parse(obj, argv)
  subcmd = args.shift
  if subcmd && !obj.respond_to?("#{subcmd}=")
    obj.send(subcmd, *args)
  else
    obj.command_missing
  end
end

Instance Method Details

#command_missingObject

This is the fallback subcommand. Override this to provide a fallback when no command is given on the commandline.

Raises:



54
55
56
# File 'lib/clio/commandable.rb', line 54

def command_missing
  raise NoCommandError
end

#execute_command(argv = ARGV) ⇒ Object

Used to invoke the command.



48
49
50
# File 'lib/clio/commandable.rb', line 48

def execute_command(argv=ARGV)
  Commandable.run(self, argv)
end

#option_missing(opt, *argv) ⇒ Object

Override option_missing if needed. This receives the name of the option and the remaining arguments list. It must consume any argument it uses from the (begining of) the list.

Raises:



63
64
65
# File 'lib/clio/commandable.rb', line 63

def option_missing(opt, *argv)
  raise NoOptionError, opt
end