benry-cmdapp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,2475 @@
1
+ # Benry-CmdApp
2
+
3
+ ($Release: 0.1.0 $)
4
+
5
+
6
+ ## What's This?
7
+
8
+ Benry-CmdApp is a framework to create command-line application.
9
+ If you want create command-line application which takes sub-commands
10
+ like `git`, `docker`, or `npm`, Benry-CmdApp is the solution.
11
+
12
+ Base idea:
13
+
14
+ * Sub-command (= action) is defined as a method in Ruby.
15
+ * Commnad-line arguments are passed to action method as positional arguments.
16
+ * Command-line options are passed to action method as keyword arguments.
17
+
18
+ For example:
19
+
20
+ * `<command> foo` in command-line invokes action method `foo()` in Ruby.
21
+ * `<command> foo arg1 arg2` invokes `foo("arg1", "arg2")`.
22
+ * `<command> foo arg --opt=val` invokes `foo("arg", opt: "val")`.
23
+
24
+ Links:
25
+
26
+ * Document: <https://kwatch.github.io/benry-ruby/benry-cmdapp.html>
27
+ * GitHub: <https://github.com/kwatch/benry-ruby/tree/main/benry-cmdapp>
28
+ * Changes: <https://github.com/kwatch/benry-ruby/tree/main/benry-cmdapp/CHANGES.md>
29
+
30
+ Benry-CmdApp requires Ruby >= 2.3.
31
+
32
+
33
+ ### Table of Contents
34
+
35
+ <!-- TOC -->
36
+
37
+ * [What's This?](#whats-this)
38
+ * [Install](#install)
39
+ * [Usage](#usage)
40
+ * [Action](#action)
41
+ * [Method Name and Action Name](#method-name-and-action-name)
42
+ * [Parameter Name in Help Message of Action](#parameter-name-in-help-message-of-action)
43
+ * [Options](#options)
44
+ * [Option Definition Format](#option-definition-format)
45
+ * [Option Value Validation](#option-value-validation)
46
+ * [Callback for Option Value](#callback-for-option-value)
47
+ * [Boolean (On/Off) Option](#boolean-onoff-option)
48
+ * [Prefix of Action Name](#prefix-of-action-name)
49
+ * [Invoke Other Action](#invoke-other-action)
50
+ * [Action Alias](#action-alias)
51
+ * [Default Action](#default-action)
52
+ * [Default Help](#default-help)
53
+ * [Private (Hidden) Action](#private-hidden-action)
54
+ * [Private (Hidden) Option](#private-hidden-option)
55
+ * [Configuratoin and Customization](#configuratoin-and-customization)
56
+ * [Application Configuration](#application-configuration)
57
+ * [Customization of Global Options](#customization-of-global-options)
58
+ * [Customization of Global Option Behaviour](#customization-of-global-option-behaviour)
59
+ * [Custom Hook of Application](#custom-hook-of-application)
60
+ * [Customization of Command Help Message](#customization-of-command-help-message)
61
+ * [Customization of Action Help Message](#customization-of-action-help-message)
62
+ * [Q & A](#q--a)
63
+ * [Q: How to Append Some Tasks to Existing Action?](#q-how-to-append-some-tasks-to-existing-action)
64
+ * [Q: How to Re-define Existing Action?](#q-how-to-re-define-existing-action)
65
+ * [Q: How to Delete Existing Action/Alias?](#q-how-to-delete-existing-actionalias)
66
+ * [Q: How to Show Entering Into or Exitting From Action?](#q-how-to-show-entering-into-or-exitting-from-action)
67
+ * [Q: How to Enable/Disable Color Mode?](#q-how-to-enabledisable-color-mode)
68
+ * [Q: How to Define Multiple Option, like `-I` Option of Ruby?](#q-how-to-define-multiple-option-like--i-option-of-ruby)
69
+ * [Q: How to Specify Detailed Description of Option?](#q-how-to-specify-detailed-description-of-option)
70
+ * [Q: How to Copy All Options from Other Action?](#q-how-to-copy-all-options-from-other-action)
71
+ * [Q: What is the Difference Between `prefix(alias_of:)` and `prefix(action:)`?](#q-what-is-the-difference-between-prefixalias_of-and-prefixaction)
72
+ * [Q: How to Change Order of Options in Help Message?](#q-how-to-change-order-of-options-in-help-message)
73
+ * [Q: Is It Possible to Make Action Names Emphasised or Weaken?](#q-is-it-possible-to-make-action-names-emphasised-or-weaken)
74
+ * [Q: Is It Possible to Add Metadata to Action or Option?](#q-is-it-possible-to-add-metadata-to-action-or-option)
75
+ * [Q: How to Make Error Messages I18Ned?](#q-how-to-make-error-messages-i18ned)
76
+ * [License and Copyright](#license-and-copyright)
77
+
78
+ <!-- /TOC -->
79
+
80
+
81
+
82
+ ## Install
83
+
84
+ ```console
85
+ $ gem install benry-cmdapp
86
+ ```
87
+
88
+
89
+
90
+ ## Usage
91
+
92
+
93
+ ### Action
94
+
95
+ * Inherit action class and define action methods in it.
96
+ * An action class can have several action methods.
97
+ * It is ok to define multiple action classes.
98
+ * Command-line arguments are passed to action method as positional arguments.
99
+
100
+ File: ex01.rb
101
+
102
+ ```ruby
103
+ #!/usr/bin/env ruby
104
+ require 'benry/cmdapp'
105
+
106
+ ## action
107
+ class MyAction < Benry::CmdApp::Action # !!!!
108
+
109
+ @action.("print greeting message") # !!!!
110
+ def hello(user="world") # !!!!
111
+ puts "Hello, #{user}!"
112
+ end
113
+
114
+ end
115
+
116
+ ## configuration
117
+ config = Benry::CmdApp::Config.new("sample app", "1.0.0")
118
+ config.default_help = true
119
+
120
+ ## run application
121
+ app = Benry::CmdApp::Application.new(config)
122
+ status_code = app.main()
123
+ exit status_code
124
+ ```
125
+
126
+ Output:
127
+
128
+ ```console
129
+ [bash]$ ruby ex01.rb hello # action
130
+ Hello, world!
131
+
132
+ [bash]$ ruby ex01.rb hello Alice # action + argument
133
+ Hello, Alice!
134
+ ```
135
+
136
+ Help message of command:
137
+
138
+ ```console
139
+ [bash]$ ruby ex01.rb -h # or `--help`
140
+ ex01.rb (1.0.0) -- sample app
141
+
142
+ Usage:
143
+ $ ex01.rb [<options>] [<action> [<arguments>...]]
144
+
145
+ Options:
146
+ -h, --help : print help message (of action if action specified)
147
+ -V, --version : print version
148
+
149
+ Actions:
150
+ hello : print greeting message
151
+ ```
152
+
153
+ Help message of action:
154
+
155
+ ```console
156
+ [bash]$ ruby ex01.rb -h hello
157
+ ex01.rb hello -- print greeting message
158
+
159
+ Usage:
160
+ $ ex01.rb hello [<user>]
161
+ ```
162
+
163
+
164
+ ### Method Name and Action Name
165
+
166
+ * Method name `print_` results in action name `print`.
167
+ This is useful to define actions which name is same as Ruby keyword or popular functions.
168
+ * Method name `foo_bar_baz` results in action name `foo-bar-baz`.
169
+ * Method name `foo__bar__baz` results in action name `foo:bar:baz`.
170
+
171
+ File: ex02.rb
172
+
173
+ ```ruby
174
+ #!/usr/bin/env ruby
175
+ require 'benry/cmdapp'
176
+
177
+ class SampleAction < Benry::CmdApp::Action
178
+
179
+ ## 'print_' => 'print'
180
+ @action.("sample #1")
181
+ def print_() # !!!!
182
+ puts __method__
183
+ end
184
+
185
+ ## 'foo_bar_baz' => 'foo-bar-baz'
186
+ @action.("sample #2")
187
+ def foo_bar_baz() # !!!!
188
+ puts __method__
189
+ end
190
+
191
+ ## 'foo__bar__baz' => 'foo:bar:baz'
192
+ @action.("sample #3")
193
+ def foo__bar__baz() # !!!!
194
+ puts __method__
195
+ end
196
+
197
+ end
198
+
199
+ config = Benry::CmdApp::Config.new("test app")
200
+ app = Benry::CmdApp::Application.new(config)
201
+ exit app.main()
202
+ ```
203
+
204
+ Help message:
205
+
206
+ ```console
207
+ [bash]$ ruby ex02.rb --help
208
+ ex02.rb -- test app
209
+
210
+ Usage:
211
+ $ ex02.rb [<options>] [<action> [<arguments>...]]
212
+
213
+ Options:
214
+ -h, --help : print help message (of action if action specified)
215
+
216
+ Actions:
217
+ foo-bar-baz : sample #2
218
+ foo:bar:baz : sample #3
219
+ print : sample #1
220
+ ```
221
+
222
+ Output:
223
+
224
+ ```console
225
+ [bash]$ ruby ex02.rb print # `print_` method
226
+ print_
227
+
228
+ [bash]$ ruby ex02.rb foo-bar-baz # `foo_bar_baz` method
229
+ foo_bar_baz
230
+
231
+ [bash]$ ruby ex02.rb foo:bar:baz # `foo__bar__baz` method
232
+ foo__bar__baz
233
+ ```
234
+
235
+
236
+ ### Parameter Name in Help Message of Action
237
+
238
+ In help message of action, positional parameters of action methods are printed under the name conversion rule.
239
+
240
+ * Parameter `foo` is printed as `<foo>`.
241
+ * Parameter `foo_bar_baz` is printed as `<foo-bar-baz>`.
242
+ * Parameter `foo_or_bar_or_baz` is printed as `<foo|bar|baz>`.
243
+
244
+ In addition, positional parameters are printed in different way according to its kind.
245
+
246
+ * If parameter `foo` is required (= doesn't have default value), it will be printed as `<foo>`.
247
+ * If parameter `foo` is optional (= has default value), it will be printed as `[<foo>]`.
248
+ * If parameter `foo` is variable length (= `*foo` style), it will be printed as `[<foo>...]`.
249
+
250
+
251
+ File: ex03.rb
252
+
253
+ ```ruby
254
+ #!/usr/bin/env ruby
255
+ require 'benry/cmdapp'
256
+
257
+ class SampleAction < Benry::CmdApp::Action
258
+
259
+ @action.("parameter names test")
260
+ def test1(aaa, bbb_or_ccc, ddd=nil, eee=nil, *fff) # !!!!
261
+ # ...
262
+ end
263
+
264
+ end
265
+
266
+ config = Benry::CmdApp::Config.new("sample app")
267
+ app = Benry::CmdApp::Application.new(config)
268
+ exit app.main()
269
+ ```
270
+
271
+ Help message:
272
+
273
+ ```console
274
+ [bash]$ ruby ex03.rb -h test1
275
+ hoge.rb test1 -- parameter names test
276
+
277
+ Usage:
278
+ $ ex03.rb test1 <aaa> <bbb|ccc> [<ddd> [<eee> [<fff>...]]] # !!!!
279
+ ```
280
+
281
+
282
+ ### Options
283
+
284
+ * Action can take command-line options.
285
+ * Option values specified in command-line are passed to actio method as keyword arguments.
286
+
287
+ File: ex04.rb
288
+
289
+ ```ruby
290
+ #!/usr/bin/env ruby
291
+ require 'benry/cmdapp'
292
+
293
+ ## action
294
+ class MyAction < Benry::CmdApp::Action
295
+
296
+ @action.("print greeting message")
297
+ @option.(:lang, "-l, --lang=<en|fr|it>", "language") # !!!!
298
+ def hello(user="world", lang: "en") # !!!!
299
+ case lang
300
+ when "en" ; puts "Hello, #{user}!"
301
+ when "fr" ; puts "Bonjour, #{user}!"
302
+ when "it" ; puts "Ciao, #{user}!"
303
+ else
304
+ raise "#{lang}: unknown language."
305
+ end
306
+ end
307
+
308
+ end
309
+
310
+ ## configuration
311
+ config = Benry::CmdApp::Config.new("sample app", "1.0.0")
312
+ config.default_help = true
313
+
314
+ ## run application
315
+ app = Benry::CmdApp::Application.new(config)
316
+ status_code = app.main()
317
+ exit status_code
318
+ ```
319
+
320
+ Output:
321
+
322
+ ```console
323
+ [bash]$ ruby ex04.rb hello
324
+ Hello, world!
325
+
326
+ [bash]$ ruby ex04.rb hello -l fr # !!!!
327
+ Bonjour, world!
328
+
329
+ [bash]$ ruby ex04.rb hello --lang=it # !!!!
330
+ Ciao, world!
331
+ ```
332
+
333
+ * An action can have multiple options.
334
+ * Option format can have indentation spaces, for example `' --help'`.
335
+
336
+ File: ex05.rb
337
+
338
+ ```ruby
339
+ #!/usr/bin/env ruby
340
+ require 'benry/cmdapp'
341
+
342
+ ## action
343
+ class MyAction < Benry::CmdApp::Action
344
+
345
+ @action.("print greeting message")
346
+ @option.(:lang , "-l, --lang=<en|fr|it>", "language")
347
+ @option.(:repeat, " --repeat=<N>", "repeat <N> times") # !!!!
348
+ def hello(user="world", lang: "en", repeat: "1")
349
+ #p repeat.class #=> String # !!!!
350
+ repeat.to_i.times do # !!!!
351
+ case lang
352
+ when "en" ; puts "Hello, #{user}!"
353
+ when "fr" ; puts "Bonjour, #{user}!"
354
+ when "it" ; puts "Ciao, #{user}!"
355
+ else
356
+ raise "#{lang}: unknown language."
357
+ end
358
+ end
359
+ end
360
+
361
+ end
362
+
363
+ ## configuration
364
+ config = Benry::CmdApp::Config.new("sample app", "1.0.0")
365
+ config.default_help = true
366
+
367
+ ## run application
368
+ app = Benry::CmdApp::Application.new(config)
369
+ status_code = app.main()
370
+ exit status_code
371
+ ```
372
+
373
+ Output:
374
+
375
+ ```console
376
+ [bash]$ ruby ex05.rb hello Alice -l fr --repeat=3
377
+ Bonjour, Alice!
378
+ Bonjour, Alice!
379
+ Bonjour, Alice!
380
+ `````
381
+
382
+ Help message:
383
+
384
+ ```console
385
+ [bash]$ ruby ex05.rb -h hello
386
+ ex05.rb hello -- print greeting message
387
+
388
+ Usage:
389
+ $ ex05.rb hello [<options>] [<user>]
390
+
391
+ Options:
392
+ -l, --lang=<en|fr|it> : language # !!!!
393
+ --repeat=<N> : repeat <N> times # !!!!
394
+ ```
395
+
396
+ For usability reason, Benry::CmdApp supports `--lang=<val>` style long option
397
+ and doesn't support `--lang <val>` style option.
398
+ Benry::CmdApp regards `--lang <val>` as 'long option without argument'
399
+ and 'argument for command'.
400
+
401
+ ```console
402
+ [bash]$ ruby ex05.rb hello --lang fr # ``--lang fr`` != ``--lang=fr``
403
+ [ERROR] --lang: argument required.
404
+ ```
405
+
406
+
407
+ ### Option Definition Format
408
+
409
+ Option definition format should be one of:
410
+
411
+ * (short option) `-q` : no values.
412
+ * (short option) `-f <file>` : value required.
413
+ * (short option) `-i[<width>]` : value is optional.
414
+ * (long option) `--quiet` : no values.
415
+ * (long option) `--file=<file>` : value required.
416
+ * (long option) `--indent[=<width>]` : value is optional.
417
+ * (short & long) `-q, --quiet` : no values.
418
+ * (short & long) `-f, --file=<file>` : value required.
419
+ * (short & long) `-i, --indent[=<width>]` : value is optional.
420
+
421
+ File: ex06.rb
422
+
423
+ ```ruby
424
+ #!/usr/bin/env ruby
425
+ require 'benry/cmdapp'
426
+
427
+ class SampleAction < Benry::CmdApp::Action
428
+
429
+ ## short options
430
+ @action.("short options")
431
+ @option.(:quiet , "-q" , "quiet mode") # none
432
+ @option.(:file , "-f <file>" , "filename") # required
433
+ @option.(:indent , "-i[<N>]" , "indent width") # optional
434
+ def test1(quiet: false, file: nil, indent: nil)
435
+ puts "quiet=#{quiet.inspect}, file=#{file.inspect}, indent=#{indent.inspect}"
436
+ end
437
+
438
+ ## long options
439
+ @action.("long options")
440
+ @option.(:quiet , "--quiet" , "quiet mode") # none
441
+ @option.(:file , "--file=<file>" , "filename") # required
442
+ @option.(:indent , "--indent[=<N>]" , "indent width") # optional
443
+ def test2(quiet: false, file: nil, indent: nil)
444
+ puts "quiet=#{quiet.inspect}, file=#{file.inspect}, indent=#{indent.inspect}"
445
+ end
446
+
447
+ ## short and long options
448
+ @action.("short and long options")
449
+ @option.(:quiet , "-q, --quiet" , "quiet mode") # none
450
+ @option.(:file , "-f, --file=<file>" , "filename") # required
451
+ @option.(:indent , "-i, --indent[=<N>]" , "indent width") # optional
452
+ def test3(quiet: false, file: nil, indent: nil)
453
+ puts "quiet=#{quiet.inspect}, file=#{file.inspect}, indent=#{indent.inspect}"
454
+ end
455
+
456
+ end
457
+
458
+ config = Benry::CmdApp::Config.new("test app")
459
+ app = Benry::CmdApp::Application.new(config)
460
+ exit app.main()
461
+ ```
462
+
463
+ Output:
464
+
465
+ ```console
466
+ [bash]$ ruby ex06.rb test1 -q -f readme.txt -i4
467
+ quiet=true, file="readme.txt", indent="4"
468
+
469
+ [bash]$ ruby ex06.rb test2 --quiet --file=readme.txt --indent=4
470
+ quiet=true, file="readme.txt", indent="4"
471
+
472
+ [bash]$ ruby ex06.rb test3 -q -f readme.txt -i4
473
+ quiet=true, file="readme.txt", indent="4"
474
+ [bash]$ ruby ex06.rb test3 --quiet --file=readme.txt --indent=4
475
+ quiet=true, file="readme.txt", indent="4"
476
+ ```
477
+
478
+ Optional argument example:
479
+
480
+ ```console
481
+ [bash]$ ruby ex06.rb test1 -i # ``-i`` results in ``true``
482
+ quiet=false, file=nil, indent=true
483
+ [bash]$ ruby ex06.rb test1 -i4 # ``-i4`` results in ``4``
484
+ quiet=false, file=nil, indent="4"
485
+
486
+ [bash]$ ruby ex06.rb test2 --indent # ``--indent`` results in ``true``
487
+ quiet=false, file=nil, indent=true
488
+ [bash]$ ruby ex06.rb test2 --indent=4 # ``--indent=4`` results in ``4``
489
+ quiet=false, file=nil, indent="4"
490
+ ```
491
+
492
+ Help message:
493
+
494
+ ```ruby
495
+ [bash]$ ruby ex06.rb -h test1
496
+ ex06.rb test1 -- short options
497
+
498
+ Usage:
499
+ $ ex06.rb test1 [<options>]
500
+
501
+ Options:
502
+ -q : quiet mode
503
+ -f <file> : filename
504
+ -i[<N>] : indent width
505
+
506
+ [bash]$ ruby ex06.rb -h test2
507
+ ex06.rb test2 -- long options
508
+
509
+ Usage:
510
+ $ ex06.rb test2 [<options>]
511
+
512
+ Options:
513
+ --quiet : quiet mode
514
+ --file=<file> : filename
515
+ --indent[=<N>] : indent width
516
+
517
+ [bash]$ ruby ex06.rb -h test3
518
+ ex06.rb test3 -- short and long options
519
+
520
+ Usage:
521
+ $ ex06.rb test3 [<options>]
522
+
523
+ Options:
524
+ -q, --quiet : quiet mode
525
+ -f, --file=<file> : filename
526
+ -i, --indent[=<N>] : indent width
527
+ ```
528
+
529
+
530
+ ### Option Value Validation
531
+
532
+ `@option.()` can validate option value via keyword argument.
533
+
534
+ * `type: <class>` specifies option value class.
535
+ Currently supports `Integer`, `Float`, `TrueClass`, and `Date`.
536
+ * `rexp: <rexp>` specifies regular expression of option value.
537
+ * `enum: <array>` specifies available values as option value.
538
+ * `range: <range>` specifies range of option value.
539
+
540
+ File: ex07.rb
541
+
542
+ ```ruby
543
+ #!/usr/bin/env ruby
544
+ require 'benry/cmdapp'
545
+
546
+ ## action
547
+ class MyAction < Benry::CmdApp::Action
548
+
549
+ @action.("print greeting message")
550
+ @option.(:lang , "-l, --lang=<en|fr|it>", "language",
551
+ enum: ["en", "fr", "it"], # !!!!
552
+ rexp: /\A\w\w\z/) # !!!!
553
+ @option.(:repeat, " --repeat=<N>", "repeat <N> times",
554
+ type: Integer, range: 1..10) # !!!!
555
+ def hello(user="world", lang: "en", repeat: 1)
556
+ #p repeat.class #=> Integer
557
+ repeat.times do
558
+ case lang
559
+ when "en" ; puts "Hello, #{user}!"
560
+ when "fr" ; puts "Bonjour, #{user}!"
561
+ when "it" ; puts "Ciao, #{user}!"
562
+ else
563
+ raise "#{lang}: unknown language."
564
+ end
565
+ end
566
+ end
567
+
568
+ end
569
+
570
+ ## configuration
571
+ config = Benry::CmdApp::Config.new("sample app", "1.0.0")
572
+ config.default_help = true
573
+
574
+ ## run application
575
+ app = Benry::CmdApp::Application.new(config)
576
+ status_code = app.main()
577
+ exit status_code
578
+ ```
579
+
580
+ Output:
581
+
582
+ ```console
583
+ [bash]$ ruby ex07.rb hello -l japan
584
+ [ERROR] -l japan: pattern unmatched.
585
+
586
+ [bash]$ ruby ex07.rb hello -l ja
587
+ [ERROR] -l ja: expected one of en/fr/it.
588
+
589
+ [bash]$ ruby ex07.rb hello --repeat=abc
590
+ [ERROR] --repeat=abc: integer expected.
591
+
592
+ [bash]$ ruby ex07.rb hello --repeat=100
593
+ [ERROR] --repeat=100: Too large (max: 10).
594
+ ```
595
+
596
+
597
+ ### Callback for Option Value
598
+
599
+ `@option.()` can take a block argument which is a callback for option value.
600
+ Callback can:
601
+
602
+ * Do custom validation of option value.
603
+ * Convert option value into other value.
604
+
605
+ File: ex08.rb
606
+
607
+ ```ruby
608
+ #!/usr/bin/env ruby
609
+ require 'benry/cmdapp'
610
+
611
+ ## action
612
+ class MyAction < Benry::CmdApp::Action
613
+
614
+ @action.("print greeting message")
615
+ @option.(:lang , "-l, --lang=<en|fr|it>", "language",
616
+ enum: ["en", "fr", "it", "EN", "FR", "IT"],
617
+ rexp: /\A\w\w\z/) {|v| v.downcase } # !!!!
618
+ @option.(:repeat, " --repeat=<N>", "repeat <N> times",
619
+ type: Integer) {|v| # !!!!
620
+ v > 0 or raise "not positive value." # !!!!
621
+ v # !!!!
622
+ } # !!!!
623
+ def hello(user="world", lang: "en", repeat: 1)
624
+ repeat.times do
625
+ case lang
626
+ when "en" ; puts "Hello, #{user}!"
627
+ when "fr" ; puts "Bonjour, #{user}!"
628
+ when "it" ; puts "Ciao, #{user}!"
629
+ else
630
+ raise "#{lang}: unknown language."
631
+ end
632
+ end
633
+ end
634
+
635
+ end
636
+
637
+ ## configuration
638
+ config = Benry::CmdApp::Config.new("sample app", "1.0.0")
639
+ config.default_help = true
640
+
641
+ ## run application
642
+ app = Benry::CmdApp::Application.new(config)
643
+ status_code = app.main()
644
+ exit status_code
645
+ ```
646
+
647
+ Output:
648
+
649
+ ```console
650
+ [bash]$ ruby ex08.rb hello -l FR # converted into lowercase
651
+ Bonjour, world!
652
+
653
+ [bash]$ ruby ex08.rb hello --repeat=0
654
+ [ERROR] --repeat=0: not positive value.
655
+ ```
656
+
657
+
658
+ ### Boolean (On/Off) Option
659
+
660
+ Benry::CmdApp doesn't support `--[no-]foobar` style option.
661
+ Instead, define boolean (on/off) option.
662
+
663
+ * Specify `type: TrueClass` to `@option.()`.
664
+ * Option value `true`, `yes`, and `on` are converted into true.
665
+ * Option value `false`, `no`, and `off` are converted into false.
666
+
667
+ File: ex09.rb
668
+
669
+ ```ruby
670
+ #!/usr/bin/env ruby
671
+ require 'benry/cmdapp'
672
+
673
+ class SampleAction < Benry::CmdApp::Action
674
+
675
+ @action.("flag test")
676
+ @option.(:verbose, "--verbose[=<on|off>]", # !!!!
677
+ "verbose mode",
678
+ type: TrueClass) # !!!!
679
+ def flagtest(verbose: false) # !!!!
680
+ puts "verbose=#{verbose}"
681
+ end
682
+
683
+ end
684
+
685
+ config = Benry::CmdApp::Config.new("sample app", "1.0.0")
686
+ app = Benry::CmdApp::Application.new(config)
687
+ exit app.main()
688
+ ```
689
+
690
+ Output:
691
+
692
+ ```console
693
+ [bash]$ ruby ex09.rb flagtest --verbose=on # on
694
+ verbose=true
695
+
696
+ [bash]$ ruby ex09.rb flagtest --verbose=off # off
697
+ verbose=false
698
+
699
+ [bash]$ ruby ex09.rb flagtest --verbose=true # on
700
+ verbose=true
701
+
702
+ [bash]$ ruby ex09.rb flagtest --verbose=false # off
703
+ verbose=false
704
+
705
+ [bash]$ ruby ex09.rb flagtest --verbose=yes # on
706
+ verbose=true
707
+
708
+ [bash]$ ruby ex09.rb flagtest --verbose=no # off
709
+ verbose=false
710
+
711
+ [bash]$ ruby ex09.rb flagtest --verbose=abc # error
712
+ [ERROR] --verbose=abc: boolean expected.
713
+ ```
714
+
715
+ If you want default value of flag to `true`, use `value:` keyword argument.
716
+
717
+ * `value:` keyword argument in `@option.()` specifies the substitute value
718
+ instead of `true` when no option value specified in command-line.
719
+
720
+ File: ex10.rb
721
+
722
+ ```ruby
723
+ #!/usr/bin/env ruby
724
+ require 'benry/cmdapp'
725
+
726
+ class SampleAction < Benry::CmdApp::Action
727
+
728
+ @action.("flag test")
729
+ @option.(:verbose, "-q, --quiet", "quiet mode",
730
+ value: false) # !!!!
731
+ def flagtest2(verbose: true) # !!!!
732
+ puts "verbose=#{verbose.inspect}"
733
+ end
734
+
735
+ end
736
+
737
+ config = Benry::CmdApp::Config.new("git helper")
738
+ app = Benry::CmdApp::Application.new(config)
739
+ exit app.main()
740
+ ```
741
+
742
+ Output:
743
+
744
+ ```console
745
+ [bash]$ ruby ex10.rb flagtest2 # true if '--quiet' NOT specified
746
+ verbose=true
747
+
748
+ [bash]$ ruby ex10.rb flagtest2 --quiet # false if '--quiet' specified
749
+ verbose=false
750
+
751
+ [bash]$ ruby ex10.rb flagtest2 --quiet=on # error
752
+ [ERROR] --quiet=on: unexpected argument.
753
+ ```
754
+
755
+ In above example, `--quiet=on` will be error because option is defined as
756
+ `@option.(:verbose, "-q, --quiet", ...)` which means that this option takes no arguments.
757
+ If you want to allow `--quiet=on`, specify option argument and `type: TrueClass`.
758
+
759
+
760
+ ```ruby
761
+ ...(snip)...
762
+
763
+ @action.("flag test")
764
+ @option.(:verbose, "-q, --quiet[=<on|off]", "quiet mode", # !!!!
765
+ type: TrueClass, value: false) # !!!!
766
+ def flagtest2(verbose: true)
767
+ puts "verbose=#{verbose.inspect}"
768
+ end
769
+
770
+ ...(snip)...
771
+ ```
772
+
773
+
774
+ ### Prefix of Action Name
775
+
776
+ * `prefix: "foo:bar"` in action class adds prefix `foo:bar:` to each action name.
777
+ * Method name `def baz__test()` with `prefix: "foo:bar"` results in action name `foo:bar:baz:test`.
778
+
779
+ File: ex11.rb
780
+
781
+ ```ruby
782
+ #!/usr/bin/env ruby
783
+ require 'benry/cmdapp'
784
+
785
+ class SampleAction < Benry::CmdApp::Action
786
+ prefix "foo:bar" # !!!!
787
+
788
+ @action.("test action #1")
789
+ def test1() # action name: 'foo:bar:test1'
790
+ puts __method__
791
+ end
792
+
793
+ @action.("test action #2")
794
+ def baz__test2() # action name: 'foo:bar:baz:test2'
795
+ puts __method__
796
+ end
797
+
798
+ end
799
+
800
+ config = Benry::CmdApp::Config.new("sample app")
801
+ app = Benry::CmdApp::Application.new(config)
802
+ exit app.main()
803
+ ```
804
+
805
+ Output:
806
+
807
+ ```console
808
+ [bash]$ ruby ex11.rb foo:bar:test1
809
+ test1
810
+
811
+ [bash]$ ruby ex11.rb foo:bar:baz:test2
812
+ baz__test2
813
+ ```
814
+
815
+ Help message:
816
+
817
+ ```console
818
+ [bash]$ ruby ex11.rb -h
819
+ ex11.rb -- sample app
820
+
821
+ Usage:
822
+ $ ex11.rb [<options>] [<action> [<arguments>...]]
823
+
824
+ Options:
825
+ -h, --help : print help message (of action if action specified)
826
+
827
+ Actions:
828
+ foo:bar:baz:test2 : test action #2
829
+ foo:bar:test1 : test action #1
830
+ ```
831
+
832
+ * `prefix: "foo:bar", action: :test` defines `foo:bar` action (intead of `foo:bar:test`) with `test()` method.
833
+
834
+ File: ex12.rb
835
+
836
+ ```ruby
837
+ #!/usr/bin/env ruby
838
+ require 'benry/cmdapp'
839
+
840
+ class SampleAction < Benry::CmdApp::Action
841
+ prefix "foo:bar", action: :test3_ # !!!!
842
+ ## or:
843
+ #prefix "foo:bar", action: "test3" # !!!!
844
+
845
+ @action.("test action #1")
846
+ def test1() # action name: 'foo:bar:test1'
847
+ puts __method__
848
+ end
849
+
850
+ @action.("test action #3")
851
+ def test3_() # action name: 'foo:bar'
852
+ puts __method__
853
+ end
854
+
855
+ end
856
+
857
+ config = Benry::CmdApp::Config.new("sample app")
858
+ app = Benry::CmdApp::Application.new(config)
859
+ exit app.main()
860
+ ```
861
+
862
+ Output:
863
+
864
+ ```console
865
+ [bash]$ ruby ex12.rb foo:bar:test1
866
+ test1
867
+
868
+ [bash]$ ruby ex12.rb foo:bar:test3
869
+ [ERROR] foo:bar:test2: unknown action.
870
+
871
+ [bash]$ ruby ex12.rb foo:bar
872
+ test3_
873
+ ```
874
+
875
+ Help message:
876
+
877
+ ```console
878
+ [bash]$ ruby ex12.rb -h
879
+ ex12.rb -- sample app
880
+
881
+ Usage:
882
+ $ ex12.rb [<options>] [<action> [<arguments>...]]
883
+
884
+ Options:
885
+ -h, --help : print help message (of action if action specified)
886
+
887
+ Actions:
888
+ foo:bar : test action #3
889
+ foo:bar:test1 : test action #1
890
+ ```
891
+
892
+
893
+ ### Invoke Other Action
894
+
895
+ * `run_action!()` invokes other action.
896
+ * `run_action_once()` invokes other action only once.
897
+ This is equivarent to 'prerequisite task' feature in task runner application.
898
+
899
+ File: ex13.rb
900
+
901
+ ```ruby
902
+ #!/usr/bin/env ruby
903
+ require 'benry/cmdapp'
904
+
905
+ class SampleAction < Benry::CmdApp::Action
906
+
907
+ @action.("create build dir")
908
+ def prepare()
909
+ puts "rm -rf build"
910
+ puts "mkdir build"
911
+ end
912
+
913
+ @action.("build something")
914
+ def build()
915
+ run_action_once("prepare") # !!!!
916
+ run_action_once("prepare") # skipped because already invoked
917
+ puts "echo 'README' > build/README.txt"
918
+ puts "zip -r build.zip build"
919
+ end
920
+
921
+ end
922
+
923
+ config = Benry::CmdApp::Config.new("sample app")
924
+ app = Benry::CmdApp::Application.new(config)
925
+ exit app.main()
926
+ ```
927
+
928
+ Output:
929
+
930
+ ```console
931
+ [bash]$ ruby ex13.rb build
932
+ rm -rf build # invoked only once!!!!
933
+ mkdir build # invoked only once!!!!
934
+ echo 'README' > build/README.txt
935
+ zip -r build.zip build
936
+ ```
937
+
938
+ * When looped action is detected, Benry::CmdApp aborts action.
939
+
940
+ File: ex14.rb
941
+
942
+ ```ruby
943
+ #!/usr/bin/env ruby
944
+ require 'benry/cmdapp'
945
+
946
+ class LoopedAction < Benry::CmdApp::Action
947
+
948
+ @action.("test #1")
949
+ def test1()
950
+ run_action_once("test2")
951
+ end
952
+
953
+ @action.("test #2")
954
+ def test2()
955
+ run_action_once("test3")
956
+ end
957
+
958
+ @action.("test #3")
959
+ def test3()
960
+ run_action_once("test1") # !!!!
961
+ end
962
+
963
+ end
964
+
965
+ config = Benry::CmdApp::Config.new("sample app")
966
+ app = Benry::CmdApp::Application.new(config)
967
+ exit app.main()
968
+ ```
969
+
970
+ Output:
971
+
972
+ ```console
973
+ [bash]$ ruby ex14.rb test1
974
+ [ERROR] test1: looped action detected.
975
+
976
+ [bash]$ ruby ex14.rb test3
977
+ [ERROR] test3: looped action detected.
978
+ ```
979
+
980
+
981
+ ### Action Alias
982
+
983
+ * Alias of action provides alternative short name of action.
984
+
985
+ File: ex15.rb
986
+
987
+ ```ruby
988
+ #!/usr/bin/env ruby
989
+ require 'benry/cmdapp'
990
+
991
+ class SampleAction < Benry::CmdApp::Action
992
+ prefix "foo:bar"
993
+
994
+ @action.("test action #1")
995
+ def test1() # action name: 'foo:bar:test1'
996
+ puts __method__
997
+ end
998
+
999
+ end
1000
+
1001
+ Benry::CmdApp.action_alias "test", "foo:bar:test1" # !!!!
1002
+
1003
+ config = Benry::CmdApp::Config.new("sample app")
1004
+ app = Benry::CmdApp::Application.new(config)
1005
+ exit app.main()
1006
+ ```
1007
+
1008
+ Output:
1009
+
1010
+ ```console
1011
+ [bash]$ ruby ex15.rb test # alias name
1012
+ test1
1013
+
1014
+ [bash]$ ruby ex15.rb foo:bar:test1 # original action name
1015
+ test1
1016
+ ```
1017
+
1018
+ Help message:
1019
+
1020
+ ```console
1021
+ [bash]$ ruby ex15.rb -h
1022
+ ex15.rb -- sample app
1023
+
1024
+ Usage:
1025
+ $ ex15.rb [<options>] [<action> [<arguments>...]]
1026
+
1027
+ Options:
1028
+ -h, --help : print help message (of action if action specified)
1029
+
1030
+ Actions:
1031
+ foo:bar:test1 : test action #1
1032
+ test : alias to 'foo:bar:test1' action
1033
+ ```
1034
+
1035
+ * Alias can include positional and keyword arguments in definition.
1036
+
1037
+ File: ex16.rb
1038
+
1039
+ ```ruby
1040
+ #!/usr/bin/env ruby
1041
+ require 'benry/cmdapp'
1042
+
1043
+ class MyAction < Benry::CmdApp::Action
1044
+
1045
+ @action.("print greeting message")
1046
+ @option.(:lang, "-l, --lang=<lang>", "language", enum: ["en", "fr", "it"])
1047
+ def hello(user="world", lang: "en")
1048
+ case lang
1049
+ when "en" ; puts "Hello, #{user}!"
1050
+ when "fr" ; puts "Bonjour, #{user}!"
1051
+ when "it" ; puts "Ciao, #{user}!"
1052
+ else
1053
+ raise "#{lang}: unknown language."
1054
+ end
1055
+ end
1056
+
1057
+ end
1058
+
1059
+ Benry::CmdApp.action_alias("bonjour", "hello", "--lang=fr") # !!!!
1060
+ Benry::CmdApp.action_alias("ciao" , "hello", "Bob", "-l", "it") # !!!!
1061
+
1062
+ config = Benry::CmdApp::Config.new("sample app")
1063
+ app = Benry::CmdApp::Application.new(config)
1064
+ exit app.main()
1065
+ ```
1066
+
1067
+ Output:
1068
+
1069
+ ```console
1070
+ [bash]$ ruby ex16.rb hello
1071
+ Hello, world!
1072
+
1073
+ [bash]$ ruby ex16.rb bonjour # !!!!
1074
+ Bonjour, world!
1075
+
1076
+ [bash]$ ruby ex16.rb bonjour Alice # !!!!
1077
+ Bonjour, Alice!
1078
+
1079
+ [bash]$ ruby ex16.rb ciao # !!!!
1080
+ Ciao, Bob!
1081
+ ```
1082
+
1083
+
1084
+ ### Default Action
1085
+
1086
+ * `config.default = "test1"` defines default action.
1087
+ In this case, action `test1` will be invoked if action name not specified in command-line.
1088
+ * Default action name is shown in help message.
1089
+
1090
+ File: ex17.rb
1091
+
1092
+ ```ruby
1093
+ #!/usr/bin/env ruby
1094
+ require 'benry/cmdapp'
1095
+
1096
+ class SampleAction < Benry::CmdApp::Action
1097
+
1098
+ @action.("test action #1")
1099
+ def test1()
1100
+ puts __method__
1101
+ end
1102
+
1103
+ end
1104
+
1105
+ config = Benry::CmdApp::Config.new("sample app")
1106
+ config.default_action = "test1" # !!!!
1107
+ app = Benry::CmdApp::Application.new(config)
1108
+ exit app.main()
1109
+ ```
1110
+
1111
+ Output:
1112
+
1113
+ ```console
1114
+ [bash]$ ruby ex17.rb test1
1115
+ test1
1116
+
1117
+ [bash]$ ruby ex17.rb # no action name!!!!
1118
+ test1
1119
+ ```
1120
+
1121
+ Help message:
1122
+
1123
+ ```console
1124
+ [bash]$ ruby ex17.rb -h
1125
+ ex17.rb -- sample app
1126
+
1127
+ Usage:
1128
+ $ ex17.rb [<options>] [<action> [<arguments>...]]
1129
+
1130
+ Options:
1131
+ -h, --help : print help message (of action if action specified)
1132
+
1133
+ Actions: (default: test1) # !!!!
1134
+ test1 : test action #1
1135
+ ```
1136
+
1137
+
1138
+ ### Default Help
1139
+
1140
+ * `config.default_help = true` prints help message if action not specified in command-line.
1141
+ * This is very useful when you don't have proper default action. It's recommended.
1142
+ * `config.default_action` is prior than `config.default_help`.
1143
+
1144
+ File: ex18.rb
1145
+
1146
+ ```ruby
1147
+ #!/usr/bin/env ruby
1148
+ require 'benry/cmdapp'
1149
+
1150
+ class SampleAction < Benry::CmdApp::Action
1151
+
1152
+ @action.("test action #1")
1153
+ def test1()
1154
+ puts __method__
1155
+ end
1156
+
1157
+ end
1158
+
1159
+ config = Benry::CmdApp::Config.new("sample app")
1160
+ config.default_help = true # !!!!
1161
+ app = Benry::CmdApp::Application.new(config)
1162
+ exit app.main()
1163
+ ```
1164
+
1165
+ Output:
1166
+
1167
+ ```console
1168
+ [bash]$ ruby ex18.rb # no action name!!!!
1169
+ ex18.rb -- sample app
1170
+
1171
+ Usage:
1172
+ $ ex18.rb [<options>] [<action> [<arguments>...]]
1173
+
1174
+ Options:
1175
+ -h, --help : print help message (of action if action specified)
1176
+
1177
+ Actions:
1178
+ test1 : test action #1
1179
+ ```
1180
+
1181
+
1182
+ ### Private (Hidden) Action
1183
+
1184
+ * If action method is private, Benry::CmdApp regards that action as private.
1185
+ * Private actions are hidden in help message.
1186
+ * Private actions are shown when `-a` or `--all` option enabled and specified.
1187
+
1188
+ File: ex20.rb
1189
+
1190
+ ```ruby
1191
+ #!/usr/bin/env ruby
1192
+ require 'benry/cmdapp'
1193
+
1194
+ class SampleAction < Benry::CmdApp::Action
1195
+
1196
+ @action.("test action #1")
1197
+ def test1()
1198
+ puts __method__
1199
+ end
1200
+
1201
+ @action.("test action #2")
1202
+ def test2()
1203
+ puts __method__
1204
+ end
1205
+ private :test2 # !!!! private method !!!!
1206
+
1207
+ private # !!!! private method !!!!
1208
+
1209
+ @action.("test action #3")
1210
+ def test3()
1211
+ puts __method__
1212
+ end
1213
+
1214
+ end
1215
+
1216
+ config = Benry::CmdApp::Config.new("sample app")
1217
+ config.option_all = true # !!!! enable '-a, --all' option !!!!
1218
+ app = Benry::CmdApp::Application.new(config)
1219
+ exit app.main()
1220
+ ```
1221
+
1222
+ Help message (without `-a` nor `--all`):
1223
+
1224
+ ```console
1225
+ [bash]$ ruby ex20.rb -h
1226
+ ex20.rb -- sample app
1227
+
1228
+ Usage:
1229
+ $ ex20.rb [<options>] [<action> [<arguments>...]]
1230
+
1231
+ Options:
1232
+ -h, --help : print help message (of action if action specified)
1233
+ -a, --all : list all actions/options including private (hidden) ones
1234
+
1235
+ Actions:
1236
+ test1 : test action #1
1237
+ ```
1238
+
1239
+ Help message (with `-a` or `--all`):
1240
+
1241
+ ```console
1242
+ [bash]$ ruby ex20.rb -h --all # !!!!
1243
+ ex20.rb -- sample app
1244
+
1245
+ Usage:
1246
+ $ ex20.rb [<options>] [<action> [<arguments>...]]
1247
+
1248
+ Options:
1249
+ -h, --help : print help message (of action if action specified)
1250
+ -a, --all : list all actions/options including private (hidden) ones
1251
+
1252
+ Actions:
1253
+ test1 : test action #1
1254
+ test2 : test action #2 # !!!!
1255
+ test3 : test action #3 # !!!!
1256
+ ```
1257
+
1258
+
1259
+ ### Private (Hidden) Option
1260
+
1261
+ * Options which name stars with `_` are treated as private option.
1262
+ * Private options are hidden in help message of action.
1263
+ * Private options are shown when `-a` or `--all` option enabled and specified.
1264
+
1265
+ File: ex21.rb
1266
+
1267
+ ```ruby
1268
+ #!/usr/bin/env ruby
1269
+ require 'benry/cmdapp'
1270
+
1271
+ class SampleAction < Benry::CmdApp::Action
1272
+
1273
+ @action.("test action")
1274
+ @option.(:verbose, "-v", "verbose mode")
1275
+ @option.(:_debug , "-D", "debug mode") # !!!!
1276
+ def test1(verbose: false, _debug: false)
1277
+ puts "verbose=#{verbose}, _debug=#{_debug}"
1278
+ end
1279
+
1280
+ end
1281
+
1282
+ config = Benry::CmdApp::Config.new("sample app")
1283
+ config.option_all = true # !!!! enable '-a, --all' option !!!!
1284
+ app = Benry::CmdApp::Application.new(config)
1285
+ exit app.main()
1286
+ ```
1287
+
1288
+ Help message (without `-a` nor `--all`):
1289
+
1290
+ ```console
1291
+ [bash]$ ruby ex21.rb -h test1
1292
+ ex21.rb test1 -- test action
1293
+
1294
+ Usage:
1295
+ $ ex21.rb test1 [<options>]
1296
+
1297
+ Options:
1298
+ -v : verbose mode
1299
+ ```
1300
+
1301
+ Help message (with `-a` or `--all`)
1302
+
1303
+ ```console
1304
+ [bash]$ ruby ex21.rb -h --all test1 # !!!!
1305
+ ex21.rb test1 -- test action
1306
+
1307
+ Usage:
1308
+ $ ex21.rb test1 [<options>]
1309
+
1310
+ Options:
1311
+ -v : verbose mode
1312
+ -D : debug mode # !!!!
1313
+ ```
1314
+
1315
+
1316
+
1317
+ ## Configuratoin and Customization
1318
+
1319
+
1320
+ ### Application Configuration
1321
+
1322
+ `Benry::CmdApp::Config` class configures application behaviour.
1323
+
1324
+ * `config.app_desc = "..."` sets command description which is shown in help message. (required)
1325
+ * `config.app_version = "1.0.0"` enables `-V` and `--version` option, and prints version number if `-V` or `--version` option specified. (default: `nil`)
1326
+ * `config.app_command = "<command>"` sets command name which is shown in help message. (default: `File.basname($0)`)
1327
+ * `config.app_detail = "<text>"` sets detailed description of command which is showin in help message. (default: `nil`)
1328
+ * `config.default_action = "<action>"` sets default action name. (default: `nil`)
1329
+ * `config.default_help = true` prints help message if no action names specified in command-line. (default: `false`)
1330
+ * `config.option_help = true` enables `-h` and `--help` options. (default: `true`)
1331
+ * `config.option_all = true` enables `-a` and `--all` options which shows private (hidden) actions and options into help message. (default: `false`)
1332
+ * `config.option_verbose = true` enables `-v` and `--verbose` options which sets `$QUIET_MODE = false`. (default: `false`)
1333
+ * `config.option_quiet = true` enables `-q` and `--quiet` options which sets `$QUIET_MODE = true`. (default: `false`)
1334
+ * `config.option_color = true` enables `--color[=<on|off>]` option which sets `$COLOR_MODE = true/false`. This affects to help message colorized or not. (default: `false`)
1335
+ * `config.option_debug = true` enables `-D` and `--debug` options which sets `$DEBUG_MODE = true`. (default: `false`)
1336
+ * `config.option_trace = true` enables `-T` and `--trace` options which sets `$TRACE_MODE = true`. Entering into and exitting from action are reported when trace mode is on. (default: `false`)
1337
+ * `config.help_aliases = true` adds `Aliases:` section in help message. (default: `false`)
1338
+ * `config.help_sections = [["<title>", "<text>"], ...]` adds section title and text into help message. (default: `[]`)
1339
+ * `config.help_postamble = "<text>"` sets postamble text in help message, such as 'Examples:' or 'Tips:'. (default: `nil`)
1340
+ * `config.feat_candidate = true` enables feature to list action names starting with 'foo:' when action name specified in command-line is `foo:`. (default: `true`)
1341
+ * `config.format_help = " %-18s : %s"` sets format of options and actions in help message. (default: `" \e[1m%-18s\e[0m : %s"`)
1342
+ * `config.format_usage = " $ %s %s"` sets format of usage in help message. (default: `" $ \e[1m%s\e[0m %s"`)
1343
+ * `config.format_heading = "[%s]"` sets format of heading in help message. (default: `"\e[34m%s\e[0m"`)
1344
+
1345
+ File: ex22.rb
1346
+
1347
+ ```ruby
1348
+ #!/usr/bin/env ruby
1349
+ require 'benry/cmdapp'
1350
+
1351
+ config = Benry::CmdApp::Config.new("sample app", "1.0.0")
1352
+ #config.default_help = true
1353
+
1354
+ config.class.instance_methods(false).each do |name|
1355
+ next if name =~ /=$/
1356
+ next if ! config.class.method_defined?("#{name}=")
1357
+ val = config.__send__(name)
1358
+ puts "%-25s = %s" % ["config.#{name}", val.inspect]
1359
+ end
1360
+ ```
1361
+
1362
+ Output:
1363
+
1364
+ ```console
1365
+ [bash]$ ruby ex22.rb
1366
+ config.app_desc = "sample app"
1367
+ config.app_version = "1.0.0"
1368
+ config.app_name = "ex22.rb"
1369
+ config.app_command = "ex22.rb"
1370
+ config.app_detail = nil
1371
+ config.default_action = nil
1372
+ config.default_help = false
1373
+ config.option_help = true
1374
+ config.option_all = false
1375
+ config.option_verbose = false
1376
+ config.option_quiet = false
1377
+ config.option_color = false
1378
+ config.option_debug = false
1379
+ config.option_trace = false
1380
+ config.help_aliases = false
1381
+ config.help_sections = []
1382
+ config.help_postamble = nil
1383
+ config.feat_candidate = true
1384
+ config.format_help = " \e[1m%-18s\e[0m : %s"
1385
+ config.format_usage = " $ \e[1m%s\e[0m %s"
1386
+ config.format_heading = "\e[34m%s\e[0m"
1387
+ config.format_appname = "\e[1m%s\e[0m"
1388
+ ```
1389
+
1390
+
1391
+ ### Customization of Global Options
1392
+
1393
+ To add custom global options:
1394
+
1395
+ * (1) Create global option schema object.
1396
+ * (2) Add custom options to it.
1397
+ * (3) Pass it to `Application.new()`.
1398
+
1399
+ File: ex23.rb
1400
+
1401
+ ```ruby
1402
+ #!/usr/bin/env ruby
1403
+ require 'benry/cmdapp'
1404
+
1405
+ class SampleAction < Benry::CmdApp::Action
1406
+
1407
+ @action.("test action")
1408
+ def test1()
1409
+ puts __method__
1410
+ end
1411
+
1412
+ end
1413
+
1414
+ ## (1) create global option shema
1415
+ config = Benry::CmdApp::Config.new("sample app")
1416
+ schema = Benry::CmdApp::AppOptionSchema.new(config) # !!!!
1417
+
1418
+ ## (2) add custom options to it
1419
+ schema.add(:logging, "--logging", "enable logging") # !!!!
1420
+
1421
+ ## (3) pass it to ``Application.new()``
1422
+ app = Benry::CmdApp::Application.new(config, schema) # !!!!
1423
+
1424
+ exit app.main()
1425
+ ```
1426
+
1427
+ Help message:
1428
+
1429
+ ```console
1430
+ [bash]$ ruby ex23.rb -h
1431
+ ex23.rb -- sample app
1432
+
1433
+ Usage:
1434
+ $ ex23.rb [<options>] [<action> [<arguments>...]]
1435
+
1436
+ Options:
1437
+ -h, --help : print help message (of action if action specified)
1438
+ --logging : enable logging # !!!!
1439
+
1440
+ Actions:
1441
+ test1 : test action
1442
+ ```
1443
+
1444
+ To customize global options entirely:
1445
+
1446
+ * (1) Create empty `AppOptionSchema` object.
1447
+ * (2) Add global options as you want.
1448
+ * (3) Create and execute Application object with it.
1449
+
1450
+ File: ex24.rb
1451
+
1452
+ ```ruby
1453
+ #!/usr/bin/env ruby
1454
+ require 'benry/cmdapp'
1455
+
1456
+ ## (1) Create empty ``AppOptionSchema`` object.
1457
+ schema = Benry::CmdApp::AppOptionSchema.new(nil) # !!!!
1458
+
1459
+ ## (2) Add global options as you want.
1460
+ schema.add(:help , "-h, --help" , "print help message")
1461
+ schema.add(:version, "-V, --version", "print version")
1462
+ schema.add(:all , "-a, --all" , "list all actions/options")
1463
+ schema.add(:verbose, "-v, --verbose", "verbose mode")
1464
+ schema.add(:quiet , "-q, --quiet" , "quiet mode")
1465
+ schema.add(:color , "--color[=<on|off>]", "enable/disable color", type: TrueClass)
1466
+ schema.add(:debug , "-D, --debug" , "set $DEBUG_MODE to true")
1467
+ schema.add(:trace , "-T, --trace" , "report enter into and exit from action")
1468
+
1469
+ ## (3) Create and execute Application object with it.
1470
+ config = Benry::CmdApp::Config.new("sample app")
1471
+ app = Benry::CmdApp::Application.new(config, schema) # !!!!
1472
+ exit app.main()
1473
+ ```
1474
+
1475
+
1476
+ ### Customization of Global Option Behaviour
1477
+
1478
+ * (1) Define subclass of `Application` class.
1479
+ * (2) Override `#do_toggle_global_switches()` method.
1480
+ * (3) Create and execute subclass object of `Application`.
1481
+
1482
+ File: ex25.rb
1483
+
1484
+ ```ruby
1485
+ #!/usr/bin/env ruby
1486
+ require 'benry/cmdapp'
1487
+
1488
+ ## (1) Define subclass of ``Application`` class.
1489
+ class MyApplication < Benry::CmdApp::Application
1490
+
1491
+ ## (2) Override ``#do_toggle_global_switches()`` method.
1492
+ def do_toggle_global_switches(_args, global_opts)
1493
+ super
1494
+ ## here is original behaviour
1495
+ #global_opts.each do |key, val|
1496
+ # case key
1497
+ # when :verbose ; $QUIET_MODE = ! val
1498
+ # when :quiet ; $QUIET_MODE = val
1499
+ # when :color ; $COLOR_MODE = val
1500
+ # when :debug ; $DEBUG_MODE = val
1501
+ # when :trace ; $TRACE_MODE = val
1502
+ # else ; # do nothing
1503
+ # end
1504
+ #end
1505
+ end
1506
+
1507
+ end
1508
+
1509
+ ## (3) Create and execute subclass object of ``Application``.
1510
+ config = Benry::CmdApp::Config.new("sample app")
1511
+ app = MyApplication.new(config) # !!!!
1512
+ exit app.main()
1513
+ ```
1514
+
1515
+ Of course, prepending custom module to Application class is also effective way.
1516
+
1517
+ File: ex26.rb
1518
+
1519
+ ```ruby
1520
+ #!/usr/bin/env ruby
1521
+ require 'benry/cmdapp'
1522
+
1523
+ module MyApplicationMod
1524
+
1525
+ def do_toggle_global_switches(_args, global_opts)
1526
+ # ....
1527
+ end
1528
+
1529
+ end
1530
+
1531
+ Benry::CmdApp::Application.prepend(MyApplicationMod) # !!!!
1532
+
1533
+ config = Benry::CmdApp::Config.new("sample app")
1534
+ app = Benry::CmdApp::Application.new(config)
1535
+ exit app.main()
1536
+ ```
1537
+
1538
+
1539
+ ### Custom Hook of Application
1540
+
1541
+ * (1) Define subclass of Application class.
1542
+ * (2) Override callback method.
1543
+ * (3) Create and execute custom application object.
1544
+
1545
+ File: ex27.rb
1546
+
1547
+ ```ruby
1548
+ #!/usr/bin/env ruby
1549
+ require 'benry/cmdapp'
1550
+
1551
+ class SampleAction < Benry::CmdApp::Action
1552
+
1553
+ @action.("test action")
1554
+ def test1()
1555
+ $logger.info("logging message") if $logger
1556
+ end
1557
+
1558
+ end
1559
+
1560
+ ## (1) Define subclass of Application class
1561
+ class MyApplication < Benry::CmdApp::Application # !!!!
1562
+
1563
+ ## (2) Override callback method
1564
+ def do_callback(args, global_opts) # !!!!
1565
+ #p @config
1566
+ #p @schema
1567
+ if global_opts[:logging]
1568
+ require 'logger'
1569
+ $logger = Logger.new(STDOUT)
1570
+ end
1571
+ ## if return :SKIP, action skipped (not invoked).
1572
+ #return :SKIP
1573
+ end
1574
+
1575
+ ## or:
1576
+ #def do_handle_global_options(args, global_opts)
1577
+ # if global_opts[:logging]
1578
+ # require 'logger'
1579
+ # $logger = Logger.new(STDOUT)
1580
+ # end
1581
+ # super
1582
+ #end
1583
+
1584
+ end
1585
+
1586
+ ## (3) create and execute custom application object
1587
+ config = Benry::CmdApp::Config.new("sample app")
1588
+ schema = Benry::CmdApp::AppOptionSchema.new(config)
1589
+ schema.add(:logging, "--logging", "enable logging")
1590
+ app = MyApplication.new(config, schema) # !!!!
1591
+ exit app.main()
1592
+ ```
1593
+
1594
+ * [EXPERIMENTAL] Instead of defining subclass of Application, you can pass callback block to Application object.
1595
+
1596
+ File: ex28.rb
1597
+
1598
+ ```ruby
1599
+ #!/usr/bin/env ruby
1600
+ require 'benry/cmdapp'
1601
+
1602
+ class SampleAction < Benry::CmdApp::Action
1603
+
1604
+ @action.("test action")
1605
+ def test1()
1606
+ $logger.info("logging message") if $logger
1607
+ end
1608
+
1609
+ end
1610
+
1611
+ config = Benry::CmdApp::Config.new("sample app")
1612
+ schema = Benry::CmdApp::AppOptionSchema.new(config)
1613
+ schema.add(:logging, "--logging", "enable logging")
1614
+ app = Benry::CmdApp::Application.new(config, schema) do # !!!!
1615
+ |args, global_opts, config| # !!!!
1616
+ if global_opts[:logging] # !!!!
1617
+ require 'logger' # !!!!
1618
+ $logger = Logger.new(STDOUT) # !!!!
1619
+ end # !!!!
1620
+ #:SKIP # !!!!
1621
+ end # !!!!
1622
+ exit app.main()
1623
+ ```
1624
+
1625
+
1626
+ ### Customization of Command Help Message
1627
+
1628
+ If you want to just add more text into command help message,
1629
+ set `config.app_detail`, `config.help_sections`, and/or `config.help_postamble`.
1630
+
1631
+ File: ex29.rb
1632
+
1633
+ ```ruby
1634
+ #!/usr/bin/env ruby
1635
+ require 'benry/cmdapp'
1636
+
1637
+ class SampleAction < Benry::CmdApp::Action
1638
+
1639
+ @action.("test action #1")
1640
+ def hello(user="world")
1641
+ puts "Hello, #{user}!"
1642
+ end
1643
+
1644
+ end
1645
+
1646
+ config = Benry::CmdApp::Config.new("sample app")
1647
+ config.app_detail = "Document: https://...." # !!!!
1648
+ config.help_sections = [ # !!!!
1649
+ ["Example:", " $ <command> hello Alice"], # !!!!
1650
+ ] # !!!!
1651
+ config.help_postamble = "(Tips: ....)" # !!!!
1652
+ app = Benry::CmdApp::Application.new(config)
1653
+ exit app.main()
1654
+ ```
1655
+
1656
+ Help message:
1657
+
1658
+ ```console
1659
+ [bash]$ ruby ex29.rb -h
1660
+ ex29.rb -- sample app
1661
+
1662
+ Document: https://.... # !!!! app.detail !!!!
1663
+
1664
+ Usage:
1665
+ $ ex29.rb [<options>] [<action> [<arguments>...]]
1666
+
1667
+ Options:
1668
+ -h, --help : print help message (of action if action specified)
1669
+
1670
+ Actions:
1671
+ hello : test action #1
1672
+
1673
+ Example: # !!!! help_sections !!!!
1674
+ $ <command> hello Alice # !!!! help_sections !!!!
1675
+
1676
+ (Tips: ....) # !!!! help_postamble !!!!
1677
+ ```
1678
+
1679
+ If you want to change behaviour of building command help message:
1680
+
1681
+ * (1) Define subclass of `Benry::CmdApp::AppHelpBuilder` class.
1682
+ * (2) Override methods.
1683
+ * (3) Create an instance object of the class.
1684
+ * (4) Pass it to Application object.
1685
+
1686
+ File: ex30.rb
1687
+
1688
+ ```ruby
1689
+ #!/usr/bin/env ruby
1690
+ require 'benry/cmdapp'
1691
+
1692
+ class SampleAction < Benry::CmdApp::Action
1693
+
1694
+ @action.("greeting message")
1695
+ def hello(user="world")
1696
+ puts "Hello, #{user}!"
1697
+ end
1698
+
1699
+ end
1700
+
1701
+ ## (1) Define subclass of ``Benry::CmdApp::AppHelpBuilder`` class.
1702
+ class MyAppHelpBuilder < Benry::CmdApp::AppHelpBuilder
1703
+
1704
+ ## (2) Override methods.
1705
+ def build_help_message(all=false, format=nil)
1706
+ super
1707
+ end
1708
+ def build_preamble(all=false)
1709
+ super
1710
+ end
1711
+ def build_usage(all=false)
1712
+ super
1713
+ end
1714
+ def build_options(all=false, format=nil)
1715
+ super
1716
+ end
1717
+ def build_actions(all=false, format=nil)
1718
+ super
1719
+ end
1720
+ def build_postamble(all=false)
1721
+ super
1722
+ end
1723
+ def heading(str)
1724
+ super
1725
+ end
1726
+ end
1727
+
1728
+ ## (3) Create an instance object of the class.
1729
+ config = Benry::CmdApp::Config.new("sample app")
1730
+ schema = Benry::CmdApp::AppOptionSchema.new(config)
1731
+ schema.add(:logging, "--logging", "enable logging")
1732
+ help_builder = MyAppHelpBuilder.new(config, schema) # !!!!
1733
+
1734
+ ## (4) Pass it to Application object.
1735
+ app = Benry::CmdApp::Application.new(config, schema, help_builder) # !!!!
1736
+ exit app.main()
1737
+ ```
1738
+
1739
+ More simple way:
1740
+
1741
+ * (1) Create a module and override methods of `Benry::CmdApp::AppHelpBuilder` class.
1742
+ * (2) Prepend it to `Benry::CmdApp::AppHelpBuilder` class.
1743
+ * (3) Create and execute Application object.
1744
+
1745
+ File: ex31.rb
1746
+
1747
+ ```ruby
1748
+ #!/usr/bin/env ruby
1749
+ require 'benry/cmdapp'
1750
+
1751
+ class SampleAction < Benry::CmdApp::Action
1752
+
1753
+ @action.("greeting message")
1754
+ def hello(user="world")
1755
+ puts "Hello, #{user}!"
1756
+ end
1757
+
1758
+ end
1759
+
1760
+ ## (1) Create a module and override methods of ``AppHelpBuilder`` class.
1761
+ module MyHelpBuilderMod
1762
+ def build_help_message(all=false, format=nil)
1763
+ super
1764
+ end
1765
+ def build_preamble(all=false)
1766
+ super
1767
+ end
1768
+ def build_usage(all=false)
1769
+ super
1770
+ end
1771
+ def build_options(all=false, format=nil)
1772
+ super
1773
+ end
1774
+ def build_actions(all=false, format=nil)
1775
+ super
1776
+ end
1777
+ def build_postamble(all=false)
1778
+ super
1779
+ end
1780
+ def heading(str)
1781
+ super
1782
+ end
1783
+ end
1784
+
1785
+ ## (2) Prepend it to ``Benry::CmdApp::AppHelpBuilder`` class.
1786
+ Benry::CmdApp::AppHelpBuilder.prepend(MyHelpBuilderMod)
1787
+
1788
+ ## (3) Create and execute Application object.
1789
+ config = Benry::CmdApp::Config.new("sample app")
1790
+ app = Benry::CmdApp::Application.new(config)
1791
+ exit app.main()
1792
+ ```
1793
+
1794
+
1795
+ ### Customization of Action Help Message
1796
+
1797
+ If you want to just add more text into action help message,
1798
+ pass `detail:` and/or `postamble:` keyword arguments to `@action.()`.
1799
+
1800
+ File: ex32.rb
1801
+
1802
+ ```ruby
1803
+ #!/usr/bin/env ruby
1804
+ require 'benry/cmdapp'
1805
+
1806
+ class SampleAction < Benry::CmdApp::Action
1807
+
1808
+ @action.("test action #1",
1809
+ detail: "Document: https://....", # !!!!
1810
+ postamble: "(Tips: ....)") # !!!!
1811
+ def hello(user="world")
1812
+ puts "Hello, #{user}!"
1813
+ end
1814
+
1815
+ end
1816
+
1817
+ config = Benry::CmdApp::Config.new("sample app")
1818
+ app = Benry::CmdApp::Application.new(config)
1819
+ exit app.main()
1820
+ ```
1821
+
1822
+ Help message:
1823
+
1824
+ ```console
1825
+ [bash]$ ruby ex32.rb -h
1826
+ ex32.rb hello -- test action #1
1827
+
1828
+ Document: https://.... # !!!!
1829
+
1830
+ Usage:
1831
+ $ ex32.rb hello [<user>]
1832
+
1833
+ (Tips: ....) # !!!!
1834
+ ```
1835
+
1836
+ If you want to change behaviour of building action help message:
1837
+
1838
+ * (1) Create a module and override methods of `Benry::CmdApp::ActionHelpBuilder` class.
1839
+ * (2) Prepend it to `Benry::CmdApp::ActionHelpBuilder` class.
1840
+ * (3) Create and execute Application object.
1841
+
1842
+ File: ex33.rb
1843
+
1844
+ ```ruby
1845
+ #!/usr/bin/env ruby
1846
+ require 'benry/cmdapp'
1847
+
1848
+ class SampleAction < Benry::CmdApp::Action
1849
+
1850
+ @action.("greeting message")
1851
+ def hello(user="world")
1852
+ puts "Hello, #{user}!"
1853
+ end
1854
+
1855
+ end
1856
+
1857
+ ## (1) Create a module and override methods of ``ActionHelpBuilder`` class.
1858
+ module MyActionHelpBuilderMod
1859
+ def build_help_message(command, all=false)
1860
+ super
1861
+ end
1862
+ def build_preamble(command, all=false)
1863
+ super
1864
+ end
1865
+ def build_usage(command, all=false)
1866
+ super
1867
+ end
1868
+ def build_options(command, all=false)
1869
+ super
1870
+ end
1871
+ def build_postamble(command, all=false)
1872
+ super
1873
+ end
1874
+ def heading(str)
1875
+ super
1876
+ end
1877
+ end
1878
+
1879
+ ## (2) Prepend it to ``Benry::CmdApp::ActionHelpBuilder`` class.
1880
+ Benry::CmdApp::ActionHelpBuilder.prepend(MyActionHelpBuilderMod) # !!!!
1881
+
1882
+ ## (3) Create and execute Application object.
1883
+ config = Benry::CmdApp::Config.new("sample app")
1884
+ app = Benry::CmdApp::Application.new(config)
1885
+ exit app.main()
1886
+ ```
1887
+
1888
+ Another way:
1889
+
1890
+ * (1) Define subclass of `ActionHelpBuilder` class.
1891
+ * (2) Set it to `ACTION_HELP_BUILDER_CLASS` constant value.
1892
+ * (3) Create and execute Application object.
1893
+
1894
+ File: ex34.rb
1895
+
1896
+ ```ruby
1897
+ #!/usr/bin/env ruby
1898
+ require 'benry/cmdapp'
1899
+
1900
+ class SampleAction < Benry::CmdApp::Action
1901
+
1902
+ @action.("greeting message")
1903
+ def hello(user="world")
1904
+ puts "Hello, #{user}!"
1905
+ end
1906
+
1907
+ end
1908
+
1909
+ ## (1) Define subclass of ``ActionHelpBuilder`` class.
1910
+ class MyActionHelpBuilder < Benry::CmdApp::ActionHelpBuilder
1911
+ def build_help_message(command, all=false)
1912
+ super
1913
+ end
1914
+ def build_preamble(command, all=false)
1915
+ super
1916
+ end
1917
+ def build_usage(command, all=false)
1918
+ super
1919
+ end
1920
+ def build_options(command, all=false)
1921
+ super
1922
+ end
1923
+ def build_postamble(command, all=false)
1924
+ super
1925
+ end
1926
+ def heading(str)
1927
+ super
1928
+ end
1929
+ end
1930
+
1931
+ ## (2) Set it to ``ACTION_HELP_BUILDER_CLASS`` constant value.
1932
+ Benry::CmdApp.module_eval do
1933
+ remove_const :ACTION_HELP_BUILDER_CLASS
1934
+ const_set :ACTION_HELP_BUILDER_CLASS, MyActionHelpBuilder
1935
+ end
1936
+
1937
+ ## (3) Create and execute Application object.
1938
+ config = Benry::CmdApp::Config.new("sample app")
1939
+ app = Benry::CmdApp::Application.new(config)
1940
+ exit app.main()
1941
+ ```
1942
+
1943
+
1944
+
1945
+ ## Q & A
1946
+
1947
+
1948
+ ### Q: How to Append Some Tasks to Existing Action?
1949
+
1950
+ A: (a) Use method alias, or (b) use prepend.
1951
+
1952
+ File: ex41.rb
1953
+
1954
+ ```ruby
1955
+ require 'benry/cmdapp'
1956
+
1957
+ class SampleAction < Benry::CmdApp::Action
1958
+
1959
+ @action.("test action #1")
1960
+ def hello(user="world")
1961
+ puts "Hello, #{user}!"
1962
+ end
1963
+
1964
+ @action.("test action #2")
1965
+ def hi(user="world")
1966
+ puts "Hi, #{user}!"
1967
+ end
1968
+
1969
+ end
1970
+
1971
+ ## (a) use method alias
1972
+ class SampleAction # open existing class
1973
+ alias __old_hello hello # alias of existing method
1974
+ def hello(user="world") # override existing method
1975
+ puts "---- >8 ---- >8 ----"
1976
+ __old_hello(user) # call original method
1977
+ puts "---- 8< ---- 8< ----"
1978
+ end
1979
+ end
1980
+
1981
+ ## (b) use prepend
1982
+ module SampleMod # define new module
1983
+ def hi(user="world") # override existing method
1984
+ puts "~~~~ >8 ~~~~ >8 ~~~~"
1985
+ super # call original method
1986
+ puts "~~~~ 8< ~~~~ 8< ~~~~"
1987
+ end
1988
+ end
1989
+ SampleAction.prepend(SampleMod) # prepend it to existing class
1990
+
1991
+ config = Benry::CmdApp::Config.new("sample app")
1992
+ app = Benry::CmdApp::Application.new(config)
1993
+ exit app.main()
1994
+ ```
1995
+
1996
+ Output:
1997
+
1998
+ ```console
1999
+ [bash]$ ruby ex41.rb hello
2000
+ ---- >8 ---- >8 ----
2001
+ Hello, world!
2002
+ ---- 8< ---- 8< ----
2003
+
2004
+ [bash]$ ruby ex41.rb hi Alice
2005
+ ~~~~ >8 ~~~~ >8 ~~~~
2006
+ Hi, Alice!
2007
+ ~~~~ 8< ~~~~ 8< ~~~~
2008
+ ```
2009
+
2010
+
2011
+ ### Q: How to Re-define Existing Action?
2012
+
2013
+ A: Remove existing action at first, and re-define action.
2014
+
2015
+ File: ex42.rb
2016
+
2017
+ ```ruby
2018
+ require 'benry/cmdapp'
2019
+
2020
+ class SampleAction < Benry::CmdApp::Action
2021
+
2022
+ @action.("sample action")
2023
+ def hello() # !!!!
2024
+ puts "Hello, world!"
2025
+ end
2026
+
2027
+ end
2028
+
2029
+ Benry::CmdApp.delete_action("hello") # !!!!
2030
+
2031
+ class OtherAction < Benry::CmdApp::Action
2032
+
2033
+ @action.("other action") # !!!!
2034
+ def hello() # !!!!
2035
+ puts "Ciao, world!"
2036
+ end
2037
+
2038
+ end
2039
+
2040
+ config = Benry::CmdApp::Config.new("sample app")
2041
+ app = Benry::CmdApp::Application.new(config)
2042
+ exit app.main()
2043
+ ```
2044
+
2045
+ Help message:
2046
+
2047
+ ```console
2048
+ [bash]$ ruby ex42.rb -h
2049
+ ex42.rb -- sample app
2050
+
2051
+ Usage:
2052
+ $ ex42.rb [<options>] [<action> [<arguments>...]]
2053
+
2054
+ Options:
2055
+ -h, --help : print help message (of action if action specified)
2056
+
2057
+ Actions:
2058
+ hello : other action # !!!!
2059
+ ```
2060
+
2061
+
2062
+ ### Q: How to Delete Existing Action/Alias?
2063
+
2064
+ A: Call `Benry::CmdApp.delete_action("<action>")` or `Benry::CmdApp.delete_alias("<alias>")`.
2065
+
2066
+
2067
+ ### Q: How to Show Entering Into or Exitting From Action?
2068
+
2069
+ A: Set `config.option_trace = true` and pass `-T` (or `--trace`) option.
2070
+
2071
+ File: ex43.rb
2072
+
2073
+ ```ruby
2074
+ require 'benry/cmdapp'
2075
+
2076
+ class SampleAction < Benry::CmdApp::Action
2077
+
2078
+ @action.("preparation")
2079
+ def prepare()
2080
+ puts "... prepare something ..."
2081
+ end
2082
+
2083
+ @action.("build")
2084
+ def build()
2085
+ run_action_once("prepare")
2086
+ puts "... build something ..."
2087
+ end
2088
+
2089
+ end
2090
+
2091
+ config = Benry::CmdApp::Config.new("sample app")
2092
+ config.option_trace = true # !!!!
2093
+ app = Benry::CmdApp::Application.new(config)
2094
+ exit app.main()
2095
+ ```
2096
+
2097
+ Output:
2098
+
2099
+ ```console
2100
+ [bash]$ ruby ex43.rb -T build # !!!!
2101
+ ## enter: build
2102
+ ## enter: prepare
2103
+ ... prepare something ...
2104
+ ## exit: prepare
2105
+ ... build something ...
2106
+ ## exit: build
2107
+ ```
2108
+
2109
+
2110
+ ### Q: How to Enable/Disable Color Mode?
2111
+
2112
+ A: Set `config.option_color = true` and pass `--color=on` or `--color=off` option.
2113
+
2114
+ File: ex44.rb
2115
+
2116
+ ```ruby
2117
+ require 'benry/cmdapp'
2118
+
2119
+ class SampleAction < Benry::CmdApp::Action
2120
+
2121
+ @action.("greeting message")
2122
+ def hello(user="world")
2123
+ puts "Hello, #{user}!"
2124
+ end
2125
+
2126
+ end
2127
+
2128
+ config = Benry::CmdApp::Config.new("sample app")
2129
+ config.option_color = true # !!!!
2130
+ app = Benry::CmdApp::Application.new(config)
2131
+ exit app.main()
2132
+ ```
2133
+
2134
+ Help message:
2135
+
2136
+ ```console
2137
+ [bash]$ ruby ex44.rb -h
2138
+ ex44.rb -- sample app
2139
+
2140
+ Usage:
2141
+ $ ex44.rb [<options>] [<action> [<arguments>...]]
2142
+
2143
+ Options:
2144
+ -h, --help : print help message (of action if action specified)
2145
+ --color[=<on|off>] : enable/disable color # !!!!
2146
+
2147
+ Actions:
2148
+ hello : greeting message
2149
+
2150
+ [bash]$ ruby ex44.rb -h --color=off # !!!!
2151
+
2152
+ [bash]$ ruby ex44.rb -h --color=on # !!!!
2153
+ ```
2154
+
2155
+
2156
+ ### Q: How to Define Multiple Option, like `-I` Option of Ruby?
2157
+
2158
+ A: Provide block parameter on `@option.()`.
2159
+
2160
+ File: ex45.rb
2161
+
2162
+ ```ruby
2163
+ require 'benry/cmdapp'
2164
+
2165
+ class TestAction < Benry::CmdApp::Action
2166
+
2167
+ @action.("multiple option test")
2168
+ @option.(:path, "-I <path>", "path") {|options, key, val| # !!!!
2169
+ arr = options[key] || [] # !!!!
2170
+ arr << val # !!!!
2171
+ arr # !!!!
2172
+ ## or: # !!!!
2173
+ #(options[key] || []) << val # !!!!
2174
+ } # !!!!
2175
+ def test(path: [])
2176
+ puts "path=#{path.inspect}" #=> path=["/tmp", "/var/tmp"]
2177
+ end
2178
+
2179
+ end
2180
+
2181
+ config = Benry::CmdApp::Config.new("test app")
2182
+ app = Benry::CmdApp::Application.new(config)
2183
+ exit app.main()
2184
+ ```
2185
+
2186
+ Output:
2187
+
2188
+ ```console
2189
+ [bash]$ ruby ex45.rb test -I /tmp -I /var/tmp # !!!!
2190
+ path=["/tmp", "/var/tmp"] # !!!!
2191
+ ```
2192
+
2193
+
2194
+ ### Q: How to Specify Detailed Description of Option?
2195
+
2196
+ A: Add `detail:` keyword argument to `@option.()`.
2197
+
2198
+ File: ex46.rb
2199
+
2200
+ ```ruby
2201
+ require 'benry/cmdapp'
2202
+
2203
+ class TestAction < Benry::CmdApp::Action
2204
+
2205
+ @action.("detailed description test")
2206
+ @option.(:mode, "-m <mode>", "output mode", detail: <<"END")
2207
+ v, verbose: print many output
2208
+ q, quiet: print litte output
2209
+ c, compact: print summary output
2210
+ END
2211
+ def test(mode: nil)
2212
+ puts "mode=#{mode.inspect}"
2213
+ end
2214
+
2215
+ end
2216
+
2217
+ config = Benry::CmdApp::Config.new("test app")
2218
+ app = Benry::CmdApp::Application.new(config)
2219
+ exit app.main()
2220
+ ```
2221
+
2222
+ Help message:
2223
+
2224
+ ```console
2225
+ [bash]$ ruby ex46.rb -h test
2226
+ ex46.rb test -- detailed description test
2227
+
2228
+ Usage:
2229
+ $ ex46.rb test [<options>]
2230
+
2231
+ Options:
2232
+ -m <mode> : output mode
2233
+ v, verbose: print many output
2234
+ q, quiet: print litte output
2235
+ c, compact: print summary output
2236
+ ```
2237
+
2238
+
2239
+ ### Q: How to Copy All Options from Other Action?
2240
+
2241
+ A: Use `@copy_options.()`.
2242
+
2243
+ File: ex47.rb
2244
+
2245
+ ```ruby
2246
+ require 'benry/cmdapp'
2247
+
2248
+ class SampleAction < Benry::CmdApp::Action
2249
+
2250
+ @action.("test action #1")
2251
+ @option.(:verbose, "-v, --verbose", "verbose mode")
2252
+ @option.(:file, "-f, --file=<file>", "filename")
2253
+ @option.(:indent, "-i, --indent[=<N>]", "indent")
2254
+ def test1(verbose: false, file: nil, indent: nil)
2255
+ puts "verbose=#{verbose}, file=#{file}, indent=#{indent}"
2256
+ end
2257
+
2258
+ @action.("test action #2")
2259
+ @copy_options.("test1") # !!!! copy options from test1 !!!!
2260
+ @option.(:debug, "-D, --debug", "debug mode")
2261
+ def test2(verbose: false, file: nil, indent: nil, debug: false)
2262
+ puts "verbose=#{verbose}, file=#{file}, indent=#{indent}, debug=#{debug}"
2263
+ end
2264
+
2265
+ end
2266
+
2267
+ config = Benry::CmdApp::Config.new("sample app")
2268
+ app = Benry::CmdApp::Application.new(config)
2269
+ exit app.main()
2270
+ ```
2271
+
2272
+ Help message of `test2` action:
2273
+
2274
+ ```console
2275
+ [bash]$ ruby ex47.rb -h test2
2276
+ ex47.rb test2 -- test action #2
2277
+
2278
+ Usage:
2279
+ $ ex47.rb test2 [<options>]
2280
+
2281
+ Options:
2282
+ -v, --verbose : verbose mode # copied!!
2283
+ -f, --file=<file> : filename # copied!!
2284
+ -i, --indent[=<N>] : indent # copied!!
2285
+ -D, --debug : debug mode
2286
+ ```
2287
+
2288
+
2289
+ ### Q: What is the Difference Between `prefix(alias_of:)` and `prefix(action:)`?
2290
+
2291
+ A: The former defines an alias, and the latter doesn't.
2292
+
2293
+ File: ex48.rb
2294
+
2295
+ ```ruby
2296
+ require 'benry/cmdapp'
2297
+
2298
+ class AaaAction < Benry::CmdApp::Action
2299
+ prefix "aaa", alias_of: :print_ # (or) alias_of: "print"
2300
+
2301
+ @action.("test #1")
2302
+ def print_()
2303
+ puts "test"
2304
+ end
2305
+
2306
+ end
2307
+
2308
+ class BbbAction < Benry::CmdApp::Action
2309
+ prefix "bbb", action: :print_ # (or) action: "print"
2310
+
2311
+ @action.("test #2")
2312
+ def print_()
2313
+ puts "test"
2314
+ end
2315
+
2316
+ end
2317
+
2318
+ config = Benry::CmdApp::Config.new("sample app")
2319
+ app = Benry::CmdApp::Application.new(config)
2320
+ exit app.main()
2321
+ ```
2322
+
2323
+ Help message:
2324
+
2325
+ ```console
2326
+ [bash]$ ruby ex48.rb
2327
+ ex48.rb -- sample app
2328
+
2329
+ Usage:
2330
+ $ ex48.rb [<options>] [<action> [<arguments>...]]
2331
+
2332
+ Options:
2333
+ -h, --help : print help message (of action if action specified)
2334
+
2335
+ Actions:
2336
+ aaa : alias of 'aaa:print' action # !!!!
2337
+ aaa:print : test #1
2338
+ bbb : test #2 # !!!!
2339
+ ```
2340
+
2341
+ In the above example, alias `aaa` is defined due to `prefix(alias_of:)`,
2342
+ and action `bbb` is not an alias due to `prefix(action:)`.
2343
+
2344
+
2345
+ ### Q: How to Change Order of Options in Help Message?
2346
+
2347
+ A: Call `AppOptionSchema#sort_options_in_this_order()`.
2348
+
2349
+ File: ex49.rb
2350
+
2351
+ ```ruby
2352
+ require 'benry/cmdapp'
2353
+
2354
+ config = Benry::CmdApp::Config.new("sample app", "1.0.0",
2355
+ option_all: true,
2356
+ option_quiet: true,
2357
+ option_color: true,
2358
+ )
2359
+ schema = Benry::CmdApp::AppOptionSchema.new(config)
2360
+ keys = [:all, :quiet, :color, :help, :version] # !!!!
2361
+ schema.sort_options_in_this_order(*keys) # !!!!
2362
+ app = Benry::CmdApp::Application.new(config, schema)
2363
+ ## or:
2364
+ #app = Benry::CmdApp::Application.new(config)
2365
+ #app.schema.sort_options_in_this_order(*keys) # !!!!
2366
+ exit app.main()
2367
+ ```
2368
+
2369
+ Help message:
2370
+
2371
+ ```console
2372
+ [bash]$ ruby ex49.rb -h
2373
+ ex49.rb (1.0.0) -- sample app
2374
+
2375
+ Usage:
2376
+ $ ex49.rb [<options>] [<action> [<arguments>...]]
2377
+
2378
+ Options:
2379
+ -a, --all : list all actions/options including private (hidden) ones
2380
+ -q, --quiet : quiet mode
2381
+ --color[=<on|off>] : enable/disable color
2382
+ -h, --help : print help message (of action if action specified)
2383
+ -V, --version : print version
2384
+
2385
+ Actions:
2386
+
2387
+ ```
2388
+
2389
+
2390
+ ### Q: Is It Possible to Make Action Names Emphasised or Weaken?
2391
+
2392
+ A: Yes. When you pass `important: true` to `@action.()`, that action will be printed with unerline in help message. When you pass `important: false`, that action will be printed in gray color.
2393
+
2394
+ File: ex50.rb
2395
+
2396
+ ```ruby
2397
+ require 'benry/cmdapp'
2398
+
2399
+ class SampleAction < Benry::CmdApp::Action
2400
+
2401
+ @action.("empasized", important: true) # !!!!
2402
+ def test1()
2403
+ end
2404
+
2405
+ @action.("weaken", important: false) # !!!!
2406
+ def test2()
2407
+ end
2408
+
2409
+ end
2410
+
2411
+ config = Benry::CmdApp::Config.new("sample app")
2412
+ app = Benry::CmdApp::Application.new(config)
2413
+ exit app.main()
2414
+ ```
2415
+
2416
+ Help message:
2417
+
2418
+ ```console
2419
+ [bash]$ ruby ex50.rb -h
2420
+ ex50.rb -- sample app
2421
+
2422
+ Usage:
2423
+ $ ex50.rb [<options>] [<action> [<arguments>...]]
2424
+
2425
+ Options:
2426
+ -h, --help : print help message (of action if action specified)
2427
+
2428
+ Actions:
2429
+ test1 : empasized # !!!! printed with underline !!!!
2430
+ test2 : weaken # !!!! printed in gray color !!!!
2431
+ ```
2432
+
2433
+
2434
+ ### Q: Is It Possible to Add Metadata to Action or Option?
2435
+
2436
+ A: Yes. Pass `tag:` keyword argument to `@action.()` or `@option.()`.
2437
+
2438
+ * `tag:` keyword argument accept any type of value such as symbol, string, array, and so on.
2439
+ * Currenty, Benry::CmdApp doesn't provide the good way to use it effectively.
2440
+ This feature may be used by command-line application or framework based on Benry::CmdApp.
2441
+
2442
+ File: ex51.rb
2443
+
2444
+ ```ruby
2445
+ require 'benry/cmdapp'
2446
+
2447
+ class SampleAction < Benry::CmdApp::Action
2448
+
2449
+ @action.("print greeting message", tag: :important) # !!!!
2450
+ @option.(:repeat, "-r <N>", "repeat N times", tag: :important) # !!!!
2451
+ def hello(user="world", repeat: nil)
2452
+ (repeat || 1).times do
2453
+ puts "Hello, #{user}!"
2454
+ end
2455
+ end
2456
+
2457
+ end
2458
+
2459
+ config = Benry::CmdApp::Config.new("sample app")
2460
+ app = Benry::CmdApp::Application.new(config)
2461
+ exit app.main()
2462
+ ```
2463
+
2464
+
2465
+ ### Q: How to Make Error Messages I18Ned?
2466
+
2467
+ A: Currently not supported. May be supported in the future release.
2468
+
2469
+
2470
+
2471
+
2472
+ ## License and Copyright
2473
+
2474
+ * $License: MIT License $
2475
+ * $Copyright: copyright(c) 2023 kwatch@gmail.com $