cli 0.2.0 → 0.3.0

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