command_kit 0.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +29 -0
  4. data/.gitignore +7 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +29 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +283 -0
  11. data/Rakefile +23 -0
  12. data/command_kit.gemspec +60 -0
  13. data/gemspec.yml +14 -0
  14. data/lib/command_kit.rb +1 -0
  15. data/lib/command_kit/arguments.rb +161 -0
  16. data/lib/command_kit/arguments/argument.rb +111 -0
  17. data/lib/command_kit/arguments/argument_value.rb +81 -0
  18. data/lib/command_kit/arguments/usage.rb +6 -0
  19. data/lib/command_kit/colors.rb +355 -0
  20. data/lib/command_kit/command.rb +42 -0
  21. data/lib/command_kit/command_name.rb +95 -0
  22. data/lib/command_kit/commands.rb +299 -0
  23. data/lib/command_kit/commands/auto_load.rb +153 -0
  24. data/lib/command_kit/commands/auto_load/subcommand.rb +90 -0
  25. data/lib/command_kit/commands/auto_require.rb +138 -0
  26. data/lib/command_kit/commands/command.rb +12 -0
  27. data/lib/command_kit/commands/help.rb +43 -0
  28. data/lib/command_kit/commands/parent_command.rb +21 -0
  29. data/lib/command_kit/commands/subcommand.rb +51 -0
  30. data/lib/command_kit/console.rb +141 -0
  31. data/lib/command_kit/description.rb +89 -0
  32. data/lib/command_kit/env.rb +43 -0
  33. data/lib/command_kit/env/home.rb +71 -0
  34. data/lib/command_kit/env/path.rb +71 -0
  35. data/lib/command_kit/examples.rb +99 -0
  36. data/lib/command_kit/exception_handler.rb +55 -0
  37. data/lib/command_kit/help.rb +62 -0
  38. data/lib/command_kit/help/man.rb +125 -0
  39. data/lib/command_kit/inflector.rb +84 -0
  40. data/lib/command_kit/main.rb +103 -0
  41. data/lib/command_kit/options.rb +179 -0
  42. data/lib/command_kit/options/option.rb +171 -0
  43. data/lib/command_kit/options/option_value.rb +90 -0
  44. data/lib/command_kit/options/parser.rb +227 -0
  45. data/lib/command_kit/options/quiet.rb +53 -0
  46. data/lib/command_kit/options/usage.rb +6 -0
  47. data/lib/command_kit/options/verbose.rb +55 -0
  48. data/lib/command_kit/options/version.rb +62 -0
  49. data/lib/command_kit/os.rb +47 -0
  50. data/lib/command_kit/pager.rb +115 -0
  51. data/lib/command_kit/printing.rb +32 -0
  52. data/lib/command_kit/printing/indent.rb +78 -0
  53. data/lib/command_kit/program_name.rb +57 -0
  54. data/lib/command_kit/stdio.rb +138 -0
  55. data/lib/command_kit/usage.rb +102 -0
  56. data/lib/command_kit/version.rb +4 -0
  57. data/lib/command_kit/xdg.rb +138 -0
  58. data/spec/arguments/argument_spec.rb +169 -0
  59. data/spec/arguments/argument_value_spec.rb +126 -0
  60. data/spec/arguments_spec.rb +213 -0
  61. data/spec/colors_spec.rb +470 -0
  62. data/spec/command_kit_spec.rb +8 -0
  63. data/spec/command_name_spec.rb +130 -0
  64. data/spec/command_spec.rb +49 -0
  65. data/spec/commands/auto_load/subcommand_spec.rb +82 -0
  66. data/spec/commands/auto_load_spec.rb +128 -0
  67. data/spec/commands/auto_require_spec.rb +142 -0
  68. data/spec/commands/fixtures/test_auto_load/cli/commands/test1.rb +10 -0
  69. data/spec/commands/fixtures/test_auto_load/cli/commands/test2.rb +10 -0
  70. data/spec/commands/fixtures/test_auto_require/lib/test_auto_require/cli/commands/test1.rb +10 -0
  71. data/spec/commands/help_spec.rb +66 -0
  72. data/spec/commands/parent_command_spec.rb +40 -0
  73. data/spec/commands/subcommand_spec.rb +99 -0
  74. data/spec/commands_spec.rb +767 -0
  75. data/spec/console_spec.rb +201 -0
  76. data/spec/description_spec.rb +203 -0
  77. data/spec/env/home_spec.rb +46 -0
  78. data/spec/env/path_spec.rb +78 -0
  79. data/spec/env_spec.rb +123 -0
  80. data/spec/examples_spec.rb +235 -0
  81. data/spec/exception_handler_spec.rb +103 -0
  82. data/spec/help_spec.rb +119 -0
  83. data/spec/inflector_spec.rb +104 -0
  84. data/spec/main_spec.rb +179 -0
  85. data/spec/options/option_spec.rb +258 -0
  86. data/spec/options/option_value_spec.rb +67 -0
  87. data/spec/options/parser_spec.rb +265 -0
  88. data/spec/options_spec.rb +137 -0
  89. data/spec/os_spec.rb +46 -0
  90. data/spec/pager_spec.rb +154 -0
  91. data/spec/printing/indent_spec.rb +130 -0
  92. data/spec/printing_spec.rb +76 -0
  93. data/spec/program_name_spec.rb +62 -0
  94. data/spec/spec_helper.rb +6 -0
  95. data/spec/stdio_spec.rb +264 -0
  96. data/spec/usage_spec.rb +237 -0
  97. data/spec/xdg_spec.rb +191 -0
  98. metadata +156 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: aa6096447b8ddf59149147d020c3fe6f5fcf6de1c167f749692fc1b50dec1b8a
