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 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