cliapp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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