cli 0.0.3 → 0.0.4
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/VERSION +1 -1
- data/cli.gemspec +6 -2
- data/lib/cli.rb +115 -148
- data/lib/cli/arguments.rb +6 -0
- data/lib/cli/dsl.rb +121 -0
- data/lib/cli/options.rb +19 -0
- data/lib/cli/switches.rb +41 -0
- data/spec/cli_spec.rb +163 -8
- metadata +8 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
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.0.
|
8
|
+
s.version = "0.0.4"
|
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-15"
|
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 = [
|
@@ -30,6 +30,10 @@ Gem::Specification.new do |s|
|
|
30
30
|
"features/step_definitions/cli_steps.rb",
|
31
31
|
"features/support/env.rb",
|
32
32
|
"lib/cli.rb",
|
33
|
+
"lib/cli/arguments.rb",
|
34
|
+
"lib/cli/dsl.rb",
|
35
|
+
"lib/cli/options.rb",
|
36
|
+
"lib/cli/switches.rb",
|
33
37
|
"spec/cli_spec.rb",
|
34
38
|
"spec/spec_helper.rb"
|
35
39
|
]
|
data/lib/cli.rb
CHANGED
@@ -2,136 +2,102 @@ require 'ostruct'
|
|
2
2
|
require 'stringio'
|
3
3
|
require 'yaml'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
class Parsed < OpenStruct
|
10
|
-
def value(argument, value)
|
11
|
-
send((argument.name.to_s + '=').to_sym, argument.cast(value))
|
12
|
-
end
|
13
|
-
|
14
|
-
def set(argument)
|
15
|
-
send((argument.name.to_s + '=').to_sym, true)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module Options
|
20
|
-
class Base
|
21
|
-
def initialize(name, options = {})
|
22
|
-
@name = name
|
23
|
-
@options = options
|
24
|
-
end
|
5
|
+
require 'cli/dsl'
|
6
|
+
require 'cli/arguments'
|
7
|
+
require 'cli/switches'
|
8
|
+
require 'cli/options'
|
25
9
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
begin
|
32
|
-
cast_class = @options[:cast]
|
33
|
-
if cast_class == nil
|
34
|
-
value
|
35
|
-
elsif cast_class == Integer
|
36
|
-
value.to_i
|
37
|
-
elsif cast_class == Float
|
38
|
-
value.to_f
|
39
|
-
elsif cast_class == YAML
|
40
|
-
YAML.load(value)
|
41
|
-
else
|
42
|
-
cast_class.new(value)
|
43
|
-
end
|
44
|
-
rescue => e
|
45
|
-
raise ParsingError, "failed to cast: #{@name} to type: #{@options[:cast].name}: #{e}"
|
46
|
-
end
|
10
|
+
class CLI
|
11
|
+
class ParserError < ArgumentError
|
12
|
+
class NameArgumetNotSymbolError < ParserError
|
13
|
+
def initialize(type, arg)
|
14
|
+
super("#{type} name has to be of type Symbol, got #{arg.class.name}")
|
47
15
|
end
|
48
16
|
end
|
49
17
|
|
50
|
-
|
51
|
-
def
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
def description
|
56
|
-
@options[:description]
|
18
|
+
class OptionsArgumentNotHashError < ParserError
|
19
|
+
def initialize(type, arg)
|
20
|
+
super("#{type} options has to be of type Hash, got #{arg.class.name}")
|
57
21
|
end
|
58
22
|
end
|
59
23
|
|
60
|
-
|
61
|
-
def
|
62
|
-
|
24
|
+
class ArgumentNameSpecifiedTwice < ParserError
|
25
|
+
def initialize(arg)
|
26
|
+
super("argument #{arg} specified twice")
|
63
27
|
end
|
28
|
+
end
|
64
29
|
|
65
|
-
|
66
|
-
|
30
|
+
class LongNameSpecifiedTwiceError < ParserError
|
31
|
+
def initialize(what, name)
|
32
|
+
super("#{what} #{name} specified twice")
|
67
33
|
end
|
34
|
+
end
|
68
35
|
|
69
|
-
|
70
|
-
|
36
|
+
class ShortNameSpecifiedTwiceError < ParserError
|
37
|
+
def initialize(what, name)
|
38
|
+
super("short #{what} #{name} specified twice")
|
71
39
|
end
|
72
40
|
end
|
73
|
-
end
|
74
|
-
|
75
|
-
class STDINHandling < Options::Base
|
76
|
-
include Options::Cast
|
77
|
-
include Options::Description
|
78
41
|
|
79
|
-
|
80
|
-
|
42
|
+
class ShortNameNotSymbolError < ParserError
|
43
|
+
def initialize(short)
|
44
|
+
super("short name has to be of type Symbol, got #{short.class.name}")
|
45
|
+
end
|
81
46
|
end
|
82
|
-
end
|
83
|
-
|
84
|
-
class Argument < Options::Base
|
85
|
-
include Options::Value
|
86
|
-
include Options::Cast
|
87
|
-
include Options::Description
|
88
47
|
|
89
|
-
|
90
|
-
|
48
|
+
class ShortNameIsInvalidError < ParserError
|
49
|
+
def initialize(short)
|
50
|
+
super("short name has to be one letter symbol, got #{short}")
|
51
|
+
end
|
91
52
|
end
|
92
53
|
end
|
93
54
|
|
94
|
-
class
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
55
|
+
class ParsingError < ArgumentError
|
56
|
+
class MissingOptionValueError < ParsingError
|
57
|
+
def initialize(option)
|
58
|
+
super("missing value for option #{option.switch}")
|
59
|
+
end
|
99
60
|
end
|
100
61
|
|
101
|
-
|
102
|
-
|
62
|
+
class UnknownSwitchError < ParsingError
|
63
|
+
def initialize(arg)
|
64
|
+
super("unknown switch #{arg}")
|
65
|
+
end
|
103
66
|
end
|
104
67
|
|
105
|
-
|
106
|
-
|
68
|
+
class MandatoryOptionsNotSpecifiedError < ParsingError
|
69
|
+
def initialize(options)
|
70
|
+
super("mandatory options not specified: #{options.map{|o| o.switch}.sort.join(', ')}")
|
71
|
+
end
|
107
72
|
end
|
108
73
|
|
109
|
-
|
110
|
-
|
74
|
+
class MandatoryArgumentNotSpecifiedError < ParsingError
|
75
|
+
def initialize(arg)
|
76
|
+
super("mandatory argument #{arg} not given")
|
77
|
+
end
|
111
78
|
end
|
112
79
|
|
113
|
-
|
114
|
-
|
80
|
+
class CastError < ParsingError
|
81
|
+
def initialize(arg, cast_name, error)
|
82
|
+
super("failed to cast: #{arg} to type: #{cast_name}: #{error}")
|
83
|
+
end
|
115
84
|
end
|
116
85
|
end
|
117
86
|
|
118
|
-
class
|
119
|
-
|
120
|
-
|
87
|
+
class Values < OpenStruct
|
88
|
+
def value(argument, value)
|
89
|
+
send((argument.name.to_s + '=').to_sym, value)
|
90
|
+
end
|
121
91
|
|
122
|
-
def
|
123
|
-
|
92
|
+
def set(argument)
|
93
|
+
value(argument, true)
|
124
94
|
end
|
125
95
|
end
|
126
96
|
|
127
97
|
def initialize(&block)
|
128
|
-
|
129
|
-
@switches =
|
130
|
-
@
|
131
|
-
@switches_short = {}
|
132
|
-
@options_default = []
|
133
|
-
@options_required = []
|
134
|
-
@arguments = []
|
98
|
+
@arguments = Arguments.new
|
99
|
+
@switches = Switches.new
|
100
|
+
@options = Options.new
|
135
101
|
instance_eval(&block) if block_given?
|
136
102
|
end
|
137
103
|
|
@@ -139,70 +105,74 @@ class CLI
|
|
139
105
|
@description = desc
|
140
106
|
end
|
141
107
|
|
142
|
-
def stdin(name =
|
143
|
-
@
|
108
|
+
def stdin(name = :data, options = {})
|
109
|
+
@stdin = DSL::Input.new(name, options)
|
144
110
|
end
|
145
111
|
|
146
112
|
def argument(name, options = {})
|
147
|
-
|
148
|
-
|
113
|
+
argument_dsl = DSL::Argument.new(name, options)
|
114
|
+
|
115
|
+
raise ParserError::ArgumentNameSpecifiedTwice.new(argument_dsl.name) if @arguments.has?(argument_dsl)
|
116
|
+
|
117
|
+
@arguments << argument_dsl
|
149
118
|
end
|
150
119
|
|
151
120
|
def switch(name, options = {})
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
121
|
+
switch_dsl = DSL::Switch.new(name, options)
|
122
|
+
|
123
|
+
raise ParserError::LongNameSpecifiedTwiceError.new('switch', switch_dsl.name) if @switches.has_long?(switch_dsl)
|
124
|
+
raise ParserError::LongNameSpecifiedTwiceError.new('option and switch', switch_dsl.name) if @options.has_long?(switch_dsl)
|
125
|
+
raise ParserError::ShortNameSpecifiedTwiceError.new('switch', switch_dsl.short) if @switches.has_short?(switch_dsl)
|
126
|
+
raise ParserError::ShortNameSpecifiedTwiceError.new('option and switch', switch_dsl.short) if @options.has_short?(switch_dsl)
|
127
|
+
|
128
|
+
@switches << switch_dsl
|
156
129
|
end
|
157
130
|
|
158
131
|
def option(name, options = {})
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
132
|
+
option_dsl = DSL::Option.new(name, options)
|
133
|
+
|
134
|
+
raise ParserError::LongNameSpecifiedTwiceError.new('option', option_dsl.name) if @options.has_long?(option_dsl)
|
135
|
+
raise ParserError::LongNameSpecifiedTwiceError.new('switch and option', option_dsl.name) if @switches.has_long?(option_dsl)
|
136
|
+
raise ParserError::ShortNameSpecifiedTwiceError.new('option', option_dsl.short) if @options.has_short?(option_dsl)
|
137
|
+
raise ParserError::ShortNameSpecifiedTwiceError.new('switch and option', option_dsl.short) if @switches.has_short?(option_dsl)
|
138
|
+
|
139
|
+
@options << option_dsl
|
165
140
|
end
|
166
141
|
|
167
142
|
def parse(_argv = ARGV, stdin = STDIN, stderr = STDERR)
|
168
|
-
|
169
|
-
|
143
|
+
values = Values.new
|
170
144
|
argv = _argv.dup
|
171
145
|
|
172
146
|
# check help
|
173
147
|
if argv.include? '-h' or argv.include? '--help'
|
174
|
-
|
175
|
-
return
|
148
|
+
values.help = usage
|
149
|
+
return values
|
176
150
|
end
|
177
151
|
|
178
152
|
# set defaults
|
179
|
-
@
|
180
|
-
|
153
|
+
@options.defaults.each do |o|
|
154
|
+
values.value(o, o.cast(o.default))
|
181
155
|
end
|
182
156
|
|
183
157
|
# process switches
|
184
|
-
|
185
|
-
while argv.first =~ /^-/
|
186
|
-
arg = argv.shift
|
187
|
-
switch = if arg =~ /^--/
|
188
|
-
@switches_long[arg.sub(/^--/, '').tr('-', '_').to_sym]
|
189
|
-
else
|
190
|
-
@switches_short[arg.sub(/^-/, '').tr('-', '_').to_sym]
|
191
|
-
end
|
158
|
+
mandatory_options = @options.mandatory.dup
|
192
159
|
|
193
|
-
|
160
|
+
while Switches.is_switch?(argv.first)
|
161
|
+
arg = argv.shift
|
194
162
|
|
195
|
-
if switch.
|
196
|
-
|
197
|
-
|
198
|
-
|
163
|
+
if switch = @switches.find(arg)
|
164
|
+
values.set(switch)
|
165
|
+
elsif option = @options.find(arg)
|
166
|
+
value = argv.shift or raise ParsingError::MissingOptionValueError.new(option)
|
167
|
+
values.value(option, option.cast(value))
|
168
|
+
mandatory_options.delete(option)
|
199
169
|
else
|
200
|
-
|
170
|
+
raise ParsingError::UnknownSwitchError.new(arg) unless switch
|
201
171
|
end
|
202
172
|
end
|
203
173
|
|
204
|
-
# check
|
205
|
-
raise ParsingError
|
174
|
+
# check mandatory options
|
175
|
+
raise ParsingError::MandatoryOptionsNotSpecifiedError.new(mandatory_options) unless mandatory_options.empty?
|
206
176
|
|
207
177
|
# process arguments
|
208
178
|
arguments = @arguments.dup
|
@@ -210,16 +180,16 @@ class CLI
|
|
210
180
|
value = if argv.length < arguments.length + 1 and argument.optional?
|
211
181
|
argument.default # not enough arguments, try to skip optional if possible
|
212
182
|
else
|
213
|
-
argv.shift or raise ParsingError
|
183
|
+
argv.shift or raise ParsingError::MandatoryArgumentNotSpecifiedError.new(argument)
|
214
184
|
end
|
215
185
|
|
216
|
-
|
186
|
+
values.value(argument, argument.cast(value))
|
217
187
|
end
|
218
188
|
|
219
189
|
# process stdin
|
220
|
-
|
190
|
+
values.stdin = @stdin.cast(stdin) if @stdin
|
221
191
|
|
222
|
-
|
192
|
+
values
|
223
193
|
end
|
224
194
|
|
225
195
|
def parse!(argv = ARGV, stdin = STDIN, stderr = STDERR, stdout = STDOUT)
|
@@ -236,29 +206,26 @@ class CLI
|
|
236
206
|
end
|
237
207
|
|
238
208
|
def usage(msg = nil)
|
239
|
-
switches = @switches.select{|s| s.class == Switch}
|
240
|
-
options = @switches.select{|s| s.class == Option}
|
241
|
-
|
242
209
|
out = StringIO.new
|
243
210
|
out.puts msg if msg
|
244
211
|
out.print "Usage: #{File.basename $0}"
|
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?
|
212
|
+
out.print ' [switches|options]' if not @switches.empty? and not @options.empty?
|
213
|
+
out.print ' [switches]' if not @switches.empty? and @options.empty?
|
214
|
+
out.print ' [options]' if @switches.empty? and not @options.empty?
|
248
215
|
out.print ' ' + @arguments.map{|a| a.to_s}.join(' ') unless @arguments.empty?
|
249
|
-
out.print " < #{@
|
216
|
+
out.print " < #{@stdin}" if @stdin
|
250
217
|
|
251
218
|
out.puts
|
252
219
|
out.puts @description if @description
|
253
220
|
|
254
|
-
if @
|
221
|
+
if @stdin and @stdin.description?
|
255
222
|
out.puts "Input:"
|
256
|
-
out.puts " #{@
|
223
|
+
out.puts " #{@stdin} - #{@stdin.description}"
|
257
224
|
end
|
258
225
|
|
259
|
-
unless switches.empty?
|
226
|
+
unless @switches.empty?
|
260
227
|
out.puts "Switches:"
|
261
|
-
switches.each do |s|
|
228
|
+
@switches.each do |s|
|
262
229
|
out.print ' '
|
263
230
|
out.print s.switch
|
264
231
|
out.print " (#{s.switch_short})" if s.has_short?
|
@@ -267,9 +234,9 @@ class CLI
|
|
267
234
|
end
|
268
235
|
end
|
269
236
|
|
270
|
-
unless options.empty?
|
237
|
+
unless @options.empty?
|
271
238
|
out.puts "Options:"
|
272
|
-
options.each do |o|
|
239
|
+
@options.each do |o|
|
273
240
|
out.print ' '
|
274
241
|
out.print o.switch
|
275
242
|
out.print " (#{o.switch_short})" if o.has_short?
|
data/lib/cli/dsl.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
class CLI
|
2
|
+
module DSL
|
3
|
+
class Base
|
4
|
+
def initialize(name, options = {})
|
5
|
+
class_name = self.class.name.gsub(/.*:/, '').downcase
|
6
|
+
raise ParserError::NameArgumetNotSymbolError.new(class_name, name) unless name.is_a? Symbol
|
7
|
+
raise ParserError::OptionsArgumentNotHashError.new(class_name, options) unless options.is_a? Hash
|
8
|
+
@name = name
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :name
|
13
|
+
end
|
14
|
+
|
15
|
+
module Cast
|
16
|
+
def cast(value)
|
17
|
+
begin
|
18
|
+
cast_class = @options[:cast]
|
19
|
+
if cast_class == nil
|
20
|
+
value
|
21
|
+
elsif cast_class == Integer
|
22
|
+
value.to_i
|
23
|
+
elsif cast_class == Float
|
24
|
+
value.to_f
|
25
|
+
elsif cast_class == YAML
|
26
|
+
YAML.load(value)
|
27
|
+
else
|
28
|
+
cast_class.new(value)
|
29
|
+
end
|
30
|
+
rescue => e
|
31
|
+
raise ParsingError::CastError.new(@name, @options[:cast].name, e)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Description
|
37
|
+
def description?
|
38
|
+
@options.member? :description
|
39
|
+
end
|
40
|
+
|
41
|
+
def description
|
42
|
+
@options[:description]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module Value
|
47
|
+
def default
|
48
|
+
@options[:default]
|
49
|
+
end
|
50
|
+
|
51
|
+
def has_default?
|
52
|
+
@options.member? :default
|
53
|
+
end
|
54
|
+
|
55
|
+
def optional?
|
56
|
+
has_default?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Input < DSL::Base
|
61
|
+
include DSL::Cast
|
62
|
+
include DSL::Description
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
(@name or @options[:cast] or 'data').to_s.tr('_', '-')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Argument < DSL::Base
|
70
|
+
include DSL::Value
|
71
|
+
include DSL::Cast
|
72
|
+
include DSL::Description
|
73
|
+
|
74
|
+
def to_s
|
75
|
+
name.to_s.tr('_', '-')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Switch < DSL::Base
|
80
|
+
def initialize(name, options = {})
|
81
|
+
super(name, options)
|
82
|
+
if short = options[:short]
|
83
|
+
raise ParserError::ShortNameNotSymbolError.new(short) if not short.is_a? Symbol
|
84
|
+
raise ParserError::ShortNameIsInvalidError.new(short) if short.to_s.length > 1
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
include DSL::Description
|
89
|
+
|
90
|
+
def has_short?
|
91
|
+
@options.member? :short
|
92
|
+
end
|
93
|
+
|
94
|
+
def short
|
95
|
+
@options[:short]
|
96
|
+
end
|
97
|
+
|
98
|
+
def switch
|
99
|
+
'--' + name.to_s.tr('_', '-')
|
100
|
+
end
|
101
|
+
|
102
|
+
def switch_short
|
103
|
+
'-' + short.to_s
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_s
|
107
|
+
switch
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class Option < Switch
|
112
|
+
include DSL::Value
|
113
|
+
include DSL::Cast
|
114
|
+
|
115
|
+
def optional?
|
116
|
+
has_default? or not @options[:required]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
data/lib/cli/options.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'cli/switches'
|
2
|
+
|
3
|
+
class CLI::Options < CLI::Switches
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
@defaults = []
|
7
|
+
@mandatory = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def <<(option_dsl)
|
11
|
+
super option_dsl
|
12
|
+
@defaults << option_dsl if option_dsl.has_default?
|
13
|
+
@mandatory << option_dsl unless option_dsl.optional?
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :defaults
|
17
|
+
attr_reader :mandatory
|
18
|
+
end
|
19
|
+
|
data/lib/cli/switches.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
class CLI::Switches < Array
|
2
|
+
def initialize
|
3
|
+
@long = {}
|
4
|
+
@short = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def <<(switch_dsl)
|
8
|
+
super(switch_dsl)
|
9
|
+
@long[switch_dsl.name] = switch_dsl
|
10
|
+
@short[switch_dsl.short] = switch_dsl if switch_dsl.has_short?
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.is_switch?(arg)
|
14
|
+
arg =~ /^-/
|
15
|
+
end
|
16
|
+
|
17
|
+
def find(arg)
|
18
|
+
if arg =~ /^--/
|
19
|
+
find_long(arg)
|
20
|
+
else
|
21
|
+
find_short(arg)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_long(arg)
|
26
|
+
@long[arg.sub(/^--/, '').tr('-', '_').to_sym]
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_short(arg)
|
30
|
+
@short[arg.sub(/^-/, '').tr('-', '_').to_sym]
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_long?(switch_dsl)
|
34
|
+
@long.member?(switch_dsl.name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_short?(switch_dsl)
|
38
|
+
@short.member?(switch_dsl.short) if switch_dsl.has_short?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
data/spec/cli_spec.rb
CHANGED
@@ -94,12 +94,35 @@ EOF
|
|
94
94
|
ps.test.should == 'hello'
|
95
95
|
end
|
96
96
|
|
97
|
+
it "should raise error if not symbol and optional hash is passed" do
|
98
|
+
lambda {
|
99
|
+
ps = CLI.new do
|
100
|
+
argument 'number'
|
101
|
+
end
|
102
|
+
}.should raise_error CLI::ParserError::NameArgumetNotSymbolError, "argument name has to be of type Symbol, got String"
|
103
|
+
|
104
|
+
lambda {
|
105
|
+
ps = CLI.new do
|
106
|
+
argument :number, :test
|
107
|
+
end
|
108
|
+
}.should raise_error CLI::ParserError::OptionsArgumentNotHashError, "argument options has to be of type Hash, got Symbol"
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should raise error if artument name is specified twice" do
|
112
|
+
lambda {
|
113
|
+
ps = CLI.new do
|
114
|
+
argument :number
|
115
|
+
argument :number
|
116
|
+
end
|
117
|
+
}.should raise_error CLI::ParserError::ArgumentNameSpecifiedTwice, 'argument number specified twice'
|
118
|
+
end
|
119
|
+
|
97
120
|
it "should raise error if not given" do
|
98
121
|
lambda {
|
99
122
|
ps = CLI.new do
|
100
123
|
argument :log
|
101
124
|
end.parse([])
|
102
|
-
}.should raise_error CLI::ParsingError
|
125
|
+
}.should raise_error CLI::ParsingError::MandatoryArgumentNotSpecifiedError, 'mandatory argument log not given'
|
103
126
|
end
|
104
127
|
|
105
128
|
it "should raise error if casting fail" do
|
@@ -108,7 +131,7 @@ EOF
|
|
108
131
|
ps = CLI.new do
|
109
132
|
argument :log, :cast => IP
|
110
133
|
end.parse(['abc'])
|
111
|
-
}.should raise_error CLI::ParsingError, 'failed to cast: log to type: IP: invalid address'
|
134
|
+
}.should raise_error CLI::ParsingError::CastError, 'failed to cast: log to type: IP: invalid address'
|
112
135
|
end
|
113
136
|
|
114
137
|
describe "with defaults" do
|
@@ -168,6 +191,34 @@ EOF
|
|
168
191
|
ps.unset.should be_nil
|
169
192
|
end
|
170
193
|
|
194
|
+
it "should raise error if not symbol and optional hash is passed" do
|
195
|
+
lambda {
|
196
|
+
ps = CLI.new do
|
197
|
+
switch 'number'
|
198
|
+
end.parse([])
|
199
|
+
}.should raise_error CLI::ParserError::NameArgumetNotSymbolError, "switch name has to be of type Symbol, got String"
|
200
|
+
|
201
|
+
lambda {
|
202
|
+
ps = CLI.new do
|
203
|
+
switch :number, :test
|
204
|
+
end
|
205
|
+
}.should raise_error CLI::ParserError::OptionsArgumentNotHashError, "switch options has to be of type Hash, got Symbol"
|
206
|
+
end
|
207
|
+
|
208
|
+
it "shoud raise error is short name is invalid" do
|
209
|
+
lambda {
|
210
|
+
ps = CLI.new do
|
211
|
+
switch :location, :short => "l"
|
212
|
+
end
|
213
|
+
}.should raise_error CLI::ParserError::ShortNameNotSymbolError, 'short name has to be of type Symbol, got String'
|
214
|
+
|
215
|
+
lambda {
|
216
|
+
ps = CLI.new do
|
217
|
+
switch :location, :short => :abc
|
218
|
+
end
|
219
|
+
}.should raise_error CLI::ParserError::ShortNameIsInvalidError, 'short name has to be one letter symbol, got abc'
|
220
|
+
end
|
221
|
+
|
171
222
|
it "should raise error on unrecognized switch" do
|
172
223
|
ps = CLI.new do
|
173
224
|
option :location
|
@@ -175,7 +226,7 @@ EOF
|
|
175
226
|
|
176
227
|
lambda {
|
177
228
|
ps.parse(['--xxx'])
|
178
|
-
}.should raise_error CLI::ParsingError
|
229
|
+
}.should raise_error CLI::ParsingError::UnknownSwitchError, 'unknown switch --xxx'
|
179
230
|
end
|
180
231
|
end
|
181
232
|
|
@@ -240,6 +291,20 @@ EOF
|
|
240
291
|
ps.gold.should be_nil
|
241
292
|
end
|
242
293
|
|
294
|
+
it "should raise error if not symbol and optional hash is passed" do
|
295
|
+
lambda {
|
296
|
+
ps = CLI.new do
|
297
|
+
option 'number'
|
298
|
+
end
|
299
|
+
}.should raise_error CLI::ParserError::NameArgumetNotSymbolError, "option name has to be of type Symbol, got String"
|
300
|
+
|
301
|
+
lambda {
|
302
|
+
ps = CLI.new do
|
303
|
+
option :number, :test
|
304
|
+
end
|
305
|
+
}.should raise_error CLI::ParserError::OptionsArgumentNotHashError, "option options has to be of type Hash, got Symbol"
|
306
|
+
end
|
307
|
+
|
243
308
|
it "should raise error on missing option argument" do
|
244
309
|
ps = CLI.new do
|
245
310
|
option :location
|
@@ -247,12 +312,13 @@ EOF
|
|
247
312
|
|
248
313
|
lambda {
|
249
314
|
ps.parse(['--location'])
|
250
|
-
}.should raise_error CLI::ParsingError
|
315
|
+
}.should raise_error CLI::ParsingError::MissingOptionValueError, 'missing value for option --location'
|
251
316
|
end
|
252
317
|
|
253
|
-
it "should raise error on missing
|
318
|
+
it "should raise error on missing mandatory option" do
|
254
319
|
ps = CLI.new do
|
255
320
|
option :location
|
321
|
+
option :weight, :required => true
|
256
322
|
option :size, :required => true
|
257
323
|
option :group, :default => 'red'
|
258
324
|
option :speed, :short => :s, :cast => Integer
|
@@ -260,7 +326,7 @@ EOF
|
|
260
326
|
|
261
327
|
lambda {
|
262
328
|
ps.parse(['--location', 'singapore'])
|
263
|
-
}.should raise_error CLI::ParsingError, "
|
329
|
+
}.should raise_error CLI::ParsingError::MandatoryOptionsNotSpecifiedError, "mandatory options not specified: --size, --weight"
|
264
330
|
end
|
265
331
|
end
|
266
332
|
|
@@ -291,6 +357,70 @@ EOF
|
|
291
357
|
ps.debug.should be_true
|
292
358
|
end
|
293
359
|
|
360
|
+
describe "name conflict reporting" do
|
361
|
+
it "raise error when long names configlict" do
|
362
|
+
lambda {
|
363
|
+
ps = CLI.new do
|
364
|
+
switch :location
|
365
|
+
switch :location
|
366
|
+
end
|
367
|
+
}.should raise_error CLI::ParserError::LongNameSpecifiedTwiceError, 'switch location specified twice'
|
368
|
+
|
369
|
+
lambda {
|
370
|
+
ps = CLI.new do
|
371
|
+
option :location
|
372
|
+
option :location
|
373
|
+
end
|
374
|
+
}.should raise_error CLI::ParserError::LongNameSpecifiedTwiceError, 'option location specified twice'
|
375
|
+
|
376
|
+
lambda {
|
377
|
+
ps = CLI.new do
|
378
|
+
switch :location
|
379
|
+
option :location
|
380
|
+
end
|
381
|
+
}.should raise_error CLI::ParserError::LongNameSpecifiedTwiceError, 'switch and option location specified twice'
|
382
|
+
|
383
|
+
lambda {
|
384
|
+
ps = CLI.new do
|
385
|
+
option :location
|
386
|
+
switch :location
|
387
|
+
end
|
388
|
+
}.should raise_error CLI::ParserError::LongNameSpecifiedTwiceError, 'option and switch location specified twice'
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
describe "short name conflict reporting" do
|
393
|
+
it "raise error when short names configlict" do
|
394
|
+
lambda {
|
395
|
+
ps = CLI.new do
|
396
|
+
switch :location, :short => :l
|
397
|
+
switch :location2, :short => :l
|
398
|
+
end
|
399
|
+
}.should raise_error CLI::ParserError::ShortNameSpecifiedTwiceError, 'short switch l specified twice'
|
400
|
+
|
401
|
+
lambda {
|
402
|
+
ps = CLI.new do
|
403
|
+
option :location, :short => :l
|
404
|
+
option :location2, :short => :l
|
405
|
+
end
|
406
|
+
}.should raise_error CLI::ParserError::ShortNameSpecifiedTwiceError, 'short option l specified twice'
|
407
|
+
|
408
|
+
lambda {
|
409
|
+
ps = CLI.new do
|
410
|
+
switch :location, :short => :l
|
411
|
+
option :location2, :short => :l
|
412
|
+
end
|
413
|
+
}.should raise_error CLI::ParserError::ShortNameSpecifiedTwiceError, 'short switch and option l specified twice'
|
414
|
+
|
415
|
+
lambda {
|
416
|
+
ps = CLI.new do
|
417
|
+
option :location2, :short => :l
|
418
|
+
switch :location, :short => :l
|
419
|
+
end
|
420
|
+
}.should raise_error CLI::ParserError::ShortNameSpecifiedTwiceError, 'short option and switch l specified twice'
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
294
424
|
describe "usage and description" do
|
295
425
|
it "parse should set help variable if -h or --help specified in the argument list and not parse the input" do
|
296
426
|
ss = CLI.new do
|
@@ -359,6 +489,31 @@ EOF
|
|
359
489
|
ss.usage.should include("log file to process")
|
360
490
|
end
|
361
491
|
|
492
|
+
it "should suggest that switches can be used in usage line" do
|
493
|
+
ss = CLI.new do
|
494
|
+
switch :location, :short => :l
|
495
|
+
end
|
496
|
+
|
497
|
+
ss.usage.first.should == "Usage: rspec [switches]\n"
|
498
|
+
end
|
499
|
+
|
500
|
+
it "should suggest that options can be used in usage line" do
|
501
|
+
ss = CLI.new do
|
502
|
+
option :location, :short => :l
|
503
|
+
end
|
504
|
+
|
505
|
+
ss.usage.first.should == "Usage: rspec [options]\n"
|
506
|
+
end
|
507
|
+
|
508
|
+
it "should suggest that switches or options can be used in usage line" do
|
509
|
+
ss = CLI.new do
|
510
|
+
switch :location, :short => :l
|
511
|
+
option :size, :short => :s
|
512
|
+
end
|
513
|
+
|
514
|
+
ss.usage.first.should == "Usage: rspec [switches|options]\n"
|
515
|
+
end
|
516
|
+
|
362
517
|
it "should allow describing whole script" do
|
363
518
|
ss = CLI.new do
|
364
519
|
description 'Log file processor'
|
@@ -398,7 +553,7 @@ EOF
|
|
398
553
|
switch :debug, :short => :d, :description => "enable debugging"
|
399
554
|
switch :logging, :short => :l
|
400
555
|
switch :run
|
401
|
-
option :location, :short => :
|
556
|
+
option :location, :short => :r, :description => "place where server is located"
|
402
557
|
option :group, :default => 'red'
|
403
558
|
option :power_up, :short => :p
|
404
559
|
option :speed, :short => :s, :cast => Integer
|
@@ -425,7 +580,7 @@ Switches:
|
|
425
580
|
--logging (-l)
|
426
581
|
--run
|
427
582
|
Options:
|
428
|
-
--location (-
|
583
|
+
--location (-r) - place where server is located
|
429
584
|
--group [red]
|
430
585
|
--power-up (-p)
|
431
586
|
--speed (-s)
|
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: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
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-15 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
type: :development
|
@@ -146,6 +146,10 @@ files:
|
|
146
146
|
- features/step_definitions/cli_steps.rb
|
147
147
|
- features/support/env.rb
|
148
148
|
- lib/cli.rb
|
149
|
+
- lib/cli/arguments.rb
|
150
|
+
- lib/cli/dsl.rb
|
151
|
+
- lib/cli/options.rb
|
152
|
+
- lib/cli/switches.rb
|
149
153
|
- spec/cli_spec.rb
|
150
154
|
- spec/spec_helper.rb
|
151
155
|
homepage: http://github.com/jpastuszek/cli
|