cli 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,11 +2,13 @@
2
2
 
3
3
  Command Line Interface gem allows you to quickly specify command argument parser that will automatically handle usage rendering, casting, default values and other stuff for you.
4
4
 
5
- CLI supports specifying:
5
+ CLI supports following specifiers:
6
6
 
7
- * switches - (`--name` or `-n`) binary operators, by default set to nil and when specified set to true
8
- * options - (`--name John` or `-n John`) switches that take value; default value can be given, otherwise default to nil
9
- * arguments - (`John`) capture command arguments that are not switches
7
+ * switch - (`--verbose` or `-v`) binary operators, by default set to nil, when specified set to true
8
+ * option - (`--name John` or `-n John`) switches that take value; default value can be specified, otherwise defaults to nil
9
+ * options - (`-n John -n Frank`) like option but can be used multiple times on command line; default value or array of values can be given, otherwise defaults to empty array
10
+ * argument - (`John`) capture single command; default value can be specified; raises error if not given
11
+ * arguments - (`John Frank`) capture multiple command arguments; defaults to empty array
10
12
  * stdin - if standard input is to be handled it can be mentioned in usage output; also stdin data casting is supported
11
13
 
12
14
  Each element can have description that will be visible in the usage output.
@@ -142,22 +144,27 @@ The `options` variable will contain:
142
144
  The `arguments` specifier matched value will always be an array of casted elements.
143
145
  Default and mandatory arguments will have priority on matching values (see specs for examples).
144
146
 
