cliapp 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGES.md +23 -0
- data/README.md +11 -14
- data/cliapp.gemspec +14 -10
- data/lib/cliapp.rb +68 -31
- data/test/action_test.rb +1 -1
- data/test/all.rb +23 -0
- data/test/application_test.rb +134 -40
- data/test/init.rb +7 -1
- data/test/module_test.rb +6 -5
- data/test/util_test.rb +1 -1
- metadata +9 -13
- data/Rakefile.rb +0 -9
- data/task/common-task.rb +0 -67
- data/task/release-task.rb +0 -77
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a908a368f333d3ad499d7439721fc9c08e9bf0eb7cbb2191acd415de76994b1b
|
4
|
+
data.tar.gz: '08a8841bd44f3a29124c7670079970e11ed23ca5cde7c947f9382178340d7980'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0192d3a964246c898d4fe7f44530fcbe647308ed32b83119e3335be593bd32c74e6dc81da761f4cd5a1d7412957350af5128a4bd189a61c071c4556ab3227009'
|
7
|
+
data.tar.gz: 8b529d1ba95f73939a68b7527a9d5c1ea1aca614d9dea673f24ec37c29741b2fe0271ab264f9529da9aa7e1174838639cb6525de6f55648b23738d5ff57e372d
|
data/CHANGES.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
CHANGES
|
2
|
+
=======
|
3
|
+
|
4
|
+
|
5
|
+
Release 0.2.0 (2024-10-11)
|
6
|
+
--------------------------
|
7
|
+
|
8
|
+
* [change] Help message colorized.
|
9
|
+
* [enhance] Define `CLIApp#main()`.
|
10
|
+
* [change] Skeleton code changed to use `CLIApp#main()`.
|
11
|
+
* [enhance] (internal) Define `Util::Color` class.
|
12
|
+
|
13
|
+
|
14
|
+
Release 0.1.1 (2024-10-05)
|
15
|
+
--------------------------
|
16
|
+
|
17
|
+
* [change] Skeleton (example) code simplified.
|
18
|
+
|
19
|
+
|
20
|
+
Release 0.1.0 (2024-10-05)
|
21
|
+
--------------------------
|
22
|
+
|
23
|
+
* First public release
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
CLIApp
|
2
2
|
======
|
3
3
|
|
4
|
-
($Version: 0.
|
4
|
+
($Version: 0.2.0 $)
|
5
5
|
|
6
6
|
CLIApp is a small framework for command-line application.
|
7
7
|
If you need to create a CLI app such as Git or Docker, CLIApp is one of the solutions.
|
@@ -45,7 +45,6 @@ app.global_options({
|
|
45
45
|
:version => [ "--version" , "print version number"],
|
46
46
|
:list => ["-l", "--list" , "list action names"],
|
47
47
|
})
|
48
|
-
APP = app
|
49
48
|
|
50
49
|
## 'hello' action
|
51
50
|
app.action("hello", "greeting message", {
|
@@ -71,18 +70,16 @@ GARBAGE_FILES = []
|
|
71
70
|
PRODUCT_FILES = []
|
72
71
|
|
73
72
|
## main
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
exit status_code
|
85
|
-
end
|
73
|
+
status_code = app.main(ARGV)
|
74
|
+
exit status_code
|
75
|
+
## or
|
76
|
+
#begin
|
77
|
+
# app.run(*ARGV)
|
78
|
+
# exit 0
|
79
|
+
#rescue OptionParser::ParseError, CLIApp::ActionError => exc
|
80
|
+
# $stderr.puts "[ERROR] #{exc.message}"
|
81
|
+
# exit 1
|
82
|
+
#end
|
86
83
|
```
|
87
84
|
|
88
85
|
Output example:
|
data/cliapp.gemspec
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
Gem::Specification.new do |spec|
|
4
5
|
spec.name = 'cliapp'
|
5
|
-
spec.version = '$Version: 0.
|
6
|
+
spec.version = '$Version: 0.2.0 $'.split()[1]
|
6
7
|
spec.author = 'kwatch'
|
7
8
|
spec.email = 'kwatch@gmail.com'
|
8
9
|
spec.platform = Gem::Platform::RUBY
|
@@ -12,18 +13,21 @@ Gem::Specification.new do |spec|
|
|
12
13
|
A small framework for CLI Applications such as Git, Docker, NPM, etc.
|
13
14
|
END
|
14
15
|
spec.license = 'MIT'
|
15
|
-
spec.files = Dir[
|
16
|
-
README.md MIT-LICENSE
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
spec.files = Dir[
|
17
|
+
'README.md', 'MIT-LICENSE', 'CHANGES.md',
|
18
|
+
"#{spec.name}.gemspec",
|
19
|
+
#'Rakefile.rb',
|
20
|
+
#'bin/*',
|
21
|
+
'lib/**/*.rb',
|
22
|
+
'test/**/*.rb',
|
23
|
+
#'task/**/*.rb',
|
24
|
+
]
|
21
25
|
spec.executables = []
|
22
26
|
spec.bindir = 'bin'
|
23
27
|
spec.require_path = 'lib'
|
24
|
-
spec.test_files = Dir['test/**/*_test.rb']
|
28
|
+
spec.test_files = ['test/all.rb'] # or: Dir['test/**/*_test.rb']
|
25
29
|
#spec.extra_rdoc_files = ['README.md', 'CHANGES.md']
|
26
30
|
|
27
|
-
spec.required_ruby_version =
|
31
|
+
spec.required_ruby_version = '>= 2.4'
|
28
32
|
spec.add_development_dependency 'oktest', '~> 1.4'
|
29
33
|
end
|
data/lib/cliapp.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
##
|
5
5
|
## Command-Line Application Framework
|
6
6
|
##
|
7
|
-
## $Version: 0.
|
8
|
-
## $Copyright: copyright
|
7
|
+
## $Version: 0.2.0 $
|
8
|
+
## $Copyright: copyright(c) 2024 kwatch@gmail.com $
|
9
9
|
## $License: MIT License $
|
10
10
|
##
|
11
11
|
|
@@ -65,7 +65,7 @@ module CLIApp
|
|
65
65
|
@help_option_width = help_option_width || 22
|
66
66
|
@help_action_width = help_action_width || 22
|
67
67
|
@actionlist_width = actionlist_width || 16
|
68
|
-
@actionlist_format = actionlist_format || nil # ex: "
|
68
|
+
@actionlist_format = actionlist_format || nil # ex: "%-16s : %s"
|
69
69
|
end
|
70
70
|
|
71
71
|
attr_accessor :name, :desc, :command, :version
|
@@ -128,6 +128,24 @@ module CLIApp
|
|
128
128
|
nil
|
129
129
|
end
|
130
130
|
|
131
|
+
def main(argv=ARGV, &error_handler)
|
132
|
+
#; [!hopc3] returns 0 if finished successfully.
|
133
|
+
run(*argv)
|
134
|
+
return 0
|
135
|
+
rescue OptionParser::ParseError, CLIApp::ActionError => exc
|
136
|
+
#; [!uwcq7] yields block with error object if error raised.
|
137
|
+
if block_given?()
|
138
|
+
yield exc
|
139
|
+
#; [!e0t6k] reports error into stderr if block not given.
|
140
|
+
else
|
141
|
+
s = "[ERROR]"
|
142
|
+
s = Util::Color.error(s) if $stderr.tty?
|
143
|
+
$stderr.puts "#{s} #{exc.message}"
|
144
|
+
end
|
145
|
+
#; [!d0g0w] returns 1 if error raised.
|
146
|
+
return 1
|
147
|
+
end
|
148
|
+
|
131
149
|
def run(*args)
|
132
150
|
#; [!qv5fz] parses global options (not parses action options).
|
133
151
|
global_opts = parse_global_options(args)
|
@@ -147,7 +165,8 @@ module CLIApp
|
|
147
165
|
action_opts = parse_action_options(action, args)
|
148
166
|
#; [!ehshp] prints action help if action option contains help option.
|
149
167
|
if action_opts[:help]
|
150
|
-
|
168
|
+
#; [!6vet4] decolorizes action help message when stdout is not a tty.
|
169
|
+
print decolorize(action_help_message(action))
|
151
170
|
return
|
152
171
|
end
|
153
172
|
#; [!0nwwe] invokes an action with action args and options.
|
@@ -157,7 +176,8 @@ module CLIApp
|
|
157
176
|
def handle_global_options(global_opts)
|
158
177
|
#; [!6n0w0] when '-h' or '--help' specified, prints help message and returns true.
|
159
178
|
if global_opts[:help]
|
160
|
-
|
179
|
+
#; [!fadq1] decolorizes app help message when stdout is not a tty.
|
180
|
+
print decolorize(application_help_message())
|
161
181
|
return true
|
162
182
|
end
|
163
183
|
#; [!zii8c] when '-V' or '--version' specified, prints version number and returns true.
|
@@ -193,22 +213,23 @@ module CLIApp
|
|
193
213
|
}.join()
|
194
214
|
optstr = options_str.empty? ? "" : " [<options>]"
|
195
215
|
actstr = actions_str.empty? ? "" : " <action> [<arguments>...]"
|
196
|
-
|
216
|
+
cl = Util::Color
|
217
|
+
ver = c.version ? " " + cl.weak("(#{c.version})") : nil
|
197
218
|
sb = []
|
198
219
|
sb << <<"END"
|
199
|
-
#{c.name}#{ver} --- #{c.desc}
|
220
|
+
#{cl.strong(c.name)}#{ver} --- #{c.desc}
|
200
221
|
|
201
|
-
Usage:
|
202
|
-
#{indent}$ #{c.command}#{optstr}#{actstr}
|
222
|
+
#{cl.header('Usage:')}
|
223
|
+
#{indent}$ #{cl.strong(c.command)}#{optstr}#{actstr}
|
203
224
|
END
|
204
225
|
sb << (options_str.empty? ? "" : <<"END")
|
205
226
|
|
206
|
-
Options:
|
227
|
+
#{cl.header('Options:')}
|
207
228
|
#{options_str.chomp()}
|
208
229
|
END
|
209
230
|
sb << (actions_str.empty? ? "" : <<"END")
|
210
231
|
|
211
|
-
Actions:
|
232
|
+
#{cl.header('Actions:')}
|
212
233
|
#{actions_str.chomp()}
|
213
234
|
END
|
214
235
|
return sb.join()
|
@@ -221,17 +242,17 @@ END
|
|
221
242
|
options_str = option_help_message(action.option_schema, width: width, indent: indent)
|
222
243
|
optstr = options_str.empty? ? "" : " [<options>]"
|
223
244
|
argstr = Util.argstr_of_proc(action.block)
|
224
|
-
c = @config
|
245
|
+
c = @config; cl = Util::Color
|
225
246
|
sb = []
|
226
247
|
sb << <<"END"
|
227
|
-
#{c.command
|
248
|
+
#{cl.strong(c.command + ' ' + action.name)} --- #{action.desc}
|
228
249
|
|
229
|
-
Usage:
|
250
|
+
#{cl.header('Usage:')}
|
230
251
|
#{c.help_indent}$ #{c.command} #{action.name}#{optstr}#{argstr}
|
231
252
|
END
|
232
253
|
sb << (options_str.empty? ? "" : <<"END")
|
233
254
|
|
234
|
-
Options:
|
255
|
+
#{cl.header('Options:')}
|
235
256
|
#{options_str.chomp()}
|
236
257
|
END
|
237
258
|
return sb.join()
|
@@ -300,11 +321,17 @@ END
|
|
300
321
|
|
301
322
|
def do_when_action_not_specified(global_opts)
|
302
323
|
#; [!w5lq9] prints application help message.
|
303
|
-
|
324
|
+
#; [!audam] decolorizes help message when stdout is not a tty.
|
325
|
+
print decolorize(application_help_message())
|
304
326
|
#; [!txqnr] returns true which means 'done'.
|
305
327
|
return true
|
306
328
|
end
|
307
329
|
|
330
|
+
def decolorize(s)
|
331
|
+
#; [!es6ay] decolorizes str if stdout is not a tty.
|
332
|
+
return $stdout.tty? ? s : Util::Color.decolorize(s)
|
333
|
+
end
|
334
|
+
|
308
335
|
end
|
309
336
|
|
310
337
|
|
@@ -362,6 +389,19 @@ END
|
|
362
389
|
return name
|
363
390
|
end
|
364
391
|
|
392
|
+
|
393
|
+
module Color
|
394
|
+
module_function
|
395
|
+
def strong(s) ; return "\e[1m#{s}\e[0m" ; end
|
396
|
+
def weak(s) ; return "\e[2m#{s}\e[0m" ; end
|
397
|
+
def header(s) ; return "\e[36m#{s}\e[0m" ; end
|
398
|
+
def error(s) ; return "\e[31m#{s}\e[0m" ; end
|
399
|
+
def decolorize(s)
|
400
|
+
return s.gsub(/\e\[.*?m/, '')
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
|
365
405
|
end
|
366
406
|
|
367
407
|
|
@@ -376,7 +416,7 @@ end
|
|
376
416
|
|
377
417
|
__END__
|
378
418
|
#!/usr/bin/env ruby
|
379
|
-
#
|
419
|
+
# encoding: utf-8
|
380
420
|
# frozen_string_literal: true
|
381
421
|
|
382
422
|
require 'cliapp'
|
@@ -390,7 +430,6 @@ app.global_options({
|
|
390
430
|
:version => [ "--version" , "print version number"],
|
391
431
|
:list => ["-l", "--list" , "list action names"],
|
392
432
|
})
|
393
|
-
APP = app
|
394
433
|
|
395
434
|
## 'hello' action
|
396
435
|
app.action("hello", "greeting message", {
|
@@ -416,15 +455,13 @@ GARBAGE_FILES = []
|
|
416
455
|
PRODUCT_FILES = []
|
417
456
|
|
418
457
|
## main
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
exit status_code
|
430
|
-
end
|
458
|
+
status_code = app.main(ARGV)
|
459
|
+
exit status_code
|
460
|
+
## or
|
461
|
+
#begin
|
462
|
+
# app.run(*ARGV)
|
463
|
+
# exit 0
|
464
|
+
#rescue OptionParser::ParseError, CLIApp::ActionError => exc
|
465
|
+
# $stderr.puts "[ERROR] #{exc.message}"
|
466
|
+
# exit 1
|
467
|
+
#end
|
data/test/action_test.rb
CHANGED
data/test/all.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative './init'
|
5
|
+
|
6
|
+
testdir = File.dirname(__FILE__)
|
7
|
+
Dir.glob(testdir + "/**/*_test.rb").sort.each do |fpath|
|
8
|
+
#require File.absolute_path(fpath)
|
9
|
+
require_relative fpath.sub(testdir, '.')
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
if __FILE__ == $0
|
14
|
+
Oktest.module_eval do
|
15
|
+
## change default reporting style from 'verbose' to 'plain'
|
16
|
+
if defined?(REPORTER_CLASS) # Oktest < 1.5
|
17
|
+
remove_const :REPORTER_CLASS
|
18
|
+
const_set :REPORTER_CLASS, Oktest::PlainReporter
|
19
|
+
else # Oktst >= 1.5
|
20
|
+
self.DEFAULT_REPORTING_STYLE = "plain"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/test/application_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
|
@@ -48,20 +48,21 @@ Oktest.scope do
|
|
48
48
|
app
|
49
49
|
end
|
50
50
|
|
51
|
-
|
52
|
-
|
51
|
+
APP_HELP_COLOR = <<"END"
|
52
|
+
\e[1mSample\e[0m \e[2m(0.1.2)\e[0m --- Sample Application
|
53
53
|
|
54
|
-
|
55
|
-
$
|
54
|
+
\e[36mUsage:\e[0m
|
55
|
+
$ \e[1msample\e[0m [<options>] <action> [<arguments>...]
|
56
56
|
|
57
|
-
|
57
|
+
\e[36mOptions:\e[0m
|
58
58
|
-h, --help print help message
|
59
59
|
-V, --version print version number
|
60
60
|
|
61
|
-
|
61
|
+
\e[36mActions:\e[0m
|
62
62
|
clean delete garbage files (& product files too if '-a')
|
63
63
|
hello greeting message
|
64
64
|
END
|
65
|
+
APP_HELP_MONO = APP_HELP_COLOR.gsub(/\e\[.*?m/, '')
|
65
66
|
|
66
67
|
|
67
68
|
topic '#global_options()' do
|
@@ -146,6 +147,50 @@ END
|
|
146
147
|
end
|
147
148
|
|
148
149
|
|
150
|
+
topic '#main()' do
|
151
|
+
|
152
|
+
spec "[!hopc3] returns 0 if finished successfully." do
|
153
|
+
|app|
|
154
|
+
ret = nil
|
155
|
+
capture_stdout do
|
156
|
+
ret = app.main(["hello"])
|
157
|
+
end
|
158
|
+
ok {ret} == 0
|
159
|
+
end
|
160
|
+
|
161
|
+
spec "[!uwcq7] yields block with error object if error raised." do
|
162
|
+
|app|
|
163
|
+
exc = nil
|
164
|
+
app.main(["helloxx"]) do |exc_|
|
165
|
+
exc = exc_
|
166
|
+
end
|
167
|
+
ok {exc}.is_a?(CLIApp::ActionNotFoundError)
|
168
|
+
ok {exc.message} == "helloxx: Action not found."
|
169
|
+
end
|
170
|
+
|
171
|
+
spec "[!e0t6k] reports error into stderr if block not given." do
|
172
|
+
|app|
|
173
|
+
serr = capture_stderr do
|
174
|
+
app.main(["helloxx"])
|
175
|
+
end
|
176
|
+
ok {serr} == "[ERROR] helloxx: Action not found.\n"
|
177
|
+
end
|
178
|
+
|
179
|
+
spec "[!d0g0w] returns 1 if error raised." do
|
180
|
+
|app|
|
181
|
+
ret = app.main(["helloxx"]) do end
|
182
|
+
ok {ret} == 1
|
183
|
+
#
|
184
|
+
ret = nil
|
185
|
+
capture_stderr do
|
186
|
+
ret = app.main(["helloxx"])
|
187
|
+
end
|
188
|
+
ok {ret} == 1
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
|
149
194
|
topic '#run()' do
|
150
195
|
|
151
196
|
spec "[!qv5fz] parses global options (not parses action options)." do
|
@@ -166,7 +211,7 @@ END
|
|
166
211
|
|
167
212
|
spec "[!j029i] prints help message if no action name specified." do
|
168
213
|
|app|
|
169
|
-
sout = capture_stdout { app.run() }
|
214
|
+
sout = capture_stdout(tty: false) { app.run() }
|
170
215
|
ok {sout} =~ /^Sample \(0\.1\.2\) --- Sample Application$/
|
171
216
|
end
|
172
217
|
|
@@ -187,7 +232,15 @@ END
|
|
187
232
|
|
188
233
|
spec "[!ehshp] prints action help if action option contains help option." do
|
189
234
|
|app|
|
190
|
-
sout = capture_stdout do
|
235
|
+
sout = capture_stdout(tty: true) do
|
236
|
+
app.run("hello", "Alice", "--help")
|
237
|
+
end
|
238
|
+
ok {sout} =~ /^\e\[1msample hello\e\[0m --- greeting message$/
|
239
|
+
end
|
240
|
+
|
241
|
+
spec "[!6vet4] decolorizes action help message when stdout is not a tty." do
|
242
|
+
|app|
|
243
|
+
sout = capture_stdout(tty: false) do
|
191
244
|
app.run("hello", "Alice", "--help")
|
192
245
|
end
|
193
246
|
ok {sout} =~ /^sample hello --- greeting message$/
|
@@ -209,10 +262,20 @@ END
|
|
209
262
|
spec "[!6n0w0] when '-h' or '--help' specified, prints help message and returns true." do
|
210
263
|
|app|
|
211
264
|
ret = nil
|
212
|
-
sout = capture_stdout do
|
265
|
+
sout = capture_stdout(tty: true) do
|
213
266
|
ret = app.handle_global_options({help: true})
|
214
267
|
end
|
215
|
-
ok {sout}
|
268
|
+
ok {sout} == APP_HELP_COLOR
|
269
|
+
ok {ret} == true
|
270
|
+
end
|
271
|
+
|
272
|
+
spec "[!fadq1] decolorizes app help message when stdout is not a tty." do
|
273
|
+
|app|
|
274
|
+
ret = nil
|
275
|
+
sout = capture_stdout(tty: false) do
|
276
|
+
ret = app.handle_global_options({help: true})
|
277
|
+
end
|
278
|
+
ok {sout} == APP_HELP_MONO
|
216
279
|
ok {ret} == true
|
217
280
|
end
|
218
281
|
|
@@ -271,35 +334,37 @@ END
|
|
271
334
|
|
272
335
|
spec "[!p02s2] builds application help message." do
|
273
336
|
|app|
|
274
|
-
ok {app.application_help_message()} ==
|
337
|
+
ok {app.application_help_message()} == APP_HELP_COLOR
|
275
338
|
end
|
276
339
|
|
277
340
|
spec "[!41l2g] includes version number if it is specified." do
|
278
341
|
|app|
|
279
342
|
ok {app.config.version} == "0.1.2"
|
280
|
-
ok {app.application_help_message()} =~
|
343
|
+
ok {app.application_help_message()} =~ /^\e\[1mSample\e\[0m \e\[2m\(0\.1\.2\)\e\[0m ---/
|
281
344
|
app2 = CLIApp.new("Sample", "Sample App", command: "sample", version: nil)
|
282
|
-
ok {app2.application_help_message()} =~
|
345
|
+
ok {app2.application_help_message()} =~ /^\e\[1mSample\e\[0m ---/
|
283
346
|
end
|
284
347
|
|
285
348
|
spec "[!2eycw] includes 'Options:' section if any global options exist." do
|
286
349
|
|app|
|
287
|
-
|
288
|
-
ok {app.application_help_message()} =~
|
350
|
+
header_rexp = /^\e\[36mOptions:\e\[0m$/
|
351
|
+
ok {app.application_help_message()} =~ header_rexp
|
352
|
+
ok {app.application_help_message()} =~ /^ \$ \e\[1msample\e\[0m \[<options>\] <action> \[<arguments>\.\.\.\]$/
|
289
353
|
app2 = CLIApp.new("Sample", "Sample App", command: "sample", version: nil)
|
290
354
|
app2.action("hello", "Hello") do end
|
291
|
-
ok {app2.application_help_message()} !~
|
292
|
-
ok {app2.application_help_message()} =~ /^ \$
|
355
|
+
ok {app2.application_help_message()} !~ header_rexp
|
356
|
+
ok {app2.application_help_message()} =~ /^ \$ \e\[1msample\e\[0m <action> \[<arguments>\.\.\.\]$/
|
293
357
|
end
|
294
358
|
|
295
359
|
spec "[!x3dim] includes 'Actions:' section if any actions defined." do
|
296
360
|
|app|
|
297
|
-
|
298
|
-
ok {app.application_help_message()} =~
|
361
|
+
header_rexp = /^\e\[36mActions:\e\[0m$/
|
362
|
+
ok {app.application_help_message()} =~ header_rexp
|
363
|
+
ok {app.application_help_message()} =~ /^ \$ \e\[1msample\e\[0m \[<options>\] <action> \[<arguments>\.\.\.\]$/
|
299
364
|
app2 = CLIApp.new("Sample", "Sample App", command: "sample", version: nil)
|
300
365
|
app2.global_options({:version=>["-V", "version"]})
|
301
|
-
ok {app2.application_help_message()} !~
|
302
|
-
ok {app2.application_help_message()} =~ /^ \$
|
366
|
+
ok {app2.application_help_message()} !~ header_rexp
|
367
|
+
ok {app2.application_help_message()} =~ /^ \$ \e\[1msample\e\[0m \[<options>\]$/
|
303
368
|
end
|
304
369
|
|
305
370
|
spec "[!vxcin] help message will be affcted by config." do
|
@@ -307,17 +372,17 @@ END
|
|
307
372
|
app.config.help_indent = " | "
|
308
373
|
app.config.help_option_width = 14
|
309
374
|
app.config.help_action_width = 6
|
310
|
-
ok {app.application_help_message()} == <<
|
311
|
-
|
375
|
+
ok {app.application_help_message()} == <<"END"
|
376
|
+
\e[1mSample\e[0m \e[2m(0.1.2)\e[0m --- Sample Application
|
312
377
|
|
313
|
-
|
314
|
-
| $
|
378
|
+
\e[36mUsage:\e[0m
|
379
|
+
| $ \e[1msample\e[0m [<options>] <action> [<arguments>...]
|
315
380
|
|
316
|
-
|
381
|
+
\e[36mOptions:\e[0m
|
317
382
|
| -h, --help print help message
|
318
383
|
| -V, --version print version number
|
319
384
|
|
320
|
-
|
385
|
+
\e[36mActions:\e[0m
|
321
386
|
| clean delete garbage files (& product files too if '-a')
|
322
387
|
| hello greeting message
|
323
388
|
END
|
@@ -328,26 +393,27 @@ END
|
|
328
393
|
|
329
394
|
topic '#action_help_message()' do
|
330
395
|
|
331
|
-
|
332
|
-
|
396
|
+
ACTION_HELP_COLOR = <<"END"
|
397
|
+
\e[1msample hello\e[0m --- greeting message
|
333
398
|
|
334
|
-
|
399
|
+
\e[36mUsage:\e[0m
|
335
400
|
$ sample hello [<options>] [<name>]
|
336
401
|
|
337
|
-
|
402
|
+
\e[36mOptions:\e[0m
|
338
403
|
-l, --lang=<en|fr|it> language
|
339
404
|
END
|
405
|
+
ACTION_HELP_MONO = ACTION_HELP_COLOR.gsub(/\e\[.*?m/, '')
|
340
406
|
|
341
407
|
spec "[!ny72g] build action help message." do
|
342
408
|
|app|
|
343
409
|
action = app.get_action(:hello)
|
344
|
-
ok {app.action_help_message(action)} ==
|
410
|
+
ok {app.action_help_message(action)} == ACTION_HELP_COLOR
|
345
411
|
end
|
346
412
|
|
347
413
|
spec "[!pr2vy] includes 'Options:' section if any options exist." do
|
348
414
|
|app|
|
349
415
|
hello = app.get_action(:hello)
|
350
|
-
ok {app.action_help_message(hello)} =~
|
416
|
+
ok {app.action_help_message(hello)} =~ /^\e\[36mOptions:\e\[0m$/
|
351
417
|
ok {app.action_help_message(hello)} =~ /^ \$ sample hello \[<options>\] \[<name>\]$/
|
352
418
|
clean = app.get_action(:clean)
|
353
419
|
ok {app.action_help_message(clean)} !~ /^Options:$/
|
@@ -359,13 +425,13 @@ END
|
|
359
425
|
app.config.help_indent = " | "
|
360
426
|
app.config.help_option_width = 25
|
361
427
|
hello = app.get_action(:hello)
|
362
|
-
ok {app.action_help_message(hello)} == <<
|
363
|
-
|
428
|
+
ok {app.action_help_message(hello)} == <<"END"
|
429
|
+
\e[1msample hello\e[0m --- greeting message
|
364
430
|
|
365
|
-
|
431
|
+
\e[36mUsage:\e[0m
|
366
432
|
| $ sample hello [<options>] [<name>]
|
367
433
|
|
368
|
-
|
434
|
+
\e[36mOptions:\e[0m
|
369
435
|
| -l, --lang=<en|fr|it> language
|
370
436
|
END
|
371
437
|
end
|
@@ -519,12 +585,22 @@ END
|
|
519
585
|
|
520
586
|
spec "[!w5lq9] prints application help message." do
|
521
587
|
|app|
|
522
|
-
sout = capture_stdout() do
|
588
|
+
sout = capture_stdout(tty: true) do
|
523
589
|
app.instance_eval do
|
524
590
|
do_when_action_not_specified({})
|
525
591
|
end
|
526
592
|
end
|
527
|
-
ok {sout} ==
|
593
|
+
ok {sout} == APP_HELP_COLOR
|
594
|
+
end
|
595
|
+
|
596
|
+
spec "[!audam] decolorizes help message when stdout is not a tty." do
|
597
|
+
|app|
|
598
|
+
sout = capture_stdout(tty: false) do
|
599
|
+
app.instance_eval do
|
600
|
+
do_when_action_not_specified({})
|
601
|
+
end
|
602
|
+
end
|
603
|
+
ok {sout} == APP_HELP_MONO
|
528
604
|
end
|
529
605
|
|
530
606
|
spec "[!txqnr] returns true which means 'done'." do
|
@@ -541,6 +617,24 @@ END
|
|
541
617
|
end
|
542
618
|
|
543
619
|
|
620
|
+
topic '#decolorize()' do
|
621
|
+
|
622
|
+
spec "[!es6ay] decolorizes str if stdout is not a tty." do
|
623
|
+
|app|
|
624
|
+
str = "\e[36mABC\e[0mDEF\e[1mGHI\e[0m"
|
625
|
+
capture_stdout(tty: false) do
|
626
|
+
s = app.instance_eval { decolorize(str) }
|
627
|
+
ok {s} == "ABCDEFGHI"
|
628
|
+
end
|
629
|
+
capture_stdout(tty: true) do
|
630
|
+
s = app.instance_eval { decolorize(str) }
|
631
|
+
ok {s} == str
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
end
|
636
|
+
|
637
|
+
|
544
638
|
end
|
545
639
|
|
546
640
|
|
data/test/init.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'oktest'
|
5
5
|
|
6
|
+
testdir = File.dirname(__FILE__)
|
7
|
+
libdir = File.absolute_path(File.dirname(testdir) + "/lib")
|
8
|
+
$LOAD_PATH << libdir unless $LOAD_PATH.include?(libdir)
|
9
|
+
|
6
10
|
require 'cliapp'
|
7
11
|
|
8
12
|
|
9
13
|
Oktest.global_scope do
|
10
14
|
|
15
|
+
## define global fixtures here
|
16
|
+
|
11
17
|
end
|
data/test/module_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
|
@@ -67,19 +67,20 @@ END
|
|
67
67
|
ok {str}.is_a?(String)
|
68
68
|
filename = "tmp.sample"
|
69
69
|
dummy_file(filename, str)
|
70
|
+
ruby = "#{RbConfig.ruby} -Ilib"
|
70
71
|
#
|
71
|
-
sout, serr = capture_command "ruby #{filename} --help"
|
72
|
+
sout, serr = capture_command "#{ruby} #{filename} --help"
|
72
73
|
ok {serr} == ""
|
73
74
|
ok {sout} == GLOBAL_HELP
|
74
75
|
#
|
75
|
-
sout, serr = capture_command "ruby #{filename} hello --help"
|
76
|
+
sout, serr = capture_command "#{ruby} #{filename} hello --help"
|
76
77
|
ok {serr} == ""
|
77
78
|
ok {sout} == HELLO_HELP
|
78
79
|
#
|
79
|
-
sout, serr = capture_command "ruby #{filename} hello"
|
80
|
+
sout, serr = capture_command "#{ruby} #{filename} hello"
|
80
81
|
ok {serr} == ""
|
81
82
|
ok {sout} == "Hello, world!\n"
|
82
|
-
sout, serr = capture_command "ruby #{filename} hello Alice --lang=fr"
|
83
|
+
sout, serr = capture_command "#{ruby} #{filename} hello Alice --lang=fr"
|
83
84
|
ok {serr} == ""
|
84
85
|
ok {sout} == "Bonjour, Alice!\n"
|
85
86
|
end
|
data/test/util_test.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cliapp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kwatch
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oktest
|
@@ -26,20 +26,19 @@ dependencies:
|
|
26
26
|
version: '1.4'
|
27
27
|
description: 'A small framework for CLI Applications such as Git, Docker, NPM, etc.
|
28
28
|
|
29
|
-
|
29
|
+
'
|
30
30
|
email: kwatch@gmail.com
|
31
31
|
executables: []
|
32
32
|
extensions: []
|
33
33
|
extra_rdoc_files: []
|
34
34
|
files:
|
35
|
+
- CHANGES.md
|
35
36
|
- MIT-LICENSE
|
36
37
|
- README.md
|
37
|
-
- Rakefile.rb
|
38
38
|
- cliapp.gemspec
|
39
39
|
- lib/cliapp.rb
|
40
|
-
- task/common-task.rb
|
41
|
-
- task/release-task.rb
|
42
40
|
- test/action_test.rb
|
41
|
+
- test/all.rb
|
43
42
|
- test/application_test.rb
|
44
43
|
- test/init.rb
|
45
44
|
- test/module_test.rb
|
@@ -48,7 +47,7 @@ homepage: https://github.com/kwatch/cliapp-ruby
|
|
48
47
|
licenses:
|
49
48
|
- MIT
|
50
49
|
metadata: {}
|
51
|
-
post_install_message:
|
50
|
+
post_install_message:
|
52
51
|
rdoc_options: []
|
53
52
|
require_paths:
|
54
53
|
- lib
|
@@ -64,11 +63,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
63
|
version: '0'
|
65
64
|
requirements: []
|
66
65
|
rubygems_version: 3.0.3.1
|
67
|
-
signing_key:
|
66
|
+
signing_key:
|
68
67
|
specification_version: 4
|
69
68
|
summary: CLI Application Framework
|
70
69
|
test_files:
|
71
|
-
- test/
|
72
|
-
- test/util_test.rb
|
73
|
-
- test/action_test.rb
|
74
|
-
- test/application_test.rb
|
70
|
+
- test/all.rb
|
data/Rakefile.rb
DELETED
data/task/common-task.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
|
4
|
-
task :default => :help # or :test if you like
|
5
|
-
|
6
|
-
|
7
|
-
desc "list task names"
|
8
|
-
task :help do
|
9
|
-
system "rake -T"
|
10
|
-
end
|
11
|
-
|
12
|
-
|
13
|
-
desc "show how to release"
|
14
|
-
task :howto, [:version] do |t, args|
|
15
|
-
ver = args[:version] || ENV['version'] || "0.0.0"
|
16
|
-
zero_p = ver.end_with?('.0')
|
17
|
-
opt_b = zero_p ? " -b" : ""
|
18
|
-
puts <<"END"
|
19
|
-
How to release:
|
20
|
-
|
21
|
-
$ git diff # confirm that there is no changes
|
22
|
-
$ rake test
|
23
|
-
$ rake test:all # test on Ruby 2.x ~ 3.x
|
24
|
-
$ git checkout#{opt_b} rel-#{ver[0..-3]} # create or switch to release branch
|
25
|
-
$ vi CHANGES.md # if necessary
|
26
|
-
$ git add CHANGES.md # if necessary
|
27
|
-
$ git commit -m "Update 'CHANGES.md'" # if necessary
|
28
|
-
$ git log -1 # if necessary
|
29
|
-
$ cid=$(git log -1 | awk 'NR==1{print $2}') # if necessary
|
30
|
-
$ rake prepare[#{ver}] # update release number
|
31
|
-
$ git add -u . # add changes
|
32
|
-
$ git status -sb . # list files in staging area
|
33
|
-
$ git commit -m "Preparation for release #{ver}"
|
34
|
-
$ rake package # create a gem package
|
35
|
-
$ rake release[#{ver}] # upload to rubygems.org
|
36
|
-
$ git push -u origin
|
37
|
-
$ git tag | fgrep #{ver} # confirm release tag
|
38
|
-
$ git push --tags
|
39
|
-
$ git checkout - # back to main branch
|
40
|
-
$ git log -1 $cid # if necessary
|
41
|
-
$ git cherry-pick $cid # if necessary
|
42
|
-
|
43
|
-
END
|
44
|
-
end
|
45
|
-
|
46
|
-
|
47
|
-
desc "run test scripts"
|
48
|
-
task :test do
|
49
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), "lib")
|
50
|
-
sh "oktest test -sp"
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
desc "run test scripts on Ruby 2.x and 3.x"
|
55
|
-
task :'test:all' do
|
56
|
-
vs_home = ENV['VS_HOME'] or raise "$VS_HOME should be set."
|
57
|
-
defined?(RUBY_VERSIONS) or raise "RUBY_VERSIONS should be defined."
|
58
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), "lib")
|
59
|
-
RUBY_VERSIONS.each do |ver|
|
60
|
-
path_pat = "#{vs_home}/ruby/#{ver}.*/bin/ruby"
|
61
|
-
ruby_path = Dir.glob(path_pat).sort.last() or
|
62
|
-
raise "#{path_pat}: Not exist."
|
63
|
-
puts "\e[33m======== Ruby #{ver} ========\e[0m"
|
64
|
-
sh "#{ruby_path} -r oktest -e 'Oktest.main' -- test -sp" do end
|
65
|
-
puts ""
|
66
|
-
end
|
67
|
-
end
|
data/task/release-task.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
|
4
|
-
desc "update version number"
|
5
|
-
task :prepare, [:version] do |t, args|
|
6
|
-
version = version_number_required(args, :prepare)
|
7
|
-
spec = load_gemspec_file(SPECFILE)
|
8
|
-
edit(spec.files) {|s|
|
9
|
-
s.gsub(/\$Version\:.*?\$/, "$Version\: #{version} $") \
|
10
|
-
.gsub(/\$Version\$/, version)
|
11
|
-
}
|
12
|
-
end
|
13
|
-
|
14
|
-
|
15
|
-
desc "create gem package"
|
16
|
-
task :package do
|
17
|
-
sh "gem build #{SPECFILE}"
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
desc "upload gem to rubygems.org"
|
22
|
-
task :release, [:version] => :package do |t, args|
|
23
|
-
version = version_number_required(args, :release)
|
24
|
-
spec = load_gemspec_file(SPECFILE)
|
25
|
-
version == spec.version.to_s or
|
26
|
-
raise "Version in gemspec file (#{spec.version}) is different from #{version}"
|
27
|
-
gemfile = "#{PROJECT}-#{version}.gem"
|
28
|
-
print "*** Are you sure to upload #{gemfile}? [y/N]: "
|
29
|
-
answer = $stdin.gets().strip()
|
30
|
-
if answer =~ /\A[yY]/
|
31
|
-
sh "git tag v#{version}"
|
32
|
-
#sh "git tag rel-#{version}"
|
33
|
-
sh "gem push #{gemfile}"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
|
38
|
-
##
|
39
|
-
## helpers
|
40
|
-
##
|
41
|
-
|
42
|
-
def edit(*filepaths)
|
43
|
-
filepaths.flatten.each do |fpath|
|
44
|
-
next if ! File.file?(fpath)
|
45
|
-
File.open(fpath, 'r+b:utf-8') do |f|
|
46
|
-
s = f.read()
|
47
|
-
new_s = yield s
|
48
|
-
if new_s != s
|
49
|
-
f.rewind()
|
50
|
-
f.truncate(0)
|
51
|
-
f.write(new_s)
|
52
|
-
puts "[Change] #{fpath}"
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def load_gemspec_file(gemspec_file)
|
59
|
-
require 'rubygems'
|
60
|
-
return Gem::Specification::load(gemspec_file)
|
61
|
-
end
|
62
|
-
|
63
|
-
def version_number_required(args, task_name)
|
64
|
-
version = args[:version] || ENV['version']
|
65
|
-
unless version
|
66
|
-
$stderr.puts <<"END"
|
67
|
-
##
|
68
|
-
## ERROR: rake #{task_name}: requires 'version=X.X.X' option.
|
69
|
-
## For example:
|
70
|
-
## $ rake #{task_name} version=1.0.0
|
71
|
-
##
|
72
|
-
END
|
73
|
-
errmsg = "rake #{task_name}: requires 'version=X.X.X' option."
|
74
|
-
raise ArgumentError.new(errmsg)
|
75
|
-
end
|
76
|
-
return version
|
77
|
-
end
|