benry-cmdapp 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.
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 $