cliapp 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|