cliapp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,77 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ desc "update version number"
5
+ task :prepare, [:version] do |t, args|
6
+ version = version_number_required(args, :prepare)
7
+ spec = load_gemspec_file(SPECFILE)
8
+ edit(spec.files) {|s|
9
+ s.gsub(/\$Version\:.*?\$/, "$Version\: #{version} $") \
10
+ .gsub(/\$Version\$/, version)
11
+ }
12
+ end
13
+
14
+
15
+ desc "create gem package"
16
+ task :package do
17
+ sh "gem build #{SPECFILE}"
18
+ end
19
+
20
+
21
+ desc "upload gem to rubygems.org"
22
+ task :release, [:version] => :package do |t, args|
23
+ version = version_number_required(args, :release)
24
+ spec = load_gemspec_file(SPECFILE)
25
+ version == spec.version.to_s or
26
+ raise "Version in gemspec file (#{spec.version}) is different from #{version}"
27
+ gemfile = "#{PROJECT}-#{version}.gem"
28
+ print "*** Are you sure to upload #{gemfile}? [y/N]: "
29
+ answer = $stdin.gets().strip()
30
+ if answer =~ /\A[yY]/
31
+ sh "git tag v#{version}"
32
+ #sh "git tag rel-#{version}"
33
+ sh "gem push #{gemfile}"
34
+ end
35
+ end
36
+
37
+
38
+ ##
39
+ ## helpers
40
+ ##
41
+
42
+ def edit(*filepaths)
43
+ filepaths.flatten.each do |fpath|
44
+ next if ! File.file?(fpath)
45
+ File.open(fpath, 'r+b:utf-8') do |f|
46
+ s = f.read()
47
+ new_s = yield s
48
+ if new_s != s
49
+ f.rewind()
50
+ f.truncate(0)
51
+ f.write(new_s)
52
+ puts "[Change] #{fpath}"
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def load_gemspec_file(gemspec_file)
59
+ require 'rubygems'
60
+ return Gem::Specification::load(gemspec_file)
61
+ end
62
+
63
+ def version_number_required(args, task_name)
64
+ version = args[:version] || ENV['version']
65
+ unless version
66
+ $stderr.puts <<"END"
67
+ ##
68
+ ## ERROR: rake #{task_name}: requires 'version=X.X.X' option.
69
+ ## For example:
70
+ ## $ rake #{task_name} version=1.0.0
71
+ ##
72
+ END
73
+ errmsg = "rake #{task_name}: requires 'version=X.X.X' option."
74
+ raise ArgumentError.new(errmsg)
75
+ end
76
+ return version
77
+ end
@@ -0,0 +1,54 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+
5
+ require_relative './init'
6
+
7
+
8
+ Oktest.scope do
9
+
10
+
11
+ topic CLIApp::Action do
12
+
13
+ fixture :action do
14
+ CLIApp::Action.new("hello", nil) do |aa, bb, cc=nil|
15
+ [aa, bb, cc]
16
+ end
17
+ end
18
+
19
+
20
+ topic '#call()' do
21
+
22
+ spec "[!pc2hw] raises error when fewer arguments." do
23
+ |action|
24
+ pr = proc { action.call(10) }
25
+ ok {pr}.raise?(CLIApp::ActionTooFewArgsError,
26
+ "Too few arguments.")
27
+ end
28
+
29
+ spec "[!6vdhh] raises error when too many arguments." do
30
+ |action|
31
+ pr = proc { action.call(10, 20, 30, 40) }
32
+ ok {pr}.raise?(CLIApp::ActionTooManyArgsError,
33
+ "Too many arguments.")
34
+ end
35
+
36
+ spec "[!7n4hs] invokes action block with args and kwargs." do
37
+ |action|
38
+ ret1 = nil
39
+ pr1 = proc { ret1 = action.call(10, 20) }
40
+ ok {pr1}.raise_nothing?
41
+ ok {ret1} == [10, 20, nil]
42
+ ret2 = nil
43
+ pr2 = proc { ret2 = action.call(10, 20, 30) }
44
+ ok {pr2}.raise_nothing?
45
+ ok {ret2} == [10, 20, 30]
46
+ end
47
+
48
+ end
49
+
50
+
51
+ end
52
+
53
+
54
+ end
@@ -0,0 +1,547 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+
5
+ require_relative './init'
6
+
7
+
8
+ Oktest.scope do
9
+
10
+
11
+ topic CLIApp::Application do
12
+
13
+ fixture :config do
14
+ CLIApp::Config.new(name: "Sample", desc: "Sample Application",
15
+ command: "sample", version: "0.1.2")
16
+ end
17
+
18
+ fixture :app do |config|
19
+ app = CLIApp::Application.new(config) do |gopts|
20
+ if gopts[:debug]
21
+ $debug_mode = true
22
+ true
23
+ else
24
+ nil
25
+ end
26
+ end
27
+ app.global_options({
28
+ :help => ["-h", "--help" , "print help message"],
29
+ :version => ["-V", "--version" , "print version number"],
30
+ })
31
+ app.action("hello", "greeting message", {
32
+ :lang => ["-l", "--lang=<en|fr|it>", "language", ["en", "it", "fr"]],
33
+ }) do |name="world", lang: "en"|
34
+ case lang
35
+ when "en" ; puts "Hello, #{name}!"
36
+ when "fr" ; puts "Bonjour, #{name}!"
37
+ when "it" ; puts "Chao, #{name}!"
38
+ else raise "** internal error: lang=#{lang.inspect}"
39
+ end
40
+ end
41
+ app.action("clean", "delete garbage files (& product files too if '-a')", {
42
+ #:all => ["-a", "--all", "delete product files, too"],
43
+ }) do |all: false|
44
+ #require 'fileutils' unless defined?(FileUtils)
45
+ #FileUtils.rm_r(Dir.glob(GARBAGE_FILES))
46
+ #FileUtils.rm_r(Dir.glob(PRODUCT_FILES)) if all
47
+ end
48
+ app
49
+ end
50
+
51
+ APP_HELP = <<'END'
52
+ Sample (0.1.2) --- Sample Application
53
+
54
+ Usage:
55
+ $ sample [<options>] <action> [<arguments>...]
56
+
57
+ Options:
58
+ -h, --help print help message
59
+ -V, --version print version number
60
+
61
+ Actions:
62
+ clean delete garbage files (& product files too if '-a')
63
+ hello greeting message
64
+ END
65
+
66
+
67
+ topic '#global_options()' do
68
+
69
+ spec "[!2kq26] accepts global option schema." do
70
+ |app|
71
+ dict = app.instance_eval { @gopts_schema }
72
+ ok {dict} == {
73
+ :help => ["-h", "--help" , "print help message"],
74
+ :version => ["-V", "--version" , "print version number"],
75
+ }
76
+ end
77
+
78
+ end
79
+
80
+
81
+ topic '#action()' do
82
+
83
+ spec "[!i1jjg] converts action name into string." do
84
+ |app|
85
+ action = app.action(:foo, "Foo") do nil end
86
+ ok {action.name} == "foo"
87
+ end
88
+
89
+ spec "[!kculn] registers an action." do
90
+ |app|
91
+ action = app.action(:bar, "Bar") do nil end
92
+ ok {app.get_action("bar")}.same?(action)
93
+ ok {app.get_action(:bar) }.same?(action)
94
+ end
95
+
96
+ spec "[!82n8q] returns an action object." do
97
+ |app|
98
+ action = app.action(:baz, "Baz") do nil end
99
+ ok {action}.is_a?(CLIApp::Action)
100
+ ok {action.name} == "baz"
101
+ end
102
+
103
+ end
104
+
105
+
106
+ topic '#get_action()' do
107
+
108
+ spec "[!hop4z] returns action object if found, nil if else." do
109
+ |app|
110
+ ok {app.get_action("hello")} != nil
111
+ ok {app.get_action("hello")}.is_a?(CLIApp::Action)
112
+ ok {app.get_action(:hello)} != nil
113
+ ok {app.get_action(:hello)}.is_a?(CLIApp::Action)
114
+ ok {app.get_action("helloxx")} == nil
115
+ end
116
+
117
+ end
118
+
119
+
120
+ topic '#each_action()' do
121
+
122
+ spec "[!u46wo] returns Enumerator object if block not given." do
123
+ |app|
124
+ ok {app.each_action()}.is_a?(Enumerator)
125
+ ok {app.each_action().collect {|a| a.name }} == ["hello", "clean"]
126
+ end
127
+
128
+ spec "[!yorp6] if `sort: true` passed, sort actions by name." do
129
+ |app|
130
+ anames = []
131
+ app.each_action(sort: true) {|a| anames << a.name }
132
+ ok {anames} == ["clean", "hello"]
133
+ #
134
+ anames = []
135
+ app.each_action(sort: false) {|a| anames << a.name }
136
+ ok {anames} == ["hello", "clean"]
137
+ end
138
+
139
+ spec "[!ealgm] yields each action object." do
140
+ |app|
141
+ app.each_action() do |a|
142
+ ok {a}.is_a?(CLIApp::Action)
143
+ end
144
+ end
145
+
146
+ end
147
+
148
+
149
+ topic '#run()' do
150
+
151
+ spec "[!qv5fz] parses global options (not parses action options)." do
152
+ |app|
153
+ sout = capture_stdout do
154
+ app.run("hello", "--help")
155
+ end
156
+ ok {sout} =~ /^sample hello --- greeting message$/
157
+ end
158
+
159
+ spec "[!kveua] handles global options such as '--help'." do
160
+ |app|
161
+ sout = capture_stdout do
162
+ app.run("--help", "hello")
163
+ end
164
+ ok {sout} =~ /^Sample \(0\.1\.2\) --- Sample Application$/
165
+ end
166
+
167
+ spec "[!j029i] prints help message if no action name specified." do
168
+ |app|
169
+ sout = capture_stdout { app.run() }
170
+ ok {sout} =~ /^Sample \(0\.1\.2\) --- Sample Application$/
171
+ end
172
+
173
+ spec "[!43u4y] raises error if action name is unknown." do
174
+ |app|
175
+ pr = proc { app.run("helloxx") }
176
+ ok {pr}.raise?(CLIApp::ActionNotFoundError,
177
+ "helloxx: Action not found.")
178
+ end
179
+
180
+ spec "[!lm0ir] parses all action options even after action args." do
181
+ |app|
182
+ sout = capture_stdout do
183
+ app.run("hello", "Alice", "--lang=fr")
184
+ end
185
+ ok {sout} == "Bonjour, Alice!\n"
186
+ end
187
+
188
+ spec "[!ehshp] prints action help if action option contains help option." do
189
+ |app|
190
+ sout = capture_stdout do
191
+ app.run("hello", "Alice", "--help")
192
+ end
193
+ ok {sout} =~ /^sample hello --- greeting message$/
194
+ end
195
+
196
+ spec "[!0nwwe] invokes an action with action args and options." do
197
+ |app|
198
+ sout = capture_stdout do
199
+ app.run("hello", "--lang=it", "Haruhi")
200
+ end
201
+ ok {sout} == "Chao, Haruhi!\n"
202
+ end
203
+
204
+ end
205
+
206
+
207
+ topic '#handle_global_options()' do
208
+
209
+ spec "[!6n0w0] when '-h' or '--help' specified, prints help message and returns true." do
210
+ |app|
211
+ ret = nil
212
+ sout = capture_stdout do
213
+ ret = app.handle_global_options({help: true})
214
+ end
215
+ ok {sout} =~ /^Sample \(0\.1\.2\) --- Sample Application$/
216
+ ok {ret} == true
217
+ end
218
+
219
+ spec "[!zii8c] when '-V' or '--version' specified, prints version number and returns true." do
220
+ |app|
221
+ ret = nil
222
+ sout = capture_stdout do
223
+ ret = app.handle_global_options({version: true})
224
+ end
225
+ ok {sout} == "0.1.2\n"
226
+ ok {ret} == true
227
+ end
228
+
229
+ spec "[!csw5l] when '-l' or '--list' specified, prints action list and returns true." do
230
+ |app|
231
+ ret = nil
232
+ sout = capture_stdout do
233
+ ret = app.handle_global_options({list: true})
234
+ end
235
+ ok {sout} == <<'END'
236
+ clean : delete garbage files (& product files too if '-a')
237
+ hello : greeting message
238
+ END
239
+ ok {ret} == true
240
+ end
241
+
242
+ spec "[!5y8ph] if global option handler block specified, call it." do
243
+ |app|
244
+ ret = nil
245
+ sout = capture_stdout do
246
+ ret = app.handle_global_options({list: true})
247
+ end
248
+ ok {sout} == <<'END'
249
+ clean : delete garbage files (& product files too if '-a')
250
+ hello : greeting message
251
+ END
252
+ ok {ret} == true
253
+ end
254
+
255
+ spec "[!s816x] returns nil if global options are not handled." do
256
+ |app|
257
+ $debug_mode = false
258
+ at_end { $debug_mode = nil }
259
+ ret = nil
260
+ sout = capture_stdout do
261
+ ret = app.handle_global_options({debug: true})
262
+ end
263
+ ok {$debug_mode} == true
264
+ ok {ret} == true
265
+ end
266
+
267
+ end
268
+
269
+
270
+ topic '#application_help_message()' do
271
+
272
+ spec "[!p02s2] builds application help message." do
273
+ |app|
274
+ ok {app.application_help_message()} == APP_HELP
275
+ end
276
+
277
+ spec "[!41l2g] includes version number if it is specified." do
278
+ |app|
279
+ ok {app.config.version} == "0.1.2"
280
+ ok {app.application_help_message()} =~ /^Sample \(0\.1\.2\) ---/
281
+ app2 = CLIApp.new("Sample", "Sample App", command: "sample", version: nil)
282
+ ok {app2.application_help_message()} =~ /^Sample ---/
283
+ end
284
+
285
+ spec "[!2eycw] includes 'Options:' section if any global options exist." do
286
+ |app|
287
+ ok {app.application_help_message()} =~ /^Options:$/
288
+ ok {app.application_help_message()} =~ /^ \$ sample \[<options>\] <action> \[<arguments>\.\.\.\]$/
289
+ app2 = CLIApp.new("Sample", "Sample App", command: "sample", version: nil)
290
+ app2.action("hello", "Hello") do end
291
+ ok {app2.application_help_message()} !~ /^Options:$/
292
+ ok {app2.application_help_message()} =~ /^ \$ sample <action> \[<arguments>\.\.\.\]$/
293
+ end
294
+
295
+ spec "[!x3dim] includes 'Actions:' section if any actions defined." do
296
+ |app|
297
+ ok {app.application_help_message()} =~ /^Actions:$/
298
+ ok {app.application_help_message()} =~ /^ \$ sample \[<options>\] <action> \[<arguments>\.\.\.\]$/
299
+ app2 = CLIApp.new("Sample", "Sample App", command: "sample", version: nil)
300
+ app2.global_options({:version=>["-V", "version"]})
301
+ ok {app2.application_help_message()} !~ /^Actions:$/
302
+ ok {app2.application_help_message()} =~ /^ \$ sample \[<options>\]$/
303
+ end
304
+
305
+ spec "[!vxcin] help message will be affcted by config." do
306
+ |app|
307
+ app.config.help_indent = " | "
308
+ app.config.help_option_width = 14
309
+ app.config.help_action_width = 6
310
+ ok {app.application_help_message()} == <<'END'
311
+ Sample (0.1.2) --- Sample Application
312
+
313
+ Usage:
314
+ | $ sample [<options>] <action> [<arguments>...]
315
+
316
+ Options:
317
+ | -h, --help print help message
318
+ | -V, --version print version number
319
+
320
+ Actions:
321
+ | clean delete garbage files (& product files too if '-a')
322
+ | hello greeting message
323
+ END
324
+ end
325
+
326
+ end
327
+
328
+
329
+ topic '#action_help_message()' do
330
+
331
+ ACTION_HELP = <<'END'
332
+ sample hello --- greeting message
333
+
334
+ Usage:
335
+ $ sample hello [<options>] [<name>]
336
+
337
+ Options:
338
+ -l, --lang=<en|fr|it> language
339
+ END
340
+
341
+ spec "[!ny72g] build action help message." do
342
+ |app|
343
+ action = app.get_action(:hello)
344
+ ok {app.action_help_message(action)} == ACTION_HELP
345
+ end
346
+
347
+ spec "[!pr2vy] includes 'Options:' section if any options exist." do
348
+ |app|
349
+ hello = app.get_action(:hello)
350
+ ok {app.action_help_message(hello)} =~ /^Options:$/
351
+ ok {app.action_help_message(hello)} =~ /^ \$ sample hello \[<options>\] \[<name>\]$/
352
+ clean = app.get_action(:clean)
353
+ ok {app.action_help_message(clean)} !~ /^Options:$/
354
+ ok {app.action_help_message(clean)} =~ /^ \$ sample clean$/
355
+ end
356
+
357
+ spec "[!1xggx] help message will be affcted by config." do
358
+ |app|
359
+ app.config.help_indent = " | "
360
+ app.config.help_option_width = 25
361
+ hello = app.get_action(:hello)
362
+ ok {app.action_help_message(hello)} == <<'END'
363
+ sample hello --- greeting message
364
+
365
+ Usage:
366
+ | $ sample hello [<options>] [<name>]
367
+
368
+ Options:
369
+ | -l, --lang=<en|fr|it> language
370
+ END
371
+ end
372
+
373
+ end
374
+
375
+
376
+ topic '#parse_global_options()' do
377
+
378
+ spec "[!o83ty] parses global options and returns it." do
379
+ |app|
380
+ args = ["-h", "--version", "hello", "--help", "--lang=it"]
381
+ opts = app.parse_global_options(args)
382
+ ok {opts} == {help: true, version: true}
383
+ ok {args} == ["hello", "--help", "--lang=it"]
384
+ end
385
+
386
+ end
387
+
388
+
389
+ topic '#parse_action_options()' do
390
+
391
+ spec "[!5m767] parses action options and returns it." do
392
+ |app|
393
+ action = app.get_action(:hello)
394
+ args = ["Alice", "-h", "--lang=it", "Carol"]
395
+ opts = app.parse_action_options(action, args)
396
+ ok {opts} == {help: true, lang: "it"}
397
+ ok {args} == ["Alice", "Carol"]
398
+ end
399
+
400
+ spec "[!k2cto] adds '-h, --help' option automatically." do
401
+ |app|
402
+ action = app.get_action(:clean)
403
+ ok {action.option_schema.keys()} == []
404
+ args = ["--help"]
405
+ opts = app.parse_action_options(action, args)
406
+ ok {opts} == {help: true}
407
+ end
408
+
409
+ end
410
+
411
+
412
+ topic '#prepare_parser()' do
413
+
414
+ spec "[!vcgq0] adds all option schema into parser." do
415
+ |app|
416
+ parser = app.instance_eval do
417
+ parser = new_parser()
418
+ prepare_parser(parser, {:quiet=>["-q", "Quiet"]})
419
+ parser
420
+ end
421
+ ok {parser.summarize()[0]} =~ /^ +-q +Quiet$/
422
+ end
423
+
424
+ spec "[!lcpvw] returns hash object which stores options." do
425
+ |app|
426
+ parser = opts = nil
427
+ app.instance_eval do
428
+ parser = new_parser()
429
+ opts = prepare_parser(parser, {:quiet=>["-q", "Quiet"]})
430
+ end
431
+ ok {opts} == {}
432
+ parser.parse(["-q"])
433
+ ok {opts} == {quiet: true}
434
+ end
435
+
436
+ end
437
+
438
+
439
+ topic '#new_parser()' do
440
+
441
+ spec "[!lnbpm] creates new parser object." do
442
+ |app|
443
+ parser = app.instance_eval { new_parser() }
444
+ ok {parser}.is_a?(OptionParser)
445
+ end
446
+
447
+ end
448
+
449
+
450
+ topic '#option_help_message()' do
451
+
452
+ spec "[!lfnlq] builds help message of options." do
453
+ |app|
454
+ schema = {
455
+ :verbose => ["-v", "--verbose", "Verbse mode"],
456
+ :quiet => ["-q", "Quiet mode"],
457
+ }
458
+ str = app.instance_eval {
459
+ option_help_message(schema, width: 20, indent: " ")
460
+ }
461
+ ok {str} == <<END
462
+ -v, --verbose Verbse mode
463
+ -q Quiet mode
464
+ END
465
+ end
466
+
467
+ end
468
+
469
+
470
+ topic '#list_actions()' do
471
+
472
+ spec "[!g99qx] returns list of action names and descriptions as a string." do
473
+ |app|
474
+ str = app.instance_eval { list_actions() }
475
+ ok {str} == <<'END'
476
+ clean : delete garbage files (& product files too if '-a')
477
+ hello : greeting message
478
+ END
479
+ end
480
+
481
+ spec "[!rl5hs] sorts actions by name." do
482
+ |app|
483
+ str = app.instance_eval { list_actions() }
484
+ names = str.each_line.grep(/^(\w+)/) { $1 }
485
+ ok {names} == ["clean", "hello"]
486
+ end
487
+
488
+ spec "[!rlak5] print only the first line of multiline description." do
489
+ |app|
490
+ app.get_action(:clean).instance_variable_set(:@desc, "AA\nBB\nCC")
491
+ str = app.instance_eval { list_actions() }
492
+ ok {str} == <<END
493
+ clean : AA
494
+ hello : greeting message
495
+ END
496
+ end
497
+
498
+ spec "[!1xggx] output will be affcted by config." do
499
+ |app|
500
+ app.config.actionlist_width = 7
501
+ str = app.instance_eval { list_actions() }
502
+ ok {str} == <<'END'
503
+ clean : delete garbage files (& product files too if '-a')
504
+ hello : greeting message
505
+ END
506
+ #
507
+ app.config.actionlist_format = " - %-10s # %s"
508
+ str = app.instance_eval { list_actions() }
509
+ ok {str} == <<'END'
510
+ - clean # delete garbage files (& product files too if '-a')
511
+ - hello # greeting message
512
+ END
513
+ end
514
+
515
+ end
516
+
517
+
518
+ topic '#do_when_action_not_specified()' do
519
+
520
+ spec "[!w5lq9] prints application help message." do
521
+ |app|
522
+ sout = capture_stdout() do
523
+ app.instance_eval do
524
+ do_when_action_not_specified({})
525
+ end
526
+ end
527
+ ok {sout} == APP_HELP
528
+ end
529
+
530
+ spec "[!txqnr] returns true which means 'done'." do
531
+ |app|
532
+ ret = nil
533
+ capture_stdout() do
534
+ app.instance_eval do
535
+ ret = do_when_action_not_specified({})
536
+ end
537
+ end
538
+ ok {ret} == true
539
+ end
540
+
541
+ end
542
+
543
+
544
+ end
545
+
546
+
547
+ end
data/test/init.rb ADDED
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'oktest'
5
+
6
+ require 'cliapp'
7
+
8
+
9
+ Oktest.global_scope do
10
+
11
+ end