cmd.rb 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1e81a52d995f13903b644a2dcc326b35b57d37bc9b0e1840509835791a7c3100
4
+ data.tar.gz: fb89b7ca7ba61d017141bebaeaa129589b812c7a7a742e5e0619cda3e4374b86
5
+ SHA512:
6
+ metadata.gz: 9131c3ffccbba0aef5cdcf3f19e7288ce8228b5095a9cac7e021c08202a09b4094a584e520a846c4be13e63f846f06a8b0583daf63dc3c01a8b7ef26185c2f0c
7
+ data.tar.gz: 3eaed61342560a42a56952e74f1723c9f35a08ae22b13c7bb403bb5dd3a8d3ca85cc046a7bda3dab6246a94d36fce301188c011e3f7789a89057e911d138ece8
@@ -0,0 +1,23 @@
1
+ name: cmd.rb
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ tests:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ os: [ubuntu-latest]
15
+ ruby: [3.1, 3.2]
16
+ runs-on: ${{ matrix.os }}
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: ${{ matrix.ruby }}
22
+ - run: bundle install
23
+ - run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.lock
2
+ .yardoc/
3
+ doc/
4
+ .bundle/
5
+ .localgems/
6
+ .gems/
data/.projectile ADDED
@@ -0,0 +1,2 @@
1
+ -.localgems/
2
+ -doc/
data/.rubocop.yml ADDED
@@ -0,0 +1,36 @@
1
+ ##
2
+ # Plugins
3
+ require:
4
+ - standard
5
+
6
+ ##
7
+ # Defaults: standard-rb
8
+ inherit_gem:
9
+ standard: config/base.yml
10
+
11
+ ##
12
+ # All cops
13
+ AllCops:
14
+ TargetRubyVersion: 3.2
15
+ Include:
16
+ - lib/*.rb
17
+ - lib/**/*.rb
18
+ - test/*.rb
19
+
20
+ ##
21
+ # Enabled
22
+ Style/FrozenStringLiteralComment:
23
+ Enabled: true
24
+
25
+ ##
26
+ # Disabled
27
+ Layout/ArgumentAlignment:
28
+ Enabled: false
29
+ Layout/MultilineMethodCallIndentation:
30
+ Enabled: false
31
+ Layout/EmptyLineBetweenDefs:
32
+ Enabled: false
33
+ Style/TrivialAccessors:
34
+ Enabled: false
35
+ Style/CommandLiteral:
36
+ Enabled: false
@@ -0,0 +1,15 @@
1
+ #main #content #filecontents p, .discussion p {
2
+ max-width: 768px;
3
+ }
4
+
5
+ #toc {
6
+ position: fixed;
7
+ right: 25px;
8
+ display: none;
9
+ }
10
+
11
+ @media screen and (min-width: 1280px) {
12
+ #toc {
13
+ display: block;
14
+ }
15
+ }
@@ -0,0 +1,5 @@
1
+ def stylesheets
2
+ s = super
3
+ s << "css/0x1eef.css"
4
+ s
5
+ end
@@ -0,0 +1,7 @@
1
+ ##
2
+ # Sort methods in ascending order based
3
+ # on the line number where a method is
4
+ # defined.
5
+ def sort_listing(listing)
6
+ listing.sort_by { _1.files[1] }
7
+ end
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ -m markdown -M redcarpet --no-private -t default -p .yardoc-template --hide-void-return
2
+ -
3
+ README.md
4
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+ gem "optparse", github: "ruby/optparse"
4
+ gem "ryo.rb", "~> 0.4", github: "0x1eef/ryo.rb", tag: "v0.4.2"
5
+ gem "test-cmd.rb", "~> 0.2", github: "0x1eef/test-cmd.rb"
6
+ gem "webrick"
data/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright (C) 2023 by 0x1eef <0x1eef@protonmail.com>
2
+
3
+ Permission to use, copy, modify, and/or distribute this
4
+ software for any purpose with or without fee is hereby
5
+ granted.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
8
+ ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
9
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
10
+ EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
12
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
14
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
15
+ OF THIS SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ ## About
2
+
3
+ cmd.rb is a library for building command-line applications
4
+ in Ruby. The interface is centered around a class that implements
5
+ a command (or it could be a sub-command) that defines a banner,
6
+ a set of options, and a set of defaults for those options. The
7
+ option parsing implementation is delegated to
8
+ [ruby/optparse](https://github.com/ruby/optparse)
9
+ (with a few minor enhancements).
10
+
11
+ ## Example
12
+
13
+ The following example demonstrates a simple command that is
14
+ implemented with `Dir.entries`. The command accepts two options
15
+ that have fallback default values set:
16
+
17
+ **Definition**
18
+
19
+ ```ruby
20
+ #!/usr/bin/env ruby
21
+
22
+ require "cmd"
23
+ class Ls < Cmd
24
+ set_banner usage: "ls [OPTIONS]",
25
+ description: "Lists the contents of a directory"
26
+ set_option "-g PATTERN", "--grep PATTERN", "A regular expression", as: Regexp, default: /.+/
27
+ set_option "-d PATH", "--directory PATH", "A path to a directory", as: String, default: Dir.home
28
+
29
+ def run
30
+ options = parse_options(argv)
31
+ options.help ? show_help : run_command(options)
32
+ end
33
+
34
+ private
35
+
36
+ def run_command(options)
37
+ puts Dir.entries(options.directory)
38
+ .grep(options.grep)
39
+ .sort
40
+ .join("\n")
41
+ end
42
+ end
43
+
44
+ ##
45
+ # Run the command
46
+ Ls.new(ARGV).run
47
+ ```
48
+
49
+ **Command help (-h)**
50
+
51
+ ```
52
+ $ chmod u+x ls
53
+ $ ./ls --help
54
+ Usage: ls [OPTIONS]
55
+
56
+ Description:
57
+ Lists the contents of a directory
58
+
59
+ Options:
60
+ -g, --grep PATTERN A regular expression
61
+ -p, --path PATH A path to a directory
62
+ -h, --help Show help
63
+
64
+ ```
65
+
66
+ ## Sources
67
+
68
+ * [Source code (GitHub)](https://github.com/0x1eef/cmd.rb#readme)
69
+ * [Source code (GitLab)](https://gitlab.com/0x1eef/cmd.rb#about)
70
+
71
+ ## Install
72
+
73
+ cmd.rb is distributed as a RubyGem through its git repositories. <br>
74
+ [GitHub](https://github.com/0x1eef/cmd.rb),
75
+ and
76
+ [GitLab](https://gitlab.com/0x1eef/cmd.rb)
77
+ are available as sources.
78
+
79
+ **Gemfile**
80
+
81
+ ```ruby
82
+ gem "cmd.rb", github: "0x1eef/cmd.rb"
83
+ gem "ryo.rb", github: "0x1eef/ryo.rb"
84
+ ```
85
+
86
+ ## <a id="license"> License </a>
87
+
88
+ [BSD Zero Clause](https://choosealicense.com/licenses/0bsd/).
89
+ <br>
90
+ See [LICENSE](./LICENSE).
data/Rakefile.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.test_files = FileList['test/*_test.rb']
5
+ t.verbose = true
6
+ t.warning = false
7
+ end
8
+ task default: :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ v0.3.2
data/cmd.rb.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ version = File.binread File.join(__dir__, "VERSION")
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "cmd.rb"
6
+ gem.authors = ["0x1eef"]
7
+ gem.email = ["0x1eef@protonmail.com"]
8
+ gem.homepage = "https://github.com/0x1eef/cmd.rb#readme"
9
+ gem.version = version[/[\d.]+/]
10
+ gem.licenses = ["0BSD"]
11
+ gem.files = `git ls-files`.split($/)
12
+ gem.require_paths = ["lib"]
13
+ gem.summary = "A library for building command-line applications"
14
+ gem.description = gem.summary
15
+ gem.add_runtime_dependency "ryo.rb", "~> 0.4"
16
+ gem.add_runtime_dependency "optparse", "~> 0.3"
17
+ gem.add_development_dependency "test-unit", "~> 3.5.7"
18
+ gem.add_development_dependency "yard", "~> 0.9"
19
+ gem.add_development_dependency "redcarpet", "~> 3.5"
20
+ gem.add_development_dependency "standard", "~> 1.13"
21
+ gem.add_development_dependency "test-cmd.rb", "~> 0.2"
22
+ gem.add_development_dependency "rake", "~> 13.1"
23
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # The {Cmd::ARGV} module provides an initialize method, and an
5
+ # attribute reader for an array of strings (usually ARGV). Classes
6
+ # who include the {Cmd Cmd} module indirectly include this module as
7
+ # well.
8
+ module Cmd::ARGV
9
+ ##
10
+ # @return [Array<String>]
11
+ # Returns an array of strings.
12
+ attr_reader :argv
13
+
14
+ ##
15
+ # @param [Array<String>] argv
16
+ # An array of strings.
17
+ def initialize(argv)
18
+ @argv = argv.dup
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+
2
+ module Cmd::Help
3
+ def show_help(io = $stdout)
4
+ banner = self.class.banner
5
+ io.puts "Usage: #{banner.usage}\n\n" \
6
+ "Description:\n #{banner.description}\n\n" \
7
+ "Options: #{option_parser.help}\n"
8
+ end
9
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require "ryo"
5
+
6
+ ##
7
+ # The {Cmd::OptionParser Cmd::OptionParser} module provides a light wrapper
8
+ # around Ruby's
9
+ # [OptionParser](https://docs.ruby-lang.org/en/3.2/OptionParser.html)
10
+ # class.
11
+ module Cmd::OptionParser
12
+ def self.included(klass)
13
+ klass.extend(ClassMethods)
14
+ end
15
+
16
+ module ClassMethods
17
+ ##
18
+ # Set command banner.
19
+ #
20
+ # @param [String] usage
21
+ # An example of how a command is used.
22
+ #
23
+ # @param [String] description
24
+ # The description of a command.
25
+ #
26
+ # @return [void]
27
+ def set_banner(usage:, description:)
28
+ banner.usage = usage
29
+ banner.description = description
30
+ end
31
+
32
+ ##
33
+ # Set a command option.
34
+ #
35
+ # @param [String] short
36
+ # Short option switch.
37
+ #
38
+ # @param [String] long
39
+ # Long option switch.
40
+ #
41
+ # @param [String] desc
42
+ # The description of an option.
43
+ #
44
+ # @param [Class] as
45
+ # The type (String, Integer, etc) of the option's value.
46
+ #
47
+ # @param [Object] default
48
+ # The default value for an option.
49
+ #
50
+ # @return [void]
51
+ def set_option(short, long, desc, as: String, default: nil)
52
+ option_parser.on(short, long, desc, as)
53
+ set_default({ short => default, long => default })
54
+ end
55
+
56
+ ##
57
+ # Set command option defaults.
58
+ #
59
+ # @param [{String => Object}] defaults
60
+ #
61
+ # @return [void]
62
+ def set_default(defaults)
63
+ @defaults = Ryo.from(defaults)
64
+ end
65
+
66
+ ##
67
+ # (see Cmd::OptionParser#option_parser)
68
+ def option_parser
69
+ @option_parser ||= ::OptionParser.new(nil, 26, " " * 2).tap do
70
+ _1.banner = ""
71
+ _1.on("-h", "--help", "Show help")
72
+ end
73
+ end
74
+
75
+ ##
76
+ # @private
77
+ def banner
78
+ @banner ||= Ryo({})
79
+ end
80
+
81
+ ##
82
+ # @private
83
+ def defaults
84
+ @defaults ||= Ryo({})
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Parses an array of strings with
90
+ # [OptionParser#parse](https://docs.ruby-lang.org/en/3.2/OptionParser.html#method-i-parse).
91
+ #
92
+ # @param [Array<String>] argv
93
+ # An array of strings
94
+ #
95
+ # @return [Ryo::Object]
96
+ # Returns the parsed options as a Ryo object
97
+ def parse_options(argv)
98
+ options = Ryo.from(self.class.defaults)
99
+ option_parser.parse(argv, into: options)
100
+ options
101
+ end
102
+
103
+ ##
104
+ # Parses an array of strings with
105
+ # [OptionParser#parse!](https://docs.ruby-lang.org/en/3.2/OptionParser.html#method-i-parse-21).
106
+ #
107
+ # @param [Array<String>] argv
108
+ # An array of strings
109
+ #
110
+ # @return [Ryo::Object]
111
+ # Returns the parsed options as a Ryo object
112
+ def parse_options!(argv)
113
+ options = Ryo.from(self.class.defaults)
114
+ option_parser.parse!(argv, into: options)
115
+ options
116
+ end
117
+
118
+ ##
119
+ # @return [OptionParser]
120
+ # Returns an instance of
121
+ # [OptionParser](https://docs.ruby-lang.org/en/3.2/OptionParser.html)
122
+ def option_parser
123
+ self.class.option_parser
124
+ end
125
+ end
data/lib/cmd.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Cmd
4
+ require_relative "cmd/mixins/argv"
5
+ require_relative "cmd/mixins/help"
6
+ require_relative "cmd/mixins/option_parser"
7
+
8
+ include ARGV
9
+ include Help
10
+
11
+ def self.inherited(klass)
12
+ klass.class_eval { include(OptionParser) }
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ require_relative "setup"
4
+
5
+ require "cmd"
6
+ class Ls < Cmd
7
+ set_banner usage: "ls [OPTIONS]",
8
+ description: "Lists the contents of a directory"
9
+ set_option "-g PATTERN", "--grep PATTERN", "A regular expression", as: Regexp, default: /.+/
10
+ set_option "-d PATH", "--directory PATH", "A path to a directory", as: String, default: Dir.home
11
+
12
+ def run
13
+ options = parse_options(argv)
14
+ options.help ? show_help : run_command(options)
15
+ end
16
+
17
+ private
18
+
19
+ def run_command(options)
20
+ puts Dir.entries(options.directory)
21
+ .grep(options.grep)
22
+ .sort
23
+ .join("\n")
24
+ end
25
+ end
26
+
27
+ ##
28
+ # Run the command
29
+ Ls.new(ARGV).run
@@ -0,0 +1 @@
1
+ require "bundler/setup"
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "setup"
4
+
5
+ class READMETest < Test::Unit::TestCase
6
+ include Test::Cmd
7
+
8
+ def test_ls_command
9
+ assert_equal "dev\netc\nhome\n",
10
+ run_example("ls --directory test/fakefs/ --grep [^.gitkeep]").stdout
11
+ end
12
+
13
+ def test_ls_command_help
14
+ help = "Usage: ls [OPTIONS]\n\n" \
15
+ "Description:\n" \
16
+ " Lists the contents of a directory\n\n" \
17
+ "Options: \n" \
18
+ " -h, --help Show help\n" \
19
+ " -g, --grep PATTERN A regular expression\n" \
20
+ " -d, --directory PATH A path to a directory\n\n"
21
+ assert_equal help, run_example("ls --help").stdout
22
+ end
23
+
24
+ private
25
+
26
+ def run_example(command)
27
+ cmd File.join(__dir__, "..", "share", "examples", "cmd.rb", command)
28
+ end
29
+ end
data/test/setup.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "test-unit"
5
+ require "test-cmd"
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cmd.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.2
5
+ platform: ruby
6
+ authors:
7
+ - '0x1eef'
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-12-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ryo.rb
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: optparse
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.5.7
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.5.7
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: redcarpet
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.5'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: standard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.13'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.13'
97
+ - !ruby/object:Gem::Dependency
98
+ name: test-cmd.rb
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.2'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '13.1'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '13.1'
125
+ description: A library for building command-line applications
126
+ email:
127
+ - 0x1eef@protonmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".github/workflows/tests.yml"
133
+ - ".gitignore"
134
+ - ".projectile"
135
+ - ".rubocop.yml"
136
+ - ".yardoc-template/default/fulldoc/html/css/0x1eef.css"
137
+ - ".yardoc-template/default/layout/html/setup.rb"
138
+ - ".yardoc-template/default/module/setup.rb"
139
+ - ".yardopts"
140
+ - Gemfile
141
+ - LICENSE
142
+ - README.md
143
+ - Rakefile.rb
144
+ - VERSION
145
+ - cmd.rb.gemspec
146
+ - lib/cmd.rb
147
+ - lib/cmd/mixins/argv.rb
148
+ - lib/cmd/mixins/help.rb
149
+ - lib/cmd/mixins/option_parser.rb
150
+ - share/examples/cmd.rb/ls
151
+ - share/examples/cmd.rb/setup.rb
152
+ - test/fakefs/.gitkeep
153
+ - test/fakefs/dev/.gitkeep
154
+ - test/fakefs/etc/.gitkeep
155
+ - test/fakefs/home/.gitkeep
156
+ - test/readme_examples_test.rb
157
+ - test/setup.rb
158
+ homepage: https://github.com/0x1eef/cmd.rb#readme
159
+ licenses:
160
+ - 0BSD
161
+ metadata: {}
162
+ post_install_message:
163
+ rdoc_options: []
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ required_rubygems_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ requirements: []
177
+ rubygems_version: 3.4.10
178
+ signing_key:
179
+ specification_version: 4
180
+ summary: A library for building command-line applications
181
+ test_files: []