cli 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|