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 +31 -8
- data/VERSION +1 -1
- data/cli.gemspec +2 -2
- data/examples/ls +5 -3
- data/lib/cli.rb +45 -16
- data/lib/cli/dsl.rb +21 -9
- data/lib/cli/options.rb +4 -0
- data/spec/option_spec.rb +41 -0
- data/spec/usage_spec.rb +2 -2
- metadata +4 -4
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
|
5
|
+
CLI supports following specifiers:
|
6
6
|
|
7
|
-
*
|
8
|
-
*
|
9
|
-
*
|
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
|
-
|
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
|
-
|
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
|
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.
|
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.
|
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-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
217
|
-
argument.default
|
240
|
+
value = if arguments.mandatory.length >= argv.length and argument.has_default?
|
241
|
+
argument.default
|
218
242
|
else
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
-
|
295
|
-
|
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
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
|
-
|
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:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 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-
|
18
|
+
date: 2011-12-21 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
type: :development
|