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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b398e8d768022a0d3ecaf12d9e63f1d5ada3a18e14f7fa0dcb737f2ec4b55aaf
4
- data.tar.gz: d1ec659812a404e055a0b11b873d118012e4090cd51bffa0f3a9639c3f0478b4
3
+ metadata.gz: a908a368f333d3ad499d7439721fc9c08e9bf0eb7cbb2191acd415de76994b1b
4
+ data.tar.gz: '08a8841bd44f3a29124c7670079970e11ed23ca5cde7c947f9382178340d7980'
5
5
  SHA512:
6
- metadata.gz: 98e18468f041be66b58e3ee92523a35b999dc9396fa4d60d6125a9007830dc410bc7cc1d2d3bae69992cc05de0ff7c2a24512ac3d52a642ef1abbacaf04b6a3e
7
- data.tar.gz: 220c43cf0dbf9a2db65bdd5d48a0fd02d6f44c9a3379b05673c670d9116bba780f05ac73e06ef9ac1f883a598f417b2ad6f8450159070e627c7b1c4a5ef9f351
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.1.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
- def main(argv=ARGV)
75
- APP.run(*argv)
76
- return 0
77
- rescue OptionParser::ParseError, CLIApp::ActionError => exc
78
- $stderr.puts "[ERROR] #{exc.message}"
79
- return 1
80
- end
81
-
82
- if __FILE__ == $0
83
- status_code = main(ARGV)
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
- # -*- coding: utf-8 -*-
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.1.0 $'.split()[1]
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[*%w[
16
- README.md MIT-LICENSE Rakefile.rb cliapp.gemspec
17
- lib/**/*.rb
18
- test/**/*.rb
19
- task/**/*.rb
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 = ">= 2.4"
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
- # -*- coding: utf-8 -*-
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.1.0 $
8
- ## $Copyright: copyright (c)2014 kwatch@gmail.com $
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: "%-#{@actionlist_width}s : %s"
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
- print action_help_message(action)
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
- print application_help_message()
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
- ver = c.version ? " (#{c.version})" : nil
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} #{action.name} --- #{action.desc}
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
- print application_help_message()
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
- # coding: utf-8
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
- def main(argv=ARGV)
420
- APP.run(*argv)
421
- return 0
422
- rescue OptionParser::ParseError, CLIApp::ActionError => exc
423
- $stderr.puts "[ERROR] #{exc.message}"
424
- return 1
425
- end
426
-
427
- if __FILE__ == $0
428
- status_code = main(ARGV)
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
@@ -1,4 +1,4 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
 
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
@@ -1,4 +1,4 @@
1
- # -*- coding: utf-8 -*-
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
- APP_HELP = <<'END'
52
- Sample (0.1.2) --- Sample Application
51
+ APP_HELP_COLOR = <<"END"
52
+ \e[1mSample\e[0m \e[2m(0.1.2)\e[0m --- Sample Application
53
53
 
54
- Usage:
55
- $ sample [<options>] <action> [<arguments>...]
54
+ \e[36mUsage:\e[0m
55
+ $ \e[1msample\e[0m [<options>] <action> [<arguments>...]
56
56
 
57
- Options:
57
+ \e[36mOptions:\e[0m
58
58
  -h, --help print help message
59
59
  -V, --version print version number
60
60
 
61
- Actions:
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} =~ /^Sample \(0\.1\.2\) --- Sample Application$/
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()} == APP_HELP
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()} =~ /^Sample \(0\.1\.2\) ---/
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()} =~ /^Sample ---/
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
- ok {app.application_help_message()} =~ /^Options:$/
288
- ok {app.application_help_message()} =~ /^ \$ sample \[<options>\] <action> \[<arguments>\.\.\.\]$/
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()} !~ /^Options:$/
292
- ok {app2.application_help_message()} =~ /^ \$ sample <action> \[<arguments>\.\.\.\]$/
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
- ok {app.application_help_message()} =~ /^Actions:$/
298
- ok {app.application_help_message()} =~ /^ \$ sample \[<options>\] <action> \[<arguments>\.\.\.\]$/
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()} !~ /^Actions:$/
302
- ok {app2.application_help_message()} =~ /^ \$ sample \[<options>\]$/
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()} == <<'END'
311
- Sample (0.1.2) --- Sample Application
375
+ ok {app.application_help_message()} == <<"END"
376
+ \e[1mSample\e[0m \e[2m(0.1.2)\e[0m --- Sample Application
312
377
 
313
- Usage:
314
- | $ sample [<options>] <action> [<arguments>...]
378
+ \e[36mUsage:\e[0m
379
+ | $ \e[1msample\e[0m [<options>] <action> [<arguments>...]
315
380
 
316
- Options:
381
+ \e[36mOptions:\e[0m
317
382
  | -h, --help print help message
318
383
  | -V, --version print version number
319
384
 
320
- Actions:
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
- ACTION_HELP = <<'END'
332
- sample hello --- greeting message
396
+ ACTION_HELP_COLOR = <<"END"
397
+ \e[1msample hello\e[0m --- greeting message
333
398
 
334
- Usage:
399
+ \e[36mUsage:\e[0m
335
400
  $ sample hello [<options>] [<name>]
336
401
 
337
- Options:
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)} == ACTION_HELP
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)} =~ /^Options:$/
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)} == <<'END'
363
- sample hello --- greeting message
428
+ ok {app.action_help_message(hello)} == <<"END"
429
+ \e[1msample hello\e[0m --- greeting message
364
430
 
365
- Usage:
431
+ \e[36mUsage:\e[0m
366
432
  | $ sample hello [<options>] [<name>]
367
433
 
368
- Options:
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} == APP_HELP
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
- # -*- coding: utf-8 -*-
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
- # -*- coding: utf-8 -*-
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
@@ -1,4 +1,4 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
 
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.1.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-04 00:00:00.000000000 Z
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/module_test.rb
72
- - test/util_test.rb
73
- - test/action_test.rb
74
- - test/application_test.rb
70
+ - test/all.rb
data/Rakefile.rb DELETED
@@ -1,9 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
-
4
- PROJECT = "cliapp"
5
- SPECFILE = PROJECT + ".gemspec"
6
-
7
- RUBY_VERSIONS = %w[3.3 3.2 3.1 3.0 2.7 2.6 2.5 2.4]
8
-
9
- Dir.glob("./task/*-task.rb").each {|x| require x }
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