147
+ `options` specifier can be used to allow specifing same option multiple times.
148
+ The `options` specifier matched value will always be an array of casted elements or empty if option not specified.
149
+
145
150
  ```ruby
146
151
  require 'cli'
147
152
  require 'pathname'
148
153
 
149
- options = CLI.new do
154
+ values = CLI.new do
150
155
  description 'Lists content of directories'
151
156
  switch :long, :short => :l, :description => 'use long listing'
157
+ options :exclude, :short => :e, :description => 'exclude files from listing'
152
158
  arguments :directories, :cast => Pathname, :default => '.', :description => 'directories to list content of'
153
159
  end.parse!
154
160
 
155
- options.directories.each do |dir|
161
+ values.directories.each do |dir|
156
162
  next unless dir.directory?
157
163
  dir.each_entry do |e|
158
164
  next if e.to_s == '.' or e.to_s == '..'
159
165
  e = dir + e
160
- if options.long
166
+ next if values.exclude.include? e.to_s
167
+ if values.long
161
168
  puts "#{e.stat.uid}:#{e.stat.gid} #{e}"
162
169
  else
163
170
  puts e
@@ -168,11 +175,13 @@ end
168
175
 
169
176
  Example help message:
170
177
 
171
- Usage: ls [switches] [--] directories*
178
+ Usage: ls [switches|options] [--] directories*
172
179
  Lists content of directories
173
180
  Switches:
174
181
  --long (-l) - use long listing
175
182
  --help (-h) - display this help message
183
+ Options:
184
+ --exclude* (-e) - exclude files from listing
176
185
  Arguments:
177
186
  directories* [.] - directories to list content of
178
187
 
@@ -192,6 +201,20 @@ Prints:
192
201
  features
193
202
  ...
194
203
 
204
+ Excluding .git and .gitignore:
205
+
206
+ examples/ls -e .git -e .gitignore
207
+
208
+ Prints:
209
+
210
+ .document
211
+ .README.md.swp
212
+ .rspec
213
+ cli.gemspec
214
+ examples
215
+ features
216
+ ...
217
+
195
218
  With directory list:
196
219
 
197
220
  examples/ls *
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
data/cli.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "cli"
8
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
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"]
12
- s.date = "2011-12-20"
12
+ s.date = "2011-12-21"
13
13
  s.description = "Provides DSL for command-line options, switches and arguments parser and stdin handling with generated usage printer"
14
14
  s.email = "jpastuszek@gmail.com"
15
15
  s.extra_rdoc_files = [
data/examples/ls CHANGED
@@ -2,18 +2,20 @@
2
2
  require 'cli'
3
3
  require 'pathname'
4
4
 
5
- options = CLI.new do
5
+ values = CLI.new do
6
6
  description 'Lists content of directories'
7
7
  switch :long, :short => :l, :description => 'use long listing'
8
+ options :exclude, :short => :e, :description => 'exclude files from listing'
8
9
  arguments :directories, :cast => Pathname, :default => '.', :description => 'directories to list content of'
9
10
  end.parse!
10
11
 
11
- options.directories.each do |dir|
12
+ values.directories.each do |dir|
12
13
  next unless dir.directory?
13
14
  dir.each_entry do |e|
14
15
  next if e.to_s == '.' or e.to_s == '..'
15
16
  e = dir + e
16
- if options.long
17
+ next if values.exclude.include? e.to_s
18
+ if values.long
17
19
  puts "#{e.stat.uid}:#{e.stat.gid} #{e}"
18
20
  else
19
21
  puts e
data/lib/cli.rb CHANGED
@@ -94,6 +94,12 @@ class CLI
94
94
  send((argument.name.to_s + '=').to_sym, value)
95
95
  end
96
96
 
97
+ def append(argument, value)
98
+ v = (send(argument.name.to_s) or [])
99
+ v << value
100
+ send((argument.name.to_s + '=').to_sym, v)
101
+ end
102
+
97
103
  def set(argument)
98
104
  value(argument, true)
99
105
  end
@@ -162,6 +168,17 @@ class CLI
162
168
  @options << option_dsl
163
169
  end
164
170
 
171
+ def options(name, options = {})
172
+ option_dsl = DSL::Options.new(name, options)
173
+
174
+ raise ParserError::LongNameSpecifiedTwiceError.new('option', option_dsl) if @options.has_long?(option_dsl)
175
+ raise ParserError::LongNameSpecifiedTwiceError.new('switch and option', option_dsl) if @switches.has_long?(option_dsl)
176
+ raise ParserError::ShortNameSpecifiedTwiceError.new('option', option_dsl) if @options.has_short?(option_dsl)
177
+ raise ParserError::ShortNameSpecifiedTwiceError.new('switch and option', option_dsl) if @switches.has_short?(option_dsl)
178
+
179
+ @options << option_dsl
180
+ end
181
+
165
182
  def parse(_argv = ARGV, stdin = STDIN, stderr = STDERR)
166
183
  values = Values.new
167
184
  argv = _argv.dup
@@ -181,6 +198,11 @@ class CLI
181
198
  end
182
199
  end
183
200
 
201
+ # initialize multi options
202
+ @options.multiple.each do |o|
203
+ values.value(o, [])
204
+ end
205
+
184
206
  # set defaults
185
207
  @options.defaults.each do |o|
186
208
  values.value(o, o.cast(o.default))
@@ -196,7 +218,11 @@ class CLI
196
218
  values.set(switch)
197
219
  elsif option = @options.find(arg)
198
220
  value = argv.shift or raise ParsingError::MissingOptionValueError.new(option)
199
- values.value(option, option.cast(value))
221
+ if option.multiple?
222
+ values.append(option, option.cast(value))
223
+ else
224
+ values.value(option, option.cast(value))
225
+ end
200
226
  mandatory_options.delete(option)
201
227
  else
202
228
  raise ParsingError::UnknownSwitchError.new(arg) unless switch
@@ -210,24 +236,24 @@ class CLI
210
236
 
211
237
  # process arguments
212
238
  arguments = @arguments.dup
213
- mandatory_arguments_left = @arguments.mandatory.length
214
-
215
239
  while argument = arguments.shift
216
- value = if argv.length == mandatory_arguments_left and not argument.mandatory?
217
- argument.default # use defaults for optional arguments
240
+ value = if arguments.mandatory.length >= argv.length and argument.has_default?
241
+ argument.default
218
242
  else
219
- argv.shift or raise ParsingError::MandatoryArgumentNotSpecifiedError.new(argument)
220
- end
221
-
222
- if argument.multiple?
223
- value = [value] unless value.is_a? Array
224
- while argv.length > arguments.length
225
- value << argv.shift
243
+ raise ParsingError::MandatoryArgumentNotSpecifiedError.new(argument) if argv.empty?
244
+ arg = argv.shift
245
+
246
+ if argument.multiple?
247
+ v = [arg]
248
+ while argv.length > arguments.length
249
+ v << argv.shift
250
+ end
251
+ v
252
+ else
253
+ arg
226
254
  end
227
255
  end
228
256
 
229
- mandatory_arguments_left -= 1 if argument.mandatory?
230
-
231
257
  values.value(argument, argument.cast(value))
232
258
  end
233
259
 
@@ -291,8 +317,11 @@ class CLI
291
317
  unless @options.empty?
292
318
  out.puts "Options:"
293
319
  @options.each do |o|
294
- out.print ' '
295
- out.print o.switch
320
+ unless o.multiple?
321
+ out.print " #{o.switch}"
322
+ else
323
+ out.print " #{o.switch}*"
324
+ end
296
325
  out.print " (#{o.switch_short})" if o.has_short?
297
326
  out.print " [%s]" % o.default if o.has_default?
298
327
  out.print " - #{o.description}" if o.description?
data/lib/cli/dsl.rb CHANGED
@@ -66,6 +66,13 @@ class CLI
66
66
  end
67
67
  end
68
68
 
69
+ module MultiDefault
70
+ def default
71
+ value = @options[:default]
72
+ value.is_a?(Array) ? value.map{|v| v.to_s} : [value.to_s]
73
+ end
74
+ end
75
+
69
76
  class Input < DSL::Base
70
77
  include DSL::Cast
71
78
  include DSL::Description
@@ -90,6 +97,8 @@ class CLI
90
97
  end
91
98
 
92
99
  class Arguments < Argument
100
+ include DSL::MultiDefault
101
+
93
102
  def cast(values)
94
103
  out = []
95
104
  values.each do |v|
@@ -98,15 +107,6 @@ class CLI
98
107
  out
99
108
  end
100
109
 
101
- def default
102
- value = @options[:default]
103
- if value.is_a? Array
104
- value.map{|v| v.to_s}
105
- else
106
- value.to_s
107
- end
108
- end
109
-
110
110
  def multiple?
111
111
  true
112
112
  end
@@ -151,6 +151,18 @@ class CLI
151
151
  def mandatory?
152
152
  not has_default? and @options[:required]
153
153
  end
154
+
155
+ def multiple?
156
+ false
157
+ end
158
+ end
159
+
160
+ class Options < Option
161
+ include DSL::MultiDefault
162
+
163
+ def multiple?
164
+ true
165
+ end
154
166
  end
155
167
  end
156
168
  end
data/lib/cli/options.rb CHANGED
@@ -8,5 +8,9 @@ class CLI::Options < CLI::Switches
8
8
  def mandatory
9
9
  select{|o| o.mandatory?}
10
10
  end
11
+
12
+ def multiple
13
+ select{|a| a.multiple?}
14
+ end
11
15
  end
12
16
 
data/spec/option_spec.rb CHANGED
@@ -26,6 +26,14 @@ describe CLI do
26
26
  ps.size.should == 24
27
27
  end
28
28
 
29
+ it "should support casting of multiple options" do
30
+ ps = CLI.new do
31
+ options :size, :cast => Integer
32
+ end.parse(['--size', '24', '--size', '10'])
33
+ ps.size.should be_a Array
34
+ ps.size.should == [24, 10]
35
+ end
36
+
29
37
  it "should support casting with lambda" do
30
38
  ps = CLI.new do
31
39
  option :size, :cast => lambda{|v| v.to_i + 2}
@@ -79,6 +87,13 @@ describe CLI do
79
87
  ps.gold.should be_nil
80
88
  end
81
89
 
90
+ it "not given option that can be specified multiple times should be an empty array" do
91
+ ps = CLI.new do
92
+ options :size, :cast => Integer
93
+ end.parse([])
94
+ ps.size.should == []
95
+ end
96
+
82
97
  it "should handle multiple long and short intermixed options" do
83
98
  ps = CLI.new do
84
99
  option :location, :short => :l
@@ -96,6 +111,32 @@ describe CLI do
96
111
  ps.gold.should be_nil
97
112
  end
98
113
 
114
+ it "should support options that can be specified multiple times" do
115
+ ps = CLI.new do
116
+ options :power_up, :short => :p
117
+ end.parse(['--power-up', 'fire'])
118
+ ps.power_up.should == ['fire']
119
+
120
+ ps = CLI.new do
121
+ options :power_up, :short => :p
122
+ end.parse(['--power-up', 'fire', '-p', 'water', '--power-up', 'air', '-p', 'ground'])
123
+ ps.power_up.should == ['fire', 'water', 'air', 'ground']
124
+ end
125
+
126
+ it "should support options that can be specified multiple times can have single default" do
127
+ ps = CLI.new do
128
+ options :power_up, :short => :p, :default => 'fire'
129
+ end.parse([])
130
+ ps.power_up.should == ['fire']
131
+ end
132
+
133
+ it "should support options that can be specified multiple times can have multiple defaults" do
134
+ ps = CLI.new do
135
+ options :power_up, :short => :p, :default => ['fire', 'air']
136
+ end.parse([])
137
+ ps.power_up.should == ['fire', 'air']
138
+ end
139
+
99
140
  it "should raise error if not symbol and optional hash is passed" do
100
141
  lambda {
101
142
  ps = CLI.new do
data/spec/usage_spec.rb CHANGED
@@ -261,7 +261,7 @@ describe CLI do
261
261
  switch :run
262
262
  option :location, :short => :r, :description => "place where server is located"
263
263
  option :group, :default => 'red'
264
- option :power_up, :short => :p
264
+ options :power_up, :short => :p
265
265
  option :speed, :short => :s, :cast => Integer
266
266
  option :the_number_of_the_beast, :short => :b, :cast => Integer, :default => 666, :description => "The number of the beast"
267
267
  option :size
@@ -289,7 +289,7 @@ Switches:
289
289
  Options:
290
290
  --location (-r) - place where server is located
291
291
  --group [red]
292
- --power-up (-p)
292
+ --power-up* (-p)
293
293
  --speed (-s)
294
294
  --the-number-of-the-beast (-b) [666] - The number of the beast
295
295
  --size
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: 23
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 0.2.0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jakub Pastuszek
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-12-20 00:00:00 Z
18
+ date: 2011-12-21 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  type: :development