4
+ data.tar.gz: c4c0a58596e92563f9f69a7fd151a7b7dd9121c8083ca104a21dd3ce53ef2fc5
5
+ SHA512:
6
+ metadata.gz: a777422cddc17a986940b7cd902ac43c01765b6ef98efe717307f545a29dfe975579bb8eacdce9647cd2ccbd0f210b7a8b0ab3ffac026d8341f2c9669ef56a18
7
+ data.tar.gz: 7b768f74911ac136b24f755534d31844febb92b5b034e672b4b93c3a45083b4731d9658830b6f700bc8b70106b772f253291ed15fcd36f5f709351808910c93b
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+ tests:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby:
12
+ # - 2.4
13
+ # - 2.5
14
+ # - 2.6
15
+ - 2.7
16
+ - 3.0
17
+ # TODO: uncomment when jruby supports ruby >= 2.7
18
+ # - jruby
19
+ name: Ruby ${{ matrix.ruby }}
20
+ steps:
21
+ - uses: actions/checkout@v2
22
+ - name: Set up Ruby
23
+ uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: ${{ matrix.ruby }}
26
+ - name: Install dependencies
27
+ run: bundle install --jobs 4 --retry 3
28
+ - name: Run tests
29
+ run: bundle exec rake test
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ /.bundle
2
+ /.yardoc/
3
+ /Gemfile.lock
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /vendor/cache/*.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title "CommandKit Documentation" --protected
data/ChangeLog.md ADDED
@@ -0,0 +1,29 @@
1
+ ### 0.1.0 / 2021-05-XX
2
+
3
+ * Initial release:
4
+ * {CommandKit::Arguments}
5
+ * {CommandKit::Colors}
6
+ * {CommandKit::CommandName}
7
+ * {CommandKit::Commands}
8
+ * {CommandKit::Commands::AutoLoad}
9
+ * {CommandKit::Commands::AutoRequire}
10
+ * {CommandKit::Console}
11
+ * {CommandKit::Description}
12
+ * {CommandKit::Env}
13
+ * {CommandKit::Env::Home}
14
+ * {CommandKit::Env::Path}
15
+ * {CommandKit::Examples}
16
+ * {CommandKit::ExceptionHandler}
17
+ * {CommandKit::Help}
18
+ * {CommandKit::Help::Man}
19
+ * {CommandKit::Main}
20
+ * {CommandKit::Options}
21
+ * {CommandKit::Options::Quiet}
22
+ * {CommandKit::Options::Verbose}
23
+ * {CommandKit::Pager}
24
+ * {CommandKit::Printing}
25
+ * {CommandKit::ProgramName}
26
+ * {CommandKit::Stdio}
27
+ * {CommandKit::Usage}
28
+ * {CommandKit::XDG}
29
+
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'rake'
7
+ gem 'rubygems-tasks', '~> 0.2'
8
+
9
+ gem 'rspec', '~> 3.0'
10
+ gem 'simplecov', '~> 0.20', require: false
11
+
12
+ gem 'kramdown'
13
+ gem 'yard', '~> 0.9'
14
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2021 Hal Brodigan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # command_kit
2
+
3
+ * [Homepage](https://github.com/postmodern/command_kit#readme)
4
+ * [Issues](https://github.com/postmodern/command_kit/issues)
5
+ * [Documentation](http://rubydoc.info/gems/command_kit/frames)
6
+ * [Email](mailto:postmodern.mod3 at gmail.com)
7
+
8
+ ## Description
9
+
10
+ A Ruby toolkit for building clean, correct, and robust CLI commands as Ruby
11
+ classes.
12
+
13
+ ## Features
14
+
15
+ * Supports defining commands as Classes.
16
+ * Supports defining options and arguments as attributes.
17
+ * Supports subcommands (explicit or lazy-loaded) and command aliases.
18
+ * Correctly handles Ctrl^C and SIGINT interrupts (aka exit 130).
19
+ * Correctly handles broken pipes (aka `mycmd | head`).
20
+ * Uses [OptionParser][optparse] for option parsing.
21
+ * Provides ANSI coloring support.
22
+ * Supports optionally displaying a man-page instead of `--help`
23
+ (see {CommandKit::Help::Man}).
24
+ * Supports XDG directories (`~/.config/`, `~/.local/share/`, `~/.cache/`).
25
+ * Easy to test:
26
+ * `MyCmd.main(arg1, arg2, options: {foo: foo}) # => 0`
27
+
28
+ ### Modules
29
+
30
+ * {CommandKit::Arguments}
31
+ * {CommandKit::Colors}
32
+ * {CommandKit::CommandName}
33
+ * {CommandKit::Commands}
34
+ * {CommandKit::Commands::AutoLoad}
35
+ * {CommandKit::Commands::AutoRequire}
36
+ * {CommandKit::Console}
37
+ * {CommandKit::Description}
38
+ * {CommandKit::Env}
39
+ * {CommandKit::Env::Home}
40
+ * {CommandKit::Env::Path}
41
+ * {CommandKit::Examples}
42
+ * {CommandKit::ExceptionHandler}
43
+ * {CommandKit::Help}
44
+ * {CommandKit::Help::Man}
45
+ * {CommandKit::Main}
46
+ * {CommandKit::Options}
47
+ * {CommandKit::Options::Quiet}
48
+ * {CommandKit::Options::Verbose}
49
+ * {CommandKit::Pager}
50
+ * {CommandKit::Printing}
51
+ * {CommandKit::ProgramName}
52
+ * {CommandKit::Stdio}
53
+ * {CommandKit::Usage}
54
+ * {CommandKit::XDG}
55
+
56
+ ## Anti-Features
57
+
58
+ * No additional runtime dependencies.
59
+ * Does not implement it's own option parser.
60
+ * Not named after a comic-book Superhero.
61
+
62
+ ## Examples
63
+
64
+ ### Command
65
+
66
+ #### lib/foo/cli/my_cmd.rb
67
+
68
+ require 'command_kit'
69
+
70
+ module Foo
71
+ module CLI
72
+ class MyCmd < CommandKit::Command
73
+
74
+ usage '[OPTIONS] [-o OUTPUT] FILE'
75
+
76
+ option :count, short: '-c',
77
+ value: {
78
+ type: Integer,
79
+ default: 1
80
+ },
81
+ desc: "Number of times"
82
+
83
+ option :output, short: '-o',
84
+ value: {
85
+ type: String,
86
+ usage: 'FILE'
87
+ },
88
+ desc: "Optional output file"
89
+
90
+ option :verbose, short: '-v', desc: "Increase verbose level" do
91
+ @verbose += 1
92
+ end
93
+
94
+ argument :file, type: String,
95
+ required: true,
96
+ usage: 'FILE',
97
+ desc: "Input file"
98
+
99
+ examples [
100
+ '-o path/to/output.txt path/to/input.txt',
101
+ '-v -c 2 -o path/to/output.txt path/to/input.txt',
102
+ ]
103
+
104
+ description 'Example command'
105
+
106
+ def initialize(**kwargs)
107
+ super(**kwargs)
108
+
109
+ @verbose = 0
110
+ end
111
+
112
+ def run(file)
113
+ puts "count=#{options[:count].inspect}"
114
+ puts "output=#{options[:output].inspect}"
115
+ puts "file=#{file.inspect}"
116
+ puts "verbose=#{@verbose.inspect}"
117
+ end
118
+
119
+ end
120
+ end
121
+ end
122
+
123
+ #### bin/my_cmd
124
+
125
+ #!/usr/bin/env ruby
126
+
127
+ $LOAD_PATH.unshift(File.expand_path('../../lib',__FILE__))
128
+ require 'foo/cli/my_cmd'
129
+
130
+ Foo::CLI::MyCmd.start
131
+
132
+ #### --help
133
+
134
+ Usage: my_cmd [OPTIONS] [-o OUTPUT] FILE
135
+
136
+ Options:
137
+ -c, --count INT Number of times (Default: 1)
138
+ -o, --output FILE Optional output file
139
+ -v, --verbose Increase verbose level
140
+ -h, --help Print help information
141
+
142
+ Arguments:
143
+ FILE Input file
144
+
145
+ Examples:
146
+ my_cmd -o path/to/output.txt path/to/input.txt
147
+ my_cmd -v -c 2 -o path/to/output.txt path/to/input.txt
148
+
149
+ Example command
150
+
151
+ ### Options
152
+
153
+ Define an option:
154
+
155
+ option :foo, desc: "Foo option"
156
+
157
+ With a custom short option:
158
+
159
+ option :foo, short: '-f',
160
+ desc: "Foo option"
161
+
162
+ With a custom long option:
163
+
164
+ option :foo, short: '--foo-opt',
165
+ desc: "Foo option"
166
+
167
+ With a custom usage string:
168
+
169
+ option :foo, value: {usage: 'FOO'},
170
+ desc: "Foo option"
171
+
172
+ With a custom block:
173
+
174
+ option :foo, desc: "Foo option" do |value|
175
+ @foo = Foo.new(value)
176
+ end
177
+
178
+ With a custom type:
179
+
180
+ option :foo, value: {type: Integer},
181
+ desc: "Foo option"
182
+
183
+ With a default value:
184
+
185
+ option :foo, value: {type: Integer, default: 1},
186
+ desc: "Foo option"
187
+
188
+ With a required value:
189
+
190
+ option :foo, value: {type: String, required: true},
191
+ desc: "Foo option"
192
+
193
+ With a custom option value Hash map:
194
+
195
+ option :flag, value: {
196
+ type: {
197
+ 'enabled' => :enabled,
198
+ 'yes' => :enabled,
199
+ 'disabled' => :disabled,
200
+ 'no' => :disabled
201
+ }
202
+ },
203
+ desc: "Flag option"
204
+
205
+ With a custom option value Array enum:
206
+
207
+ option :enum, value: {type: %w[yes no]},
208
+ desc: "Enum option"
209
+
210
+ With a custom option value Regexp:
211
+
212
+ option :date, value: {type: /(\d+)-(\d+)-(\d{2,4})/},
213
+ desc: "Regexp optin" do |date,d,m,y|
214
+ # ...
215
+ end
216
+
217
+ ### Arguments
218
+
219
+ Define an argument:
220
+
221
+ argument :bar, desc: "Bar argument"
222
+
223
+ With a custom usage string:
224
+
225
+ option :bar, usage: 'BAR',
226
+ desc: "Bar argument"
227
+
228
+ With a custom block:
229
+
230
+ argument :bar, desc: "Bar argument" do |bar|
231
+ # ...
232
+ end
233
+
234
+ With a custom type:
235
+
236
+ argument :bar, type: Integer,
237
+ desc: "Bar argument"
238
+
239
+ With a default value:
240
+
241
+ argument :bar, default: "bar.txt",
242
+ desc: "Bar argument"
243
+
244
+ An optional argument:
245
+
246
+ argument :bar, required: true,
247
+ desc: "Bar argument"
248
+
249
+ A repeating argument:
250
+
251
+ argument :bar, repeats: true,
252
+ desc: "Bar argument"
253
+
254
+ ## Requirements
255
+
256
+ * [ruby] >= 2.7.0
257
+
258
+ ## Install
259
+
260
+ $ gem install command_kit
261
+
262
+ ### Gemfile
263
+
264
+ gem 'command_kit', '~> 0.1'
265
+
266
+ ## Alternatives
267
+
268
+ * [dry-cli](https://dry-rb.org/gems/dry-cli/0.6/)
269
+ * [cmdparse](https://cmdparse.gettalong.org/)
270
+
271
+ ## Special Thanks
272
+
273
+ Special thanks to everyone who answered my questions and gave feedback on
274
+ Twitter.
275
+
276
+ ## Copyright
277
+
278
+ Copyright (c) 2021 Hal Brodigan
279
+
280
+ See {file:LICENSE.txt} for details.
281
+
282
+ [ruby]: https://www.ruby-lang.org/
283
+ [optparse]: https://rubydoc.info/stdlib/optparse/OptionParser
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler/setup'
7
+ rescue LoadError => e
8
+ abort e.message
9
+ end
10
+
11
+ require 'rake'
12
+ require 'rubygems/tasks'
13
+ Gem::Tasks.new
14
+
15
+ require 'rspec/core/rake_task'
16
+ RSpec::Core::RakeTask.new
17
+
18
+ task :test => :spec
19
+ task :default => :spec
20
+
21
+ require 'yard'
22
+ YARD::Rake::YardocTask.new
23
+ task :doc => :yard
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ require 'yaml'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gemspec = YAML.load_file('gemspec.yml')
7
+
8
+ gem.name = gemspec.fetch('name')
9
+ gem.version = gemspec.fetch('version') do
10
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
11
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
12
+
13
+ require 'command_kit/version'
14
+ CommandKit::VERSION
15
+ end
16
+
17
+ gem.summary = gemspec['summary']
18
+ gem.description = gemspec['description']
19
+ gem.licenses = Array(gemspec['license'])
20
+ gem.authors = Array(gemspec['authors'])
21
+ gem.email = gemspec['email']
22
+ gem.homepage = gemspec['homepage']
23
+
24
+ glob = lambda { |patterns| gem.files & Dir[*patterns] }
25
+
26
+ gem.files = `git ls-files`.split($/)
27
+ gem.files = glob[gemspec['files']] if gemspec['files']
28
+
29
+ gem.executables = gemspec.fetch('executables') do
30
+ glob['bin/*'].map { |path| File.basename(path) }
31
+ end
32
+ gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
33
+
34
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
35
+ gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
36
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
37
+
38
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
39
+ %w[ext lib].select { |dir| File.directory?(dir) }
40
+ })
41
+
42
+ gem.requirements = Array(gemspec['requirements'])
43
+ gem.required_ruby_version = gemspec['required_ruby_version']
44
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
45
+ gem.post_install_message = gemspec['post_install_message']
46
+
47
+ split = lambda { |string| string.split(/,\s*/) }
48
+
49
+ if gemspec['dependencies']
50
+ gemspec['dependencies'].each do |name,versions|
51
+ gem.add_dependency(name,split[versions])
52
+ end
53
+ end
54
+
55
+ if gemspec['development_dependencies']
56
+ gemspec['development_dependencies'].each do |name,versions|
57
+ gem.add_development_dependency(name,split[versions])
58
+ end
59
+ end
60
+ end