cli 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/VERSION +1 -1
  2. data/cli.gemspec +1 -1
  3. data/lib/cli.rb +86 -52
  4. data/spec/cli_spec.rb +52 -13
  5. metadata +3 -3
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "cli"
8
- s.version = "0.0.2"
8
+ s.version = "0.0.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jakub Pastuszek"]
data/lib/cli.rb CHANGED
@@ -7,12 +7,25 @@ class CLI
7
7
  end
8
8
 
9
9
  class Parsed < OpenStruct
10
- def set(argument, value)
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
- class STDINHandling
49
- def initialize(name, options = {})
50
- @name = name
51
- @options = options
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
- def initialize(name, options = {})
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 Option < Argument
93
- def optional?
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
- @options = []
121
- @optoins_long = {}
122
- @optoins_short = {}
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
- @options << o
145
- @optoins_long[name] = o
146
- @optoins_short[o.short] = o if o.has_short?
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.set(o, o.default)
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
- switch = argv.shift
171
- option = if switch =~ /^--/
172
- @optoins_long[switch.sub(/^--/, '').tr('-', '_').to_sym]
186
+ arg = argv.shift
187
+ switch = if arg =~ /^--/
188
+ @switches_long[arg.sub(/^--/, '').tr('-', '_').to_sym]
173
189
  else
174
- @optoins_short[switch.sub(/^-/, '').tr('-', '_').to_sym]
190
+ @switches_short[arg.sub(/^-/, '').tr('-', '_').to_sym]
175
191
  end
176
192
 
177
- if option
178
- value = argv.shift or raise ParsingError, "missing option argument: #{option}"
179
- parsed.set(option, value)
180
- options_required.delete(option)
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
- raise ParsingError, "unknonw switch: #{switch}"
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.set(argument, value)
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]' unless @optoins_long.empty?
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 @optoins_long.empty?
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
- @options.each do |o|
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?
@@ -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
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cli
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jakub Pastuszek