clio 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +170 -0
- data/HISTORY +11 -0
- data/MANIFEST +181 -0
- data/METADATA +18 -0
- data/NEWS +10 -0
- data/README +52 -0
- data/admin/config/reap.yaml +30 -0
- data/admin/depot/commandline.rb +219 -0
- data/admin/depot/multicommand.rb +403 -0
- data/admin/depot/test_multicommand.rb +40 -0
- data/admin/log/notes.xml +28 -0
- data/admin/log/stats.html +25 -0
- data/admin/log/syntax.log +0 -0
- data/admin/log/testunit.log +16 -0
- data/admin/pack/clio-0.0.1.gem +0 -0
- data/admin/share/reap/example.rb +7 -0
- data/admin/temps/lib/clio/about.rb.erb +4 -0
- data/lib/clio/ansicode.rb +319 -0
- data/lib/clio/command.rb +296 -0
- data/lib/clio/commandable.rb +195 -0
- data/lib/clio/commandline.rb +275 -0
- data/lib/clio/consoleutils.rb +117 -0
- data/lib/clio/errors.rb +16 -0
- data/lib/clio/option.rb +36 -0
- data/lib/clio/progressbar.rb +253 -0
- data/lib/clio/runmode.rb +126 -0
- data/lib/clio/string.rb +147 -0
- data/test/test_command.rb +42 -0
- data/test/test_commandline.rb +83 -0
- data/vendor/Console/Console.cpp +1203 -0
- data/vendor/Console/Console.rdoc +690 -0
- data/vendor/Console/Console_ANSI.rdoc +302 -0
- data/vendor/Console/HISTORY.txt +7 -0
- data/vendor/Console/INSTALL.txt +18 -0
- data/vendor/Console/Makefile +162 -0
- data/vendor/Console/README.txt +26 -0
- data/vendor/Console/doc/classes/Win32.html +115 -0
- data/vendor/Console/doc/classes/Win32/Console.html +650 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000001.html +31 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000002.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000003.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000004.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000005.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000006.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000007.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000008.html +24 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000009.html +44 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000010.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000011.html +33 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000012.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000013.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000014.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000015.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000016.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000017.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000018.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000019.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000020.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000021.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000022.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000023.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000024.html +35 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000025.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000026.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000027.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000028.html +31 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000029.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000030.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000031.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000032.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000033.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000034.html +25 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI.html +103 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.html +220 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000035.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000036.html +205 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000037.html +40 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000038.html +25 -0
- data/vendor/Console/doc/classes/Win32/Console/API.html +758 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000039.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000040.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000041.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000042.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000043.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000044.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000045.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000046.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000047.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000048.html +30 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000049.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000050.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000051.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000052.html +30 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000053.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000054.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000055.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000056.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000057.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000058.html +47 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000059.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000060.html +47 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000061.html +34 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000062.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000063.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000064.html +35 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000065.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000066.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000067.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000068.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000069.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000070.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000071.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000072.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000073.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000074.html +31 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000075.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000076.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000077.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000078.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000079.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000080.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/Constants.html +360 -0
- data/vendor/Console/doc/created.rid +1 -0
- data/vendor/Console/doc/files/Console_ANSI_rdoc.html +407 -0
- data/vendor/Console/doc/files/Console_cpp.html +104 -0
- data/vendor/Console/doc/files/Console_rdoc.html +964 -0
- data/vendor/Console/doc/files/lib/Win32/Console/ANSI_rb.html +123 -0
- data/vendor/Console/doc/files/lib/Win32/Console_rb.html +297 -0
- data/vendor/Console/doc/fr_class_index.html +32 -0
- data/vendor/Console/doc/fr_file_index.html +31 -0
- data/vendor/Console/doc/fr_method_index.html +106 -0
- data/vendor/Console/doc/index.html +24 -0
- data/vendor/Console/doc/rdoc-style.css +172 -0
- data/vendor/Console/extconf.rb +18 -0
- data/vendor/Console/lib/Term/ansicolor.rb +76 -0
- data/vendor/Console/lib/Win32/Console.rb +970 -0
- data/vendor/Console/lib/Win32/Console/ANSI.rb +305 -0
- data/vendor/Console/test/test_cursor.rb +9 -0
- data/vendor/Console/test/test_mouse.rb +6 -0
- data/vendor/Console/test/test_readinput.rb +62 -0
- data/vendor/Console/test/test_readoutput.rb +52 -0
- data/vendor/Console/test/test_sendevent.rb +17 -0
- data/vendor/Console/test/test_title.rb +14 -0
- data/vendor/Console/test/test_write.rb +36 -0
- metadata +253 -0
data/lib/clio/command.rb
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
require 'clio/errors'
|
|
2
|
+
require 'clio/option'
|
|
3
|
+
|
|
4
|
+
module Clio
|
|
5
|
+
|
|
6
|
+
# = Command
|
|
7
|
+
#
|
|
8
|
+
# Command is Clio's low-level command option parser solution.
|
|
9
|
+
# Despite being low-level, it is actually quite easy to used
|
|
10
|
+
# and integrates will with Ruby.
|
|
11
|
+
#
|
|
12
|
+
# The Command class does not to try overly determine your needs via a
|
|
13
|
+
# declarative options DSL, rather it just makes it easy for you to
|
|
14
|
+
# process options into a command class. It does this primarily by
|
|
15
|
+
# using a simple method naming trick. Methods with names starting
|
|
16
|
+
# with underscores (eg. _n or __name) are treated as options.
|
|
17
|
+
#
|
|
18
|
+
# Clio::Command encourages the command pattern (hence the name). So one
|
|
19
|
+
# class does one thing and one thing only. This helps ensure a robust
|
|
20
|
+
# design, albeit at the expense of churning a quick all-in-one solution.
|
|
21
|
+
#
|
|
22
|
+
# For a quicker solution to command line parsing have a look at
|
|
23
|
+
# Clio::Commandable or Clio::Commandline.
|
|
24
|
+
#
|
|
25
|
+
# Although it is low-level it does provide a single high-level "DSL"
|
|
26
|
+
# command for describing usage. This is purely a descriptive measure,
|
|
27
|
+
# and has no barring on the functionality. It is provided to ease
|
|
28
|
+
# the creation of help and command completion output.
|
|
29
|
+
#
|
|
30
|
+
# Simply specify:
|
|
31
|
+
#
|
|
32
|
+
# usage :optname, "options description", :type=>"TYPE", :default=>"DEFAULT"
|
|
33
|
+
#
|
|
34
|
+
# Here is an example of usage.
|
|
35
|
+
#
|
|
36
|
+
# MainCommand < Clio::Command
|
|
37
|
+
#
|
|
38
|
+
# usage :quiet, "keep it quiet?", :type=>:BOOLEAN, :default=>:FALSE
|
|
39
|
+
# usage :file, "what file to use", :type=>:FILE, :alias => :f
|
|
40
|
+
#
|
|
41
|
+
# # boolean flag
|
|
42
|
+
# def __quiet
|
|
43
|
+
# @quiet = true
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# # required option
|
|
47
|
+
# def __file(fname)
|
|
48
|
+
# @file = fname
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
51
|
+
# # one letter shortcut
|
|
52
|
+
# alias _f __flag
|
|
53
|
+
#
|
|
54
|
+
# # run command
|
|
55
|
+
# def call(*args)
|
|
56
|
+
# subcommand = args.shift
|
|
57
|
+
# case subcommand
|
|
58
|
+
# when 'show'
|
|
59
|
+
# puts File.read(@file)
|
|
60
|
+
# when 'rshow'
|
|
61
|
+
# puts File.read(@file).reverse
|
|
62
|
+
# else
|
|
63
|
+
# puts "Unknown subcommand"
|
|
64
|
+
# end
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# MainCommand.run
|
|
70
|
+
#
|
|
71
|
+
# You can chain subcommands together via a case statement like
|
|
72
|
+
# that given above. Eg.
|
|
73
|
+
#
|
|
74
|
+
# case subcommand
|
|
75
|
+
# when 'build'
|
|
76
|
+
# BuildCommand.run(args)
|
|
77
|
+
# ...
|
|
78
|
+
#
|
|
79
|
+
# TODO: Support passing a string or *args, opts in place of ARGV.
|
|
80
|
+
#
|
|
81
|
+
class Command
|
|
82
|
+
|
|
83
|
+
# Used to invoke this command.
|
|
84
|
+
def run(argv=ARGV)
|
|
85
|
+
args = self.class.parse(self, argv)
|
|
86
|
+
call(*args)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# This is command function. Override this
|
|
90
|
+
# to do what the command does.
|
|
91
|
+
def call(*args)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Override option_missing if needed.
|
|
95
|
+
# This receives the name of the option and
|
|
96
|
+
# the remaining arguments list. It must consume
|
|
97
|
+
# any argument it uses from the (begining of)
|
|
98
|
+
# the list.
|
|
99
|
+
def option_missing(opt, *argv)
|
|
100
|
+
raise NoOptionError, opt
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class << self
|
|
104
|
+
|
|
105
|
+
#
|
|
106
|
+
def parse(obj, argv=ARGV)
|
|
107
|
+
argv = argv.dup
|
|
108
|
+
args, opts, i = [], {}, 0
|
|
109
|
+
while argv.size > 0
|
|
110
|
+
case opt = argv.shift
|
|
111
|
+
when /=/
|
|
112
|
+
parse_equal(obj, opt, argv)
|
|
113
|
+
when /^--/
|
|
114
|
+
parse_option(obj, opt, argv)
|
|
115
|
+
when /^-/
|
|
116
|
+
parse_flags(obj, opt, argv)
|
|
117
|
+
else
|
|
118
|
+
args << opt
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
return args
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
#
|
|
125
|
+
def parse_equal(obj, opt, argv)
|
|
126
|
+
if md = /^[-]*(.*?)=(.*?)$/.match(opt)
|
|
127
|
+
x, v = md[1], md[2]
|
|
128
|
+
else
|
|
129
|
+
raise ArgumentError, "#{x}"
|
|
130
|
+
end
|
|
131
|
+
if obj.respond_to?("__#{x}")
|
|
132
|
+
obj.send("__#{x}",v)
|
|
133
|
+
else
|
|
134
|
+
obj.option_missing(x, v) # argv?
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
#
|
|
139
|
+
def parse_option(obj, opt, argv)
|
|
140
|
+
x = opt[2..-1]
|
|
141
|
+
if obj.respond_to?("__#{x}")
|
|
142
|
+
m = obj.method("__#{x}")
|
|
143
|
+
if m.arity >= 0
|
|
144
|
+
a = []
|
|
145
|
+
m.arity.times{ a << argv.shift }
|
|
146
|
+
m.call(*a)
|
|
147
|
+
else
|
|
148
|
+
m.call
|
|
149
|
+
end
|
|
150
|
+
else
|
|
151
|
+
obj.option_missing(x, argv)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
#
|
|
156
|
+
def parse_flags(obj, opt, args)
|
|
157
|
+
x = opt[1..-1]
|
|
158
|
+
c = 0
|
|
159
|
+
x.split(//).each do |k|
|
|
160
|
+
if m = obj.method("_#{k}")
|
|
161
|
+
a = []
|
|
162
|
+
m.arity.times{ a << argv.shift }
|
|
163
|
+
m.call(*a)
|
|
164
|
+
else
|
|
165
|
+
obj.option_missing(x, argv)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Shortcut for
|
|
171
|
+
#
|
|
172
|
+
# Command.new.run()
|
|
173
|
+
#
|
|
174
|
+
def run(argv=ARGV)
|
|
175
|
+
new.run(argv)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def uses
|
|
179
|
+
@usage ||= []
|
|
180
|
+
@usage.collect do |u|
|
|
181
|
+
u.usage
|
|
182
|
+
end.join(' ')
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def usage(name, desc, opts)
|
|
186
|
+
@usage ||= []
|
|
187
|
+
@usage << Option.new(name, desc, opts)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
#def help
|
|
191
|
+
# #command_attrs.each do |k, o|
|
|
192
|
+
# # puts "%-20s %s" % [o.usage, o.description]
|
|
193
|
+
# #end
|
|
194
|
+
#end
|
|
195
|
+
|
|
196
|
+
end #class << self
|
|
197
|
+
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
=begin
|
|
201
|
+
# TODO: use clio/option
|
|
202
|
+
class UseOption
|
|
203
|
+
attr_reader :name
|
|
204
|
+
attr_accessor :type
|
|
205
|
+
attr_accessor :init
|
|
206
|
+
attr_accessor :desc
|
|
207
|
+
|
|
208
|
+
alias_method :default, :init
|
|
209
|
+
alias_method :description, :desc
|
|
210
|
+
|
|
211
|
+
def initialize(name, desc, opts)
|
|
212
|
+
@name = name
|
|
213
|
+
@desc = desc
|
|
214
|
+
@type = opts[:type] || 'value'
|
|
215
|
+
@init = opts[:default] || opts[:init]
|
|
216
|
+
end
|
|
217
|
+
def usage
|
|
218
|
+
"--#{name}=#{type.to_s.upcase}"
|
|
219
|
+
end
|
|
220
|
+
def assert_valid(value)
|
|
221
|
+
raise "invalid" unless valid?(value)
|
|
222
|
+
end
|
|
223
|
+
def valid?(value)
|
|
224
|
+
validation ? validation.call(value) : true
|
|
225
|
+
end
|
|
226
|
+
def validation(&block)
|
|
227
|
+
@validation = block if block
|
|
228
|
+
@validation
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
=end
|
|
232
|
+
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
=begin :spec:
|
|
237
|
+
|
|
238
|
+
require 'quarry/spec'
|
|
239
|
+
|
|
240
|
+
class MyCommand < Clio::Command
|
|
241
|
+
attr_reader :size, :quiet, :file
|
|
242
|
+
|
|
243
|
+
def initialize
|
|
244
|
+
@file = 'hey.txt' # default
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
use :quiet, "supress standard output", :type => :boolean
|
|
248
|
+
|
|
249
|
+
def __quiet(bool=true)
|
|
250
|
+
@quiet = bool ? true : bool
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
use :size, "what size will it be?", :type => :integer, :default => '0'
|
|
254
|
+
|
|
255
|
+
def __size(integer)
|
|
256
|
+
@size = integer.to_i
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
use :file, "where to store the stuff", :init => 'hey.txt'
|
|
260
|
+
|
|
261
|
+
def __file(fname)
|
|
262
|
+
@file = fname
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def call(*args)
|
|
266
|
+
@args = args
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
Quarry.spec "Command" do
|
|
271
|
+
before do
|
|
272
|
+
@mc = MyCommand.new
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
demonstrate 'boolean option' do
|
|
276
|
+
@mc.run(['--quiet'])
|
|
277
|
+
@mc.quiet.assert == true
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
demonstrate 'integer option' do
|
|
281
|
+
@mc.run(['--size=4'])
|
|
282
|
+
@mc.size.assert == 4
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
demonstrate 'default value' do
|
|
286
|
+
@mc.run([''])
|
|
287
|
+
@mc.file.assert == 'hey.txt'
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
demonstrate 'usage output' do
|
|
291
|
+
MyCommand.usage.assert == "--quiet=BOOLEAN --size=INTEGER --file=VALUE"
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
=end
|
|
296
|
+
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require 'clio/errors'
|
|
2
|
+
|
|
3
|
+
module Clio
|
|
4
|
+
|
|
5
|
+
# = Commandable mixin
|
|
6
|
+
#
|
|
7
|
+
# The Commandable mixin is a very quick and and easy
|
|
8
|
+
# way to make almost any class usable via a command
|
|
9
|
+
# line interface. It simply uses writer methods as
|
|
10
|
+
# option setters, and the first command line argument
|
|
11
|
+
# as the method to call, with the subsequent arguments
|
|
12
|
+
# passed to the method.
|
|
13
|
+
#
|
|
14
|
+
# The only limitation of this approach (besides the weak
|
|
15
|
+
# control of the process) is that required options must
|
|
16
|
+
# be specified with the key=value notation.
|
|
17
|
+
#
|
|
18
|
+
# class X
|
|
19
|
+
# include Clio::Commandable
|
|
20
|
+
#
|
|
21
|
+
# attr_accessor :quiet
|
|
22
|
+
#
|
|
23
|
+
# def bread(*args)
|
|
24
|
+
# ["BREAD", quiet, *args]
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# def butter(*args)
|
|
28
|
+
# ["BUTTER", quiet, *args]
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# x = X.new
|
|
33
|
+
#
|
|
34
|
+
# x.execute_command("butter yum")
|
|
35
|
+
# => ["BUTTER", nil, "yum"]
|
|
36
|
+
#
|
|
37
|
+
# x.execute_command("bread --quiet")
|
|
38
|
+
# => ["BUTTER", true]
|
|
39
|
+
#
|
|
40
|
+
# Commandable also defines #command_missing and #option_missing,
|
|
41
|
+
# which you can override to provide suitable results.
|
|
42
|
+
#
|
|
43
|
+
module Commandable
|
|
44
|
+
|
|
45
|
+
# Used to invoke the command.
|
|
46
|
+
def execute_command(argv=ARGV)
|
|
47
|
+
Commandable.run(self, argv)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# This is the fallback subcommand. Override this to provide
|
|
51
|
+
# a fallback when no command is given on the commandline.
|
|
52
|
+
def command_missing
|
|
53
|
+
raise NoMethodError
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Override option_missing if needed.
|
|
57
|
+
# This receives the name of the option and
|
|
58
|
+
# the remaining arguments list. It must consume
|
|
59
|
+
# any argument it uses from the (begining of)
|
|
60
|
+
# the list.
|
|
61
|
+
def option_missing(opt, *argv)
|
|
62
|
+
raise NoOptionError, opt
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class << self
|
|
66
|
+
|
|
67
|
+
def run(obj, argv=ARGV)
|
|
68
|
+
args = parse(obj, argv)
|
|
69
|
+
subcmd = args.shift
|
|
70
|
+
if subcmd && !obj.respond_to?("#{subcmd}=")
|
|
71
|
+
obj.send(subcmd, *args)
|
|
72
|
+
else
|
|
73
|
+
obj.command_missing
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
#def run(obj)
|
|
78
|
+
# methname, args = *parse(obj)
|
|
79
|
+
# meth = obj.method(methname)
|
|
80
|
+
# meth.call(*args)
|
|
81
|
+
#end
|
|
82
|
+
|
|
83
|
+
#
|
|
84
|
+
def parse(obj, argv)
|
|
85
|
+
case argv
|
|
86
|
+
when String
|
|
87
|
+
require 'shellwords'
|
|
88
|
+
argv = Shellwords.shellwords(argv)
|
|
89
|
+
else
|
|
90
|
+
argv = argv.dup
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
argv = argv.dup
|
|
94
|
+
args, opts, i = [], {}, 0
|
|
95
|
+
while argv.size > 0
|
|
96
|
+
case opt = argv.shift
|
|
97
|
+
when /=/
|
|
98
|
+
parse_equal(obj, opt, argv)
|
|
99
|
+
when /^--/
|
|
100
|
+
parse_option(obj, opt, argv)
|
|
101
|
+
when /^-/
|
|
102
|
+
parse_flags(obj, opt, argv)
|
|
103
|
+
else
|
|
104
|
+
args << opt
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
return args
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
def parse_equal(obj, opt, argv)
|
|
112
|
+
if md = /^[-]*(.*?)=(.*?)$/.match(opt)
|
|
113
|
+
x, v = md[1], md[2]
|
|
114
|
+
else
|
|
115
|
+
raise ArgumentError, "#{x}"
|
|
116
|
+
end
|
|
117
|
+
if obj.respond_to?("#{x}=")
|
|
118
|
+
# TODO: to_b if 'true' or 'false' ?
|
|
119
|
+
obj.send("#{x}=",v)
|
|
120
|
+
else
|
|
121
|
+
obj.option_missing(x, v) # argv?
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
#
|
|
126
|
+
def parse_option(obj, opt, argv)
|
|
127
|
+
x = opt[2..-1]
|
|
128
|
+
if obj.respond_to?("#{x}=")
|
|
129
|
+
obj.send("#{x}=",true)
|
|
130
|
+
else
|
|
131
|
+
obj.option_missing(x, argv)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
#
|
|
136
|
+
def parse_flags(obj, opt, args)
|
|
137
|
+
x = opt[1..-1]
|
|
138
|
+
c = 0
|
|
139
|
+
x.split(//).each do |k|
|
|
140
|
+
if obj.respond_to?("#{k}=")
|
|
141
|
+
obj.send("#{k}=",true)
|
|
142
|
+
else
|
|
143
|
+
obj.option_missing(x, argv)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
end #class << self
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
=begin SPEC
|
|
157
|
+
|
|
158
|
+
require 'quarry/spec'
|
|
159
|
+
|
|
160
|
+
class X
|
|
161
|
+
include Clio::Commandable
|
|
162
|
+
|
|
163
|
+
attr_accessor :file
|
|
164
|
+
attr_accessor :quiet
|
|
165
|
+
|
|
166
|
+
attr :cmd
|
|
167
|
+
|
|
168
|
+
#
|
|
169
|
+
def bread(*args)
|
|
170
|
+
@cmd = "BREAD"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
#
|
|
174
|
+
def butter(*args)
|
|
175
|
+
@cmd = "BUTTER"
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
Quarry.spec "Commandable" do
|
|
180
|
+
|
|
181
|
+
verify "first command runs" do
|
|
182
|
+
x = X.new
|
|
183
|
+
x.execute_command("bread")
|
|
184
|
+
x.cmd.assert == "BREAD"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
verify "second command runs" do
|
|
188
|
+
x = X.new
|
|
189
|
+
x.execute_command("butter")
|
|
190
|
+
x.cmd.assert == "BUTTER"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
=end
|
|
195
|
+
|