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.
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