cli 0.0.2 → 0.0.3
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/VERSION +1 -1
- data/cli.gemspec +1 -1
- data/lib/cli.rb +86 -52
- data/spec/cli_spec.rb +52 -13
- metadata +3 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/cli.gemspec
CHANGED
data/lib/cli.rb
CHANGED
@@ -7,12 +7,25 @@ class CLI
|
|
7
7
|
end
|
8
8
|
|
9
9
|
class Parsed < OpenStruct
|
10
|
-
def
|
10
|
+
def value(argument, value)
|
11
11
|
send((argument.name.to_s + '=').to_sym, argument.cast(value))
|
12
12
|
end
|
13
|
+
|
14
|
+
def set(argument)
|
15
|
+
send((argument.name.to_s + '=').to_sym, true)
|
16
|
+
end
|
13
17
|
end
|
14
18
|
|
15
19
|
module Options
|
20
|
+
class Base
|
21
|
+
def initialize(name, options = {})
|
22
|
+
@name = name
|
23
|
+
@options = options
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :name
|
27
|
+
end
|
28
|
+
|
16
29
|
module Cast
|
17
30
|
def cast(value)
|
18
31
|
begin
|
@@ -43,14 +56,23 @@ class CLI
|
|
43
56
|
@options[:description]
|
44
57
|
end
|
45
58
|
end
|
46
|
-
end
|
47
59
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
60
|
+
module Value
|
61
|
+
def default
|
62
|
+
@options[:default]
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_default?
|
66
|
+
@options.member? :default
|
67
|
+
end
|
68
|
+
|
69
|
+
def optional?
|
70
|
+
has_default?
|
71
|
+
end
|
52
72
|
end
|
73
|
+
end
|
53
74
|
|
75
|
+
class STDINHandling < Options::Base
|
54
76
|
include Options::Cast
|
55
77
|
include Options::Description
|
56
78
|
|
@@ -59,40 +81,18 @@ class CLI
|
|
59
81
|
end
|
60
82
|
end
|
61
83
|
|
62
|
-
class Argument
|
63
|
-
|
64
|
-
@name = name
|
65
|
-
@options = {
|
66
|
-
:cast => String
|
67
|
-
}.merge(options)
|
68
|
-
end
|
69
|
-
|
70
|
-
attr_reader :name
|
71
|
-
|
84
|
+
class Argument < Options::Base
|
85
|
+
include Options::Value
|
72
86
|
include Options::Cast
|
73
87
|
include Options::Description
|
74
88
|
|
75
|
-
def default
|
76
|
-
@options[:default]
|
77
|
-
end
|
78
|
-
|
79
|
-
def has_default?
|
80
|
-
@options.member? :default
|
81
|
-
end
|
82
|
-
|
83
|
-
def optional?
|
84
|
-
has_default?
|
85
|
-
end
|
86
|
-
|
87
89
|
def to_s
|
88
90
|
name.to_s.tr('_', '-')
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
92
|
-
class
|
93
|
-
|
94
|
-
has_default? or not @options[:required]
|
95
|
-
end
|
94
|
+
class Switch < Options::Base
|
95
|
+
include Options::Description
|
96
96
|
|
97
97
|
def has_short?
|
98
98
|
@options.member? :short
|
@@ -115,11 +115,20 @@ class CLI
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
+
class Option < Switch
|
119
|
+
include Options::Value
|
120
|
+
include Options::Cast
|
121
|
+
|
122
|
+
def optional?
|
123
|
+
has_default? or not @options[:required]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
118
127
|
def initialize(&block)
|
119
128
|
#TODO: optoins should be in own class?
|
120
|
-
@
|
121
|
-
@
|
122
|
-
@
|
129
|
+
@switches = []
|
130
|
+
@switches_long = {}
|
131
|
+
@switches_short = {}
|
123
132
|
@options_default = []
|
124
133
|
@options_required = []
|
125
134
|
@arguments = []
|
@@ -139,11 +148,18 @@ class CLI
|
|
139
148
|
@arguments << Argument.new(name, options)
|
140
149
|
end
|
141
150
|
|
151
|
+
def switch(name, options = {})
|
152
|
+
o = Switch.new(name, options)
|
153
|
+
@switches << o
|
154
|
+
@switches_long[name] = o
|
155
|
+
@switches_short[o.short] = o if o.has_short?
|
156
|
+
end
|
157
|
+
|
142
158
|
def option(name, options = {})
|
143
159
|
o = Option.new(name, options)
|
144
|
-
@
|
145
|
-
@
|
146
|
-
@
|
160
|
+
@switches << o
|
161
|
+
@switches_long[name] = o
|
162
|
+
@switches_short[o.short] = o if o.has_short?
|
147
163
|
@options_default << o if o.has_default?
|
148
164
|
@options_required << o unless o.optional?
|
149
165
|
end
|
@@ -161,25 +177,27 @@ class CLI
|
|
161
177
|
|
162
178
|
# set defaults
|
163
179
|
@options_default.each do |o|
|
164
|
-
parsed.
|
180
|
+
parsed.value(o, o.default)
|
165
181
|
end
|
166
182
|
|
167
183
|
# process switches
|
168
184
|
options_required = @options_required.dup
|
169
185
|
while argv.first =~ /^-/
|
170
|
-
|
171
|
-
|
172
|
-
@
|
186
|
+
arg = argv.shift
|
187
|
+
switch = if arg =~ /^--/
|
188
|
+
@switches_long[arg.sub(/^--/, '').tr('-', '_').to_sym]
|
173
189
|
else
|
174
|
-
@
|
190
|
+
@switches_short[arg.sub(/^-/, '').tr('-', '_').to_sym]
|
175
191
|
end
|
176
192
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
193
|
+
raise ParsingError, "unknonw switch: #{arg}" unless switch
|
194
|
+
|
195
|
+
if switch.kind_of? Option
|
196
|
+
value = argv.shift or raise ParsingError, "missing option argument: #{switch}"
|
197
|
+
parsed.value(switch, value)
|
198
|
+
options_required.delete(switch)
|
181
199
|
else
|
182
|
-
|
200
|
+
parsed.set(switch)
|
183
201
|
end
|
184
202
|
end
|
185
203
|
|
@@ -195,7 +213,7 @@ class CLI
|
|
195
213
|
argv.shift or raise ParsingError, "missing argument: #{argument}"
|
196
214
|
end
|
197
215
|
|
198
|
-
parsed.
|
216
|
+
parsed.value(argument, value)
|
199
217
|
end
|
200
218
|
|
201
219
|
# process stdin
|
@@ -218,10 +236,15 @@ class CLI
|
|
218
236
|
end
|
219
237
|
|
220
238
|
def usage(msg = nil)
|
239
|
+
switches = @switches.select{|s| s.class == Switch}
|
240
|
+
options = @switches.select{|s| s.class == Option}
|
241
|
+
|
221
242
|
out = StringIO.new
|
222
243
|
out.puts msg if msg
|
223
244
|
out.print "Usage: #{File.basename $0}"
|
224
|
-
out.print ' [options]'
|
245
|
+
out.print ' [switches|options]' if not switches.empty? and not options.empty?
|
246
|
+
out.print ' [switches]' if not switches.empty? and options.empty?
|
247
|
+
out.print ' [options]' if switches.empty? and not options.empty?
|
225
248
|
out.print ' ' + @arguments.map{|a| a.to_s}.join(' ') unless @arguments.empty?
|
226
249
|
out.print " < #{@stdin_handling}" if @stdin_handling
|
227
250
|
|
@@ -233,9 +256,20 @@ class CLI
|
|
233
256
|
out.puts " #{@stdin_handling} - #{@stdin_handling.description}"
|
234
257
|
end
|
235
258
|
|
236
|
-
unless
|
259
|
+
unless switches.empty?
|
260
|
+
out.puts "Switches:"
|
261
|
+
switches.each do |s|
|
262
|
+
out.print ' '
|
263
|
+
out.print s.switch
|
264
|
+
out.print " (#{s.switch_short})" if s.has_short?
|
265
|
+
out.print " - #{s.description}" if s.description?
|
266
|
+
out.puts
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
unless options.empty?
|
237
271
|
out.puts "Options:"
|
238
|
-
|
272
|
+
options.each do |o|
|
239
273
|
out.print ' '
|
240
274
|
out.print o.switch
|
241
275
|
out.print " (#{o.switch_short})" if o.has_short?
|
data/spec/cli_spec.rb
CHANGED
@@ -149,6 +149,36 @@ EOF
|
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
152
|
+
describe 'switch handling' do
|
153
|
+
it "should handle long switch names" do
|
154
|
+
ps = CLI.new do
|
155
|
+
switch :location
|
156
|
+
switch :unset
|
157
|
+
end.parse(['--location'])
|
158
|
+
ps.location.should be_true
|
159
|
+
ps.unset.should be_nil
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should handle short switch names" do
|
163
|
+
ps = CLI.new do
|
164
|
+
switch :location, :short => :l
|
165
|
+
switch :unset, :short => :u
|
166
|
+
end.parse(['-l'])
|
167
|
+
ps.location.should be_true
|
168
|
+
ps.unset.should be_nil
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should raise error on unrecognized switch" do
|
172
|
+
ps = CLI.new do
|
173
|
+
option :location
|
174
|
+
end
|
175
|
+
|
176
|
+
lambda {
|
177
|
+
ps.parse(['--xxx'])
|
178
|
+
}.should raise_error CLI::ParsingError
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
152
182
|
describe 'option handling' do
|
153
183
|
it "should handle long option names" do
|
154
184
|
ps = CLI.new do
|
@@ -210,16 +240,6 @@ EOF
|
|
210
240
|
ps.gold.should be_nil
|
211
241
|
end
|
212
242
|
|
213
|
-
it "should raise error on unrecognized switch" do
|
214
|
-
ps = CLI.new do
|
215
|
-
option :location
|
216
|
-
end
|
217
|
-
|
218
|
-
lambda {
|
219
|
-
ps.parse(['--xxx', 'singapore'])
|
220
|
-
}.should raise_error CLI::ParsingError
|
221
|
-
end
|
222
|
-
|
223
243
|
it "should raise error on missing option argument" do
|
224
244
|
ps = CLI.new do
|
225
245
|
option :location
|
@@ -244,19 +264,20 @@ EOF
|
|
244
264
|
end
|
245
265
|
end
|
246
266
|
|
247
|
-
it "should handle options and then arguments" do
|
267
|
+
it "should handle options, switches and then arguments" do
|
248
268
|
ps = CLI.new do
|
249
269
|
option :location, :short => :l
|
250
270
|
option :group, :default => 'red'
|
251
271
|
option :power_up, :short => :p
|
252
272
|
option :speed, :short => :s, :cast => Integer
|
253
273
|
option :size
|
274
|
+
switch :debug
|
254
275
|
|
255
276
|
argument :log, :cast => Pathname
|
256
277
|
argument :magick, :default => 'word'
|
257
278
|
argument :test
|
258
279
|
argument :code, :cast => Integer, :default => '123'
|
259
|
-
end.parse(['-l', 'singapore', '--power-up', 'yes', '-s', '24', '--size', 'XXXL', '/tmp', 'hello'])
|
280
|
+
end.parse(['-l', 'singapore', '--power-up', 'yes', '-s', '24', '--debug', '--size', 'XXXL', '/tmp', 'hello'])
|
260
281
|
|
261
282
|
ps.group.should == 'red'
|
262
283
|
ps.power_up.should == 'yes'
|
@@ -267,6 +288,7 @@ EOF
|
|
267
288
|
ps.magick.should == 'word'
|
268
289
|
ps.test.should == 'hello'
|
269
290
|
ps.code.should == 123
|
291
|
+
ps.debug.should be_true
|
270
292
|
end
|
271
293
|
|
272
294
|
describe "usage and description" do
|
@@ -277,6 +299,7 @@ EOF
|
|
277
299
|
option :power_up, :short => :p
|
278
300
|
option :speed, :short => :s, :cast => Integer
|
279
301
|
option :size
|
302
|
+
switch :debug
|
280
303
|
|
281
304
|
argument :log, :cast => Pathname
|
282
305
|
argument :magick, :default => 'word'
|
@@ -309,6 +332,15 @@ EOF
|
|
309
332
|
ps.location.should == 'singapore'
|
310
333
|
end
|
311
334
|
|
335
|
+
it "should allow describing switches" do
|
336
|
+
ss = CLI.new do
|
337
|
+
switch :debug, :short => :d, :description => "enable debugging"
|
338
|
+
switch :logging
|
339
|
+
end
|
340
|
+
|
341
|
+
ss.usage.should include("enable debugging")
|
342
|
+
end
|
343
|
+
|
312
344
|
it "should allow describing options" do
|
313
345
|
ss = CLI.new do
|
314
346
|
option :location, :short => :l, :description => "place where server is located"
|
@@ -363,6 +395,9 @@ EOF
|
|
363
395
|
u = CLI.new do
|
364
396
|
description 'Log file processor'
|
365
397
|
stdin :log_data, :cast => YAML, :description => "YAML formatted log data"
|
398
|
+
switch :debug, :short => :d, :description => "enable debugging"
|
399
|
+
switch :logging, :short => :l
|
400
|
+
switch :run
|
366
401
|
option :location, :short => :l, :description => "place where server is located"
|
367
402
|
option :group, :default => 'red'
|
368
403
|
option :power_up, :short => :p
|
@@ -381,10 +416,14 @@ EOF
|
|
381
416
|
#puts u
|
382
417
|
|
383
418
|
u.should == <<EOS
|
384
|
-
Usage: rspec [options] log magick string number code illegal-prime < log-data
|
419
|
+
Usage: rspec [switches|options] log magick string number code illegal-prime < log-data
|
385
420
|
Log file processor
|
386
421
|
Input:
|
387
422
|
log-data - YAML formatted log data
|
423
|
+
Switches:
|
424
|
+
--debug (-d) - enable debugging
|
425
|
+
--logging (-l)
|
426
|
+
--run
|
388
427
|
Options:
|
389
428
|
--location (-l) - place where server is located
|
390
429
|
--group [red]
|
metadata
CHANGED