ejt_command_line 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MDQzM2QzNDU0NGE4MzhiOWZhMDc1ZGE0YjA3MDQzZTNkMDQ3Y2MzZA==
5
+ data.tar.gz: !binary |-
6
+ NmIxZDE2MTE0ZjZiNzQ5OWI4MjFhZTU0MGQwZTU2M2IyMDYxZTBlNA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NDQ2MDc2ZmEzZjY2ODZjNTAyNTQwNzNlMjM3YThjYWFiM2IwNjc0ZmQwNGQ4
10
+ MDdhODFjYjM4YTk5N2M1ZWM3ODRjNGEzZGM2YmRiMzU1MTNiYjRlNjEwN2U2
11
+ YTQ2MGRmOGMyM2FhMDI4ZTU4NjkyMjBmZGM0NTJiYjQ4OTkwMzg=
12
+ data.tar.gz: !binary |-
13
+ NTdlNzU2NTYwMmNiY2RmZDIxM2I5ZjNiYmZiOWJkZDY3ODc2ZTA0OGZhYTJh
14
+ MWY5MDllZDNkNWVlZjVjYzZlNjE5Y2NjN2UzMDU4YTM2NDBkNGI0ODZkNzAz
15
+ YWU2OTJlYWI4ZGYzMTViMjIxNzcwYWQzMWQ3MjU1YTdmMzc2NTU=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ejt_command_line.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Joe Thornber
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # EjtCommandLine
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'ejt_command_line'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install ejt_command_line
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ejt_command_line/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ejt_command_line"
8
+ spec.version = EjtCommandLine::VERSION
9
+ spec.authors = ["Joe Thornber"]
10
+ spec.email = ["ejt@redhat.com"]
11
+ spec.description = %q{Yet another command line parser. Allows you define argument types}
12
+ spec.summary = %q{Command line parser}
13
+ spec.homepage = ""
14
+ spec.license = "GPL"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,3 @@
1
+ module EjtCommandLine
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,268 @@
1
+ require "ejt_command_line/version"
2
+ require 'set'
3
+
4
+ #----------------------------------------------------------------
5
+
6
+ module CommandLine
7
+ # from prelude
8
+ def bracket_(release)
9
+ r = nil
10
+ begin
11
+ r = yield
12
+ ensure
13
+ release.call
14
+ end
15
+ r
16
+ end
17
+
18
+ #----------------------------------------------------------------
19
+
20
+ class CommandLineError < StandardError
21
+ end
22
+
23
+ class ParseError < CommandLineError
24
+ end
25
+
26
+ class ConfigureError < CommandLineError
27
+ end
28
+
29
+ #----------------------------------------------------------------
30
+
31
+ def simple_switch_parser(*aliases)
32
+ ArrayParser::choice(*aliases)
33
+ end
34
+
35
+ def value_switch_parser(*aliases, &block)
36
+ p = sequence(choice(*aliases), value(&block))
37
+ end
38
+
39
+ #----------------------------------------------------------------
40
+
41
+ class Switch
42
+ attr_reader :flags, :parser
43
+
44
+ def initialize(flags, parser = nil)
45
+ @flags = flags
46
+ @parser = parser
47
+ end
48
+
49
+ def has_flag?(flag)
50
+ @flags.member?(flag)
51
+ end
52
+ end
53
+
54
+ class Command
55
+ attr_reader :switches
56
+
57
+ def initialize
58
+ @switches = []
59
+ @mutually_exclusive_sets = [] # list of lists of syms
60
+ @mandatory = []
61
+ end
62
+
63
+ def add_switches(syms)
64
+ @switches += syms
65
+ end
66
+
67
+ def add_mutually_exclusive_set(syms)
68
+ @mutually_exclusive_sets << syms.to_set
69
+ end
70
+
71
+ def add_mandatory_switch(sym)
72
+ @mandatory << sym
73
+ end
74
+
75
+ def check_mutual_exclusion(syms)
76
+ nr_sets = @mutually_exclusive_sets.size
77
+ set_counts = Array.new(nr_sets, [])
78
+
79
+ syms.each do |s|
80
+ # is it in an exclusive set?
81
+ 0.upto(nr_sets - 1) do |n|
82
+ if @mutually_exclusive_sets[n].member?(s)
83
+ set_counts[n] << s
84
+ end
85
+ end
86
+ end
87
+
88
+ 0.upto(nr_sets - 1) do |n|
89
+ if set_counts[n].size > 1
90
+ msg = "mutually exclusive options used:\n"
91
+ set_counts[n].each {|sym| msg += " #{sym}\n"}
92
+ raise ParseError, msg
93
+ end
94
+ end
95
+ end
96
+
97
+ def check_mandatory(syms)
98
+ missing = []
99
+
100
+ @mandatory.each do |m|
101
+ unless syms.member?(m)
102
+ missing << m
103
+ end
104
+ end
105
+
106
+ if missing.size > 0
107
+ msg = "missing mandatory switches:\n"
108
+ missing.each do |m|
109
+ msg += " #{m}\n"
110
+ end
111
+
112
+ raise ParseError, msg
113
+ end
114
+ end
115
+ end
116
+
117
+ class Parser
118
+ GLOBAL_SYM = :global__
119
+
120
+ def initialize(&block)
121
+ @switches = {}
122
+ @global_switches = []
123
+ @value_types = {}
124
+ @commands = Hash.new {|hash, key| Command.new}
125
+ @current_command = @commands[GLOBAL_SYM]
126
+
127
+ configure(&block) if block
128
+ end
129
+
130
+ def configure(&block)
131
+ self.instance_eval(&block)
132
+ end
133
+
134
+ def value_type(sym, &parser)
135
+ if @value_types.member?(sym)
136
+ raise ConfigureError, "duplicate value type '#{sym}'"
137
+ end
138
+
139
+ @value_types[sym] = parser
140
+ end
141
+
142
+ def simple_switch(sym, *flags)
143
+ @switches[sym] = Switch.new(flags)
144
+ end
145
+
146
+ def value_switch(sym, value_sym, *flags)
147
+ @switches[sym] = Switch.new(flags, get_value_parser(value_sym))
148
+ end
149
+
150
+ def global(&block)
151
+ command(GLOBAL_SYM, &block)
152
+ end
153
+
154
+ def command(sym, &block)
155
+ old = @current_command
156
+ @current_command = @commands[sym] = Command.new
157
+
158
+ if block
159
+ release = lambda {@current_command = old}
160
+ bracket_(release) do
161
+ self.instance_eval(&block)
162
+ end
163
+ end
164
+ end
165
+
166
+ def switches(*syms)
167
+ check_switches_are_defined(syms)
168
+ @current_command.add_switches(syms)
169
+ end
170
+
171
+ def one_of(*syms)
172
+ check_switches_are_defined(syms)
173
+ @current_command.add_switches(syms)
174
+ @current_command.add_mutually_exclusive_set(syms)
175
+ end
176
+
177
+ def mandatory(sym)
178
+ syms = [sym]
179
+ check_switches_are_defined(syms)
180
+ @current_command.add_switches(syms)
181
+ @current_command.add_mandatory_switch(sym)
182
+ end
183
+
184
+ def parse(handler, *args)
185
+ command, opts, plain_args = parse_(args)
186
+ handler.send(command, opts, plain_args)
187
+ end
188
+
189
+ private
190
+ def parse_value(arg, s, args)
191
+ if s.parser
192
+ if args.size == 0
193
+ raise ParseError, "no value specified for switch '#{arg}'"
194
+ end
195
+
196
+ value = args.shift
197
+ begin
198
+ s.parser.call(value)
199
+ rescue => e
200
+ raise ParseError, "couldn't parse value '#{arg}=#{value}'\n#{e}"
201
+ end
202
+ else
203
+ true
204
+ end
205
+ end
206
+
207
+ def parse_(args)
208
+ in_command = false
209
+ opts = {}
210
+ plain_args = []
211
+ valid_switches = @commands[GLOBAL_SYM].switches
212
+ command = :global_command
213
+
214
+ while args.size > 0 do
215
+ arg = args.shift
216
+
217
+ if arg =~ /^-/
218
+ sym, s = find_switch(valid_switches, arg)
219
+ opts[sym] = parse_value(arg, s, args)
220
+
221
+ else
222
+ cmd = arg.intern
223
+
224
+ if !in_command && @commands.member?(cmd)
225
+ command = cmd
226
+ valid_switches = @commands[cmd].switches
227
+ in_command = true
228
+ else
229
+ plain_args << arg
230
+ end
231
+ end
232
+ end
233
+
234
+ @commands[command].check_mutual_exclusion(opts.keys)
235
+ @commands[command].check_mandatory(opts.keys)
236
+ [command, opts, plain_args]
237
+ end
238
+
239
+ def check_switches_are_defined(syms)
240
+ syms.each do |sym|
241
+ raise ConfigureError, "unknown switch '#{sym}'" unless @switches.member?(sym)
242
+ end
243
+ end
244
+
245
+ def find_switch(valid_switches, switch)
246
+ catch :found do
247
+ valid_switches.each do |sym|
248
+ s = @switches[sym]
249
+ if s.has_flag?(switch)
250
+ throw :found, [sym, s]
251
+ end
252
+ end
253
+
254
+ raise ParseError, "unexpected switch '#{switch}'"
255
+ end
256
+ end
257
+
258
+ def get_value_parser(sym)
259
+ if @value_types.member?(sym)
260
+ @value_types[sym]
261
+ else
262
+ raise ConfigureError, "unknown value type '#{sym}'"
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ #----------------------------------------------------------------
@@ -0,0 +1,358 @@
1
+ require 'spec_helper'
2
+
3
+ include CommandLine
4
+
5
+ #----------------------------------------------------------------
6
+
7
+ describe "Parser" do
8
+ before :each do
9
+ @clh = Parser.new
10
+ end
11
+
12
+ def help_switch
13
+ @clh.simple_switch :help, '--help', '-h'
14
+ end
15
+
16
+ describe "creation" do
17
+ it "should take a config block" do
18
+ block_watcher = mock()
19
+ block_watcher.should_receive(:executed)
20
+
21
+ clh = Parser.new do
22
+ value_type :string do |str|
23
+ str
24
+ end
25
+
26
+ block_watcher.executed
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "value types" do
32
+ it "should allow you to register new value types" do
33
+ @clh.configure do
34
+ value_type :string do |str|
35
+ str
36
+ end
37
+
38
+ value_type :int do |str|
39
+ str.to_i
40
+ end
41
+ end
42
+ end
43
+
44
+ it "should fail it you try and define a duplicate value type" do
45
+ @clh.value_type :string do |str|
46
+ str
47
+ end
48
+
49
+ expect do
50
+ @clh.value_type :string do |str|
51
+ str
52
+ end
53
+ end.to raise_error(ConfigureError, /string/)
54
+ end
55
+ end
56
+
57
+ describe "switches are defined separately from commands" do
58
+ it "should let you define binary switch" do
59
+ help_switch
60
+ end
61
+
62
+ it "should fail if you try and define a switch that takes an unknown value type" do
63
+ expect {@clh.value_switch :resize_to, :volume_size, '--resize-to'}.to raise_error(ConfigureError, /volume_size/)
64
+ end
65
+
66
+ it "should let you define an option that takes a single value" do
67
+ @clh.configure do
68
+ value_type :volume_size do |str|
69
+ str.to_i
70
+ end
71
+
72
+ value_switch :resize_to, :volume_size, '--resize-to'
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "global switches" do
78
+ it "should let you bind global switch" do
79
+ help_switch
80
+ @clh.configure do
81
+ global do
82
+ switches :help
83
+ end
84
+ end
85
+ end
86
+
87
+ it "should raise an error if the switch hasn't been previously defined" do
88
+ expect do
89
+ @clh.global do
90
+ switches :become_sentient
91
+ end
92
+ end.to raise_error(ConfigureError)
93
+ end
94
+ end
95
+
96
+ describe "sub commands" do
97
+ it "should let you register a command" do
98
+ @clh.configure do
99
+ command(:create) {}
100
+ end
101
+ end
102
+
103
+ it "should let you omit the block for a command" do
104
+ @clh.configure do
105
+ command :create
106
+ end
107
+ end
108
+
109
+ it "should let you bind switches" do
110
+ @clh.configure do
111
+ simple_switch :grow_to, '--grow-to'
112
+ simple_switch :grow_by, '--grow-by'
113
+ simple_switch :shrink_to, '--shrink-to'
114
+ simple_switch :shrink_by, '--shrink-by'
115
+
116
+ command :resize do
117
+ switches :grow_to, :grow_by, :shrink_to, :shrink_by
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe "parsing" do
124
+ describe "global command" do
125
+ it "should handle no switches" do
126
+ handler = mock()
127
+ handler.should_receive(:global_command).with({}, [])
128
+ @clh.parse(handler)
129
+ end
130
+
131
+ it "should raise a ParseError if an unrecognised switch is used" do
132
+ handler = mock()
133
+ expect {@clh.parse(handler, '--go-back-in-time')}.to raise_error(ParseError, /--go-back-in-time/)
134
+ end
135
+
136
+ it "should handle binary switches" do
137
+ handler = mock()
138
+ handler.should_receive(:global_command).with({:help => true}, [])
139
+
140
+ @clh.configure do
141
+ simple_switch :help, '--help', '-h'
142
+ global do
143
+ switches :help
144
+ end
145
+ end
146
+
147
+ @clh.parse(handler, '-h')
148
+ end
149
+
150
+ it "should handle multiple binary switches" do
151
+ handler = mock()
152
+ handler.should_receive(:global_command).with({:help => true, :ro => true}, [])
153
+
154
+ @clh.configure do
155
+ simple_switch :help, '--help', '-h'
156
+ simple_switch :ro, '--read-only', '-r'
157
+
158
+ global do
159
+ switches :help, :ro
160
+ end
161
+ end
162
+
163
+ @clh.parse(handler, '-h', '--read-only')
164
+ end
165
+
166
+ it "should handle valued switches" do
167
+ handler = mock()
168
+
169
+ @clh.configure do
170
+ value_type :int do |str|
171
+ str.to_i
172
+ end
173
+
174
+ value_switch :count, :int, '--count', '-c'
175
+
176
+ global do
177
+ switches :count
178
+ end
179
+ end
180
+
181
+ handler.should_receive(:global_command).
182
+ with({:count => 17}, [])
183
+ @clh.parse(handler, '--count', '17')
184
+
185
+ handler.should_receive(:global_command).
186
+ with({:count => 17}, ['one', 'two'])
187
+ @clh.parse(handler, 'one', '-c', '17', 'two')
188
+ end
189
+
190
+ it "should raise an ArgumentError if no value is given for a valued switch" do
191
+ handler = mock()
192
+
193
+ @clh.configure do
194
+ value_type :int do |str|
195
+ str.to_i
196
+ end
197
+
198
+ value_switch :count, :int, '--count', '-c'
199
+
200
+ global do
201
+ switches :count
202
+ end
203
+ end
204
+
205
+ expect do
206
+ @clh.parse(handler, '--count')
207
+ end.to raise_error(ParseError, /count/)
208
+ end
209
+
210
+ it "should filter non-switches out" do
211
+ handler = mock()
212
+ handler.should_receive(:global_command).
213
+ with({:help => true, :ro => true}, ['my_file', 'my_other_file'])
214
+
215
+ @clh.configure do
216
+ simple_switch :help, '--help', '-h'
217
+ simple_switch :ro, '--read-only', '-r'
218
+
219
+ global do
220
+ switches :help, :ro
221
+ end
222
+ end
223
+
224
+ @clh.parse(handler, '-h', 'my_file', '--read-only', 'my_other_file')
225
+ end
226
+ end
227
+
228
+ describe "simple commands" do
229
+ it "should handle commands that take no switches" do
230
+ @clh.configure do
231
+ command :create do
232
+ end
233
+ end
234
+
235
+ handler = mock()
236
+ handler.should_receive(:create).with({}, ['fred'])
237
+ @clh.parse(handler, 'create', 'fred')
238
+ end
239
+ end
240
+
241
+ describe "commands" do
242
+ before :each do
243
+ @clh.configure do
244
+ value_type :int do |str|
245
+ str.to_i
246
+ end
247
+
248
+ value_switch :grow_to, :int, '--grow-to'
249
+ value_switch :grow_by, :int, '--grow-by'
250
+ value_switch :shrink_to, :int, '--shrink-to'
251
+ value_switch :shrink_by, :int, '--shrink-by'
252
+
253
+ command :resize do
254
+ switches :grow_to, :grow_by, :shrink_to, :shrink_by
255
+ end
256
+
257
+ command :shrink do
258
+ switches :grow_to, :grow_by, :shrink_to, :shrink_by
259
+ end
260
+
261
+ command :grow do
262
+ switches :grow_to, :grow_by, :shrink_to, :shrink_by
263
+ end
264
+ end
265
+ end
266
+
267
+ it "should allow you to define a sub command" do
268
+ handler = mock()
269
+ handler.should_receive(:resize).with({:grow_to => 12345}, ['fred'])
270
+ @clh.parse(handler, 'resize', '--grow-to', '12345', 'fred')
271
+ end
272
+
273
+ it "should prevent you calling two sub commands on the same line" do
274
+ handler = mock()
275
+ handler.should_receive(:resize).
276
+ with({:grow_to => 1234, :shrink_to => 2345}, ['shrink', 'fred'])
277
+ @clh.parse(handler, 'resize', '--grow-to', '1234', 'shrink', '--shrink-to', '2345', 'fred')
278
+ end
279
+ end
280
+
281
+ describe "exclusive switches" do
282
+ before :each do
283
+ @clh.configure do
284
+ value_type :int do |str|
285
+ str.to_i
286
+ end
287
+
288
+ value_switch :grow_to, :int, '--grow-to'
289
+ value_switch :grow_by, :int, '--grow-by'
290
+ value_switch :shrink_to, :int, '--shrink-to'
291
+ value_switch :shrink_by, :int, '--shrink-by'
292
+
293
+ command :resize do
294
+ one_of :grow_to, :grow_by, :shrink_to, :shrink_by
295
+ end
296
+ end
297
+ end
298
+
299
+ it "should parse one exclusive switch" do
300
+ handler = mock()
301
+ handler.should_receive(:resize).
302
+ with({:grow_to => 1234}, ['fred'])
303
+ @clh.parse(handler, 'resize', '--grow-to', '1234', 'fred')
304
+ end
305
+
306
+ it "should raise a ParseError if more than one switch from an exclusive set is defined" do
307
+ handler = mock()
308
+ expect do
309
+ @clh.parse(handler, 'resize', '--grow-to', '1234', '--shrink-by', '2345', 'fred')
310
+ end.to raise_error(ParseError, /mutually exclusive/)
311
+ # FIXME: would be nice to see the actual flags in the exception
312
+ end
313
+
314
+ it "should let you define more than one exclusive set" do
315
+ pending
316
+ end
317
+ end
318
+
319
+ it "should handle --foo=<value>" do
320
+ pending "todo"
321
+ end
322
+ end
323
+
324
+ describe "mandatory switches" do
325
+ before :each do
326
+ @clh.configure do
327
+ value_type :int do |str|
328
+ str.to_i
329
+ end
330
+
331
+ value_switch :grow_to, :int, '--grow-to'
332
+ value_switch :grow_by, :int, '--grow-by'
333
+ value_switch :shrink_to, :int, '--shrink-to'
334
+ value_switch :shrink_by, :int, '--shrink-by'
335
+
336
+ command :resize do
337
+ mandatory :grow_to
338
+ end
339
+ end
340
+ end
341
+
342
+ it "should parse ok if mandatory switch is given" do
343
+ handler = mock()
344
+ handler.should_receive(:resize).
345
+ with({:grow_to => 3}, ['fred'])
346
+ @clh.parse(handler, 'resize', '--grow-to', '3', 'fred')
347
+ end
348
+
349
+ it "should raise a ParseError if a mandatory switch is omitted" do
350
+ handler = mock()
351
+ expect do
352
+ @clh.parse(handler, 'resize', 'fred')
353
+ end.to raise_error(ParseError, /grow_to/)
354
+ end
355
+ end
356
+ end
357
+
358
+ #----------------------------------------------------------------
@@ -0,0 +1,359 @@
1
+ require 'spec_helper'
2
+ require 'lib/command_line'
3
+
4
+ include CommandLine
5
+
6
+ #----------------------------------------------------------------
7
+
8
+ describe "Parser" do
9
+ before :each do
10
+ @clh = Parser.new
11
+ end
12
+
13
+ def help_switch
14
+ @clh.simple_switch :help, '--help', '-h'
15
+ end
16
+
17
+ describe "creation" do
18
+ it "should take a config block" do
19
+ block_watcher = mock()
20
+ block_watcher.should_receive(:executed)
21
+
22
+ clh = Parser.new do
23
+ value_type :string do |str|
24
+ str
25
+ end
26
+
27
+ block_watcher.executed
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "value types" do
33
+ it "should allow you to register new value types" do
34
+ @clh.configure do
35
+ value_type :string do |str|
36
+ str
37
+ end
38
+
39
+ value_type :int do |str|
40
+ str.to_i
41
+ end
42
+ end
43
+ end
44
+
45
+ it "should fail it you try and define a duplicate value type" do
46
+ @clh.value_type :string do |str|
47
+ str
48
+ end
49
+
50
+ expect do
51
+ @clh.value_type :string do |str|
52
+ str
53
+ end
54
+ end.to raise_error(ConfigureError, /string/)
55
+ end
56
+ end
57
+
58
+ describe "switches are defined separately from commands" do
59
+ it "should let you define binary switch" do
60
+ help_switch
61
+ end
62
+
63
+ it "should fail if you try and define a switch that takes an unknown value type" do
64
+ expect {@clh.value_switch :resize_to, :volume_size, '--resize-to'}.to raise_error(ConfigureError, /volume_size/)
65
+ end
66
+
67
+ it "should let you define an option that takes a single value" do
68
+ @clh.configure do
69
+ value_type :volume_size do |str|
70
+ str.to_i
71
+ end
72
+
73
+ value_switch :resize_to, :volume_size, '--resize-to'
74
+ end
75
+ end
76
+ end
77
+
78
+ describe "global switches" do
79
+ it "should let you bind global switch" do
80
+ help_switch
81
+ @clh.configure do
82
+ global do
83
+ switches :help
84
+ end
85
+ end
86
+ end
87
+
88
+ it "should raise an error if the switch hasn't been previously defined" do
89
+ expect do
90
+ @clh.global do
91
+ switches :become_sentient
92
+ end
93
+ end.to raise_error(ConfigureError)
94
+ end
95
+ end
96
+
97
+ describe "sub commands" do
98
+ it "should let you register a command" do
99
+ @clh.configure do
100
+ command(:create) {}
101
+ end
102
+ end
103
+
104
+ it "should let you omit the block for a command" do
105
+ @clh.configure do
106
+ command :create
107
+ end
108
+ end
109
+
110
+ it "should let you bind switches" do
111
+ @clh.configure do
112
+ simple_switch :grow_to, '--grow-to'
113
+ simple_switch :grow_by, '--grow-by'
114
+ simple_switch :shrink_to, '--shrink-to'
115
+ simple_switch :shrink_by, '--shrink-by'
116
+
117
+ command :resize do
118
+ switches :grow_to, :grow_by, :shrink_to, :shrink_by
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ describe "parsing" do
125
+ describe "global command" do
126
+ it "should handle no switches" do
127
+ handler = mock()
128
+ handler.should_receive(:global_command).with({}, [])
129
+ @clh.parse(handler)
130
+ end
131
+
132
+ it "should raise a ParseError if an unrecognised switch is used" do
133
+ handler = mock()
134
+ expect {@clh.parse(handler, '--go-back-in-time')}.to raise_error(ParseError, /--go-back-in-time/)
135
+ end
136
+
137
+ it "should handle binary switches" do
138
+ handler = mock()
139
+ handler.should_receive(:global_command).with({:help => true}, [])
140
+
141
+ @clh.configure do
142
+ simple_switch :help, '--help', '-h'
143
+ global do
144
+ switches :help
145
+ end
146
+ end
147
+
148
+ @clh.parse(handler, '-h')
149
+ end
150
+
151
+ it "should handle multiple binary switches" do
152
+ handler = mock()
153
+ handler.should_receive(:global_command).with({:help => true, :ro => true}, [])
154
+
155
+ @clh.configure do
156
+ simple_switch :help, '--help', '-h'
157
+ simple_switch :ro, '--read-only', '-r'
158
+
159
+ global do
160
+ switches :help, :ro
161
+ end
162
+ end
163
+
164
+ @clh.parse(handler, '-h', '--read-only')
165
+ end
166
+
167
+ it "should handle valued switches" do
168
+ handler = mock()
169
+
170
+ @clh.configure do
171
+ value_type :int do |str|
172
+ str.to_i
173
+ end
174
+
175
+ value_switch :count, :int, '--count', '-c'
176
+
177
+ global do
178
+ switches :count
179
+ end
180
+ end
181
+
182
+ handler.should_receive(:global_command).
183
+ with({:count => 17}, [])
184
+ @clh.parse(handler, '--count', '17')
185
+
186
+ handler.should_receive(:global_command).
187
+ with({:count => 17}, ['one', 'two'])
188
+ @clh.parse(handler, 'one', '-c', '17', 'two')
189
+ end
190
+
191
+ it "should raise an ArgumentError if no value is given for a valued switch" do
192
+ handler = mock()
193
+
194
+ @clh.configure do
195
+ value_type :int do |str|
196
+ str.to_i
197
+ end
198
+
199
+ value_switch :count, :int, '--count', '-c'
200
+
201
+ global do
202
+ switches :count
203
+ end
204
+ end
205
+
206
+ expect do
207
+ @clh.parse(handler, '--count')
208
+ end.to raise_error(ParseError, /count/)
209
+ end
210
+
211
+ it "should filter non-switches out" do
212
+ handler = mock()
213
+ handler.should_receive(:global_command).
214
+ with({:help => true, :ro => true}, ['my_file', 'my_other_file'])
215
+
216
+ @clh.configure do
217
+ simple_switch :help, '--help', '-h'
218
+ simple_switch :ro, '--read-only', '-r'
219
+
220
+ global do
221
+ switches :help, :ro
222
+ end
223
+ end
224
+
225
+ @clh.parse(handler, '-h', 'my_file', '--read-only', 'my_other_file')
226
+ end
227
+ end
228
+
229
+ describe "simple commands" do
230
+ it "should handle commands that take no switches" do
231
+ @clh.configure do
232
+ command :create do
233
+ end
234
+ end
235
+
236
+ handler = mock()
237
+ handler.should_receive(:create).with({}, ['fred'])
238
+ @clh.parse(handler, 'create', 'fred')
239
+ end
240
+ end
241
+
242
+ describe "commands" do
243
+ before :each do
244
+ @clh.configure do
245
+ value_type :int do |str|
246
+ str.to_i
247
+ end
248
+
249
+ value_switch :grow_to, :int, '--grow-to'
250
+ value_switch :grow_by, :int, '--grow-by'
251
+ value_switch :shrink_to, :int, '--shrink-to'
252
+ value_switch :shrink_by, :int, '--shrink-by'
253
+
254
+ command :resize do
255
+ switches :grow_to, :grow_by, :shrink_to, :shrink_by
256
+ end
257
+
258
+ command :shrink do
259
+ switches :grow_to, :grow_by, :shrink_to, :shrink_by
260
+ end
261
+
262
+ command :grow do
263
+ switches :grow_to, :grow_by, :shrink_to, :shrink_by
264
+ end
265
+ end
266
+ end
267
+
268
+ it "should allow you to define a sub command" do
269
+ handler = mock()
270
+ handler.should_receive(:resize).with({:grow_to => 12345}, ['fred'])
271
+ @clh.parse(handler, 'resize', '--grow-to', '12345', 'fred')
272
+ end
273
+
274
+ it "should prevent you calling two sub commands on the same line" do
275
+ handler = mock()
276
+ handler.should_receive(:resize).
277
+ with({:grow_to => 1234, :shrink_to => 2345}, ['shrink', 'fred'])
278
+ @clh.parse(handler, 'resize', '--grow-to', '1234', 'shrink', '--shrink-to', '2345', 'fred')
279
+ end
280
+ end
281
+
282
+ describe "exclusive switches" do
283
+ before :each do
284
+ @clh.configure do
285
+ value_type :int do |str|
286
+ str.to_i
287
+ end
288
+
289
+ value_switch :grow_to, :int, '--grow-to'
290
+ value_switch :grow_by, :int, '--grow-by'
291
+ value_switch :shrink_to, :int, '--shrink-to'
292
+ value_switch :shrink_by, :int, '--shrink-by'
293
+
294
+ command :resize do
295
+ one_of :grow_to, :grow_by, :shrink_to, :shrink_by
296
+ end
297
+ end
298
+ end
299
+
300
+ it "should parse one exclusive switch" do
301
+ handler = mock()
302
+ handler.should_receive(:resize).
303
+ with({:grow_to => 1234}, ['fred'])
304
+ @clh.parse(handler, 'resize', '--grow-to', '1234', 'fred')
305
+ end
306
+
307
+ it "should raise a ParseError if more than one switch from an exclusive set is defined" do
308
+ handler = mock()
309
+ expect do
310
+ @clh.parse(handler, 'resize', '--grow-to', '1234', '--shrink-by', '2345', 'fred')
311
+ end.to raise_error(ParseError, /mutually exclusive/)
312
+ # FIXME: would be nice to see the actual flags in the exception
313
+ end
314
+
315
+ it "should let you define more than one exclusive set" do
316
+ pending
317
+ end
318
+ end
319
+
320
+ it "should handle --foo=<value>" do
321
+ pending "todo"
322
+ end
323
+ end
324
+
325
+ describe "mandatory switches" do
326
+ before :each do
327
+ @clh.configure do
328
+ value_type :int do |str|
329
+ str.to_i
330
+ end
331
+
332
+ value_switch :grow_to, :int, '--grow-to'
333
+ value_switch :grow_by, :int, '--grow-by'
334
+ value_switch :shrink_to, :int, '--shrink-to'
335
+ value_switch :shrink_by, :int, '--shrink-by'
336
+
337
+ command :resize do
338
+ mandatory :grow_to
339
+ end
340
+ end
341
+ end
342
+
343
+ it "should parse ok if mandatory switch is given" do
344
+ handler = mock()
345
+ handler.should_receive(:resize).
346
+ with({:grow_to => 3}, ['fred'])
347
+ @clh.parse(handler, 'resize', '--grow-to', '3', 'fred')
348
+ end
349
+
350
+ it "should raise a ParseError if a mandatory switch is omitted" do
351
+ handler = mock()
352
+ expect do
353
+ @clh.parse(handler, 'resize', 'fred')
354
+ end.to raise_error(ParseError, /grow_to/)
355
+ end
356
+ end
357
+ end
358
+
359
+ #----------------------------------------------------------------
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'ejt_command_line'
5
+
6
+ RSpec.configure do |config|
7
+
8
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ejt_command_line
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Joe Thornber
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Yet another command line parser. Allows you define argument types
56
+ email:
57
+ - ejt@redhat.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .rspec
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - ejt_command_line.gemspec
69
+ - lib/ejt_command_line.rb
70
+ - lib/ejt_command_line/version.rb
71
+ - spec/command_line_spec.rb
72
+ - spec/command_line_spec.rb~
73
+ - spec/spec_helper.rb
74
+ homepage: ''
75
+ licenses:
76
+ - GPL
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.0.3
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Command line parser
98
+ test_files:
99
+ - spec/command_line_spec.rb
100
+ - spec/command_line_spec.rb~
101
+ - spec/spec_helper.rb