command_kit 0.1.0.pre2 → 0.2.1

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 +4 -4
  2. data/.github/workflows/ruby.yml +15 -0
  3. data/.rubocop.yml +141 -0
  4. data/ChangeLog.md +98 -2
  5. data/Gemfile +3 -0
  6. data/README.md +189 -117
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/command.rb +1 -1
  10. data/gemspec.yml +10 -2
  11. data/lib/command_kit/arguments/argument.rb +2 -0
  12. data/lib/command_kit/arguments/argument_value.rb +2 -0
  13. data/lib/command_kit/arguments.rb +23 -4
  14. data/lib/command_kit/colors.rb +253 -45
  15. data/lib/command_kit/command.rb +6 -1
  16. data/lib/command_kit/command_name.rb +9 -0
  17. data/lib/command_kit/commands/auto_load.rb +24 -1
  18. data/lib/command_kit/commands/auto_require.rb +16 -0
  19. data/lib/command_kit/commands/command.rb +3 -0
  20. data/lib/command_kit/commands/help.rb +5 -2
  21. data/lib/command_kit/commands/parent_command.rb +7 -0
  22. data/lib/command_kit/commands/subcommand.rb +13 -1
  23. data/lib/command_kit/commands.rb +54 -9
  24. data/lib/command_kit/description.rb +12 -1
  25. data/lib/command_kit/env/home.rb +9 -0
  26. data/lib/command_kit/env/path.rb +16 -1
  27. data/lib/command_kit/env.rb +4 -0
  28. data/lib/command_kit/examples.rb +12 -1
  29. data/lib/command_kit/exception_handler.rb +4 -0
  30. data/lib/command_kit/help/man.rb +26 -30
  31. data/lib/command_kit/help.rb +7 -1
  32. data/lib/command_kit/inflector.rb +49 -17
  33. data/lib/command_kit/interactive.rb +248 -0
  34. data/lib/command_kit/main.rb +18 -9
  35. data/lib/command_kit/man.rb +44 -0
  36. data/lib/command_kit/open_app.rb +69 -0
  37. data/lib/command_kit/options/option.rb +3 -6
  38. data/lib/command_kit/options/option_value.rb +5 -2
  39. data/lib/command_kit/options/parser.rb +46 -19
  40. data/lib/command_kit/options/quiet.rb +3 -0
  41. data/lib/command_kit/options/verbose.rb +5 -0
  42. data/lib/command_kit/options/version.rb +6 -0
  43. data/lib/command_kit/options.rb +32 -7
  44. data/lib/command_kit/os/linux.rb +157 -0
  45. data/lib/command_kit/os.rb +165 -11
  46. data/lib/command_kit/package_manager.rb +200 -0
  47. data/lib/command_kit/pager.rb +80 -11
  48. data/lib/command_kit/printing/indent.rb +27 -4
  49. data/lib/command_kit/printing.rb +35 -1
  50. data/lib/command_kit/program_name.rb +7 -0
  51. data/lib/command_kit/stdio.rb +24 -0
  52. data/lib/command_kit/sudo.rb +40 -0
  53. data/lib/command_kit/terminal.rb +159 -0
  54. data/lib/command_kit/usage.rb +14 -0
  55. data/lib/command_kit/version.rb +1 -1
  56. data/lib/command_kit/xdg.rb +13 -0
  57. data/lib/command_kit.rb +1 -0
  58. data/spec/arguments/argument_spec.rb +2 -2
  59. data/spec/arguments_spec.rb +53 -27
  60. data/spec/colors_spec.rb +277 -13
  61. data/spec/command_name_spec.rb +1 -1
  62. data/spec/command_spec.rb +79 -5
  63. data/spec/commands/auto_load/subcommand_spec.rb +1 -1
  64. data/spec/commands/auto_load_spec.rb +34 -3
  65. data/spec/commands/auto_require_spec.rb +2 -2
  66. data/spec/commands/help_spec.rb +1 -1
  67. data/spec/commands/parent_command_spec.rb +1 -1
  68. data/spec/commands/subcommand_spec.rb +1 -1
  69. data/spec/commands_spec.rb +103 -29
  70. data/spec/description_spec.rb +1 -25
  71. data/spec/env/home_spec.rb +1 -1
  72. data/spec/env/path_spec.rb +7 -1
  73. data/spec/examples_spec.rb +1 -25
  74. data/spec/exception_handler_spec.rb +1 -1
  75. data/spec/help/man_spec.rb +45 -58
  76. data/spec/help_spec.rb +0 -25
  77. data/spec/inflector_spec.rb +71 -9
  78. data/spec/interactive_spec.rb +415 -0
  79. data/spec/main_spec.rb +7 -7
  80. data/spec/man_spec.rb +46 -0
  81. data/spec/open_app_spec.rb +85 -0
  82. data/spec/options/option_spec.rb +5 -5
  83. data/spec/options/option_value_spec.rb +56 -1
  84. data/spec/options_spec.rb +283 -1
  85. data/spec/os/linux_spec.rb +164 -0
  86. data/spec/os_spec.rb +201 -14
  87. data/spec/package_manager_spec.rb +806 -0
  88. data/spec/pager_spec.rb +76 -11
  89. data/spec/printing/indent_spec.rb +8 -6
  90. data/spec/printing_spec.rb +33 -3
  91. data/spec/program_name_spec.rb +1 -1
  92. data/spec/spec_helper.rb +0 -3
  93. data/spec/sudo_spec.rb +51 -0
  94. data/spec/{console_spec.rb → terminal_spec.rb} +65 -35
  95. data/spec/usage_spec.rb +2 -2
  96. data/spec/xdg_spec.rb +1 -1
  97. metadata +26 -8
  98. data/lib/command_kit/console.rb +0 -141
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: baf0cab5c4519bef85a5ac27597c1c67b28a77cbe86d6c34b72687a91e1ef906
4
- data.tar.gz: 1b816bb979dcff48caf5c09ac7f4dcd9006871c94b9fd3bee5a7c2ce1763815a
3
+ metadata.gz: a218265a42b8b11c95888d07a3dcc3817d534adf830f8e7b1f1790bae73f05d6
4
+ data.tar.gz: c11cf5b2179d0701d5164b5ffcc07f2040cf8c813ea68caafdd7602e8603b754
5
5
  SHA512:
6
- metadata.gz: 284c9a6ff6bb1584add29558658054fc0814cfc0b1a1609b16a577154bc85e7011d9989abc75418bbd631f00a5f6c444dd104b3d938be347ad32ddf08e84ec83
7
- data.tar.gz: f0df98d7654058aa5be0aa884703ef540be3a1b7e2be52cd4a975913731ee4572d30cfc1a7b5213b9a165e7a18f95f4bbd3d9eb1a217362370a6c3b9e6c7ea93
6
+ metadata.gz: 7bd20d0e6b33b1d84933ea246c51b552c8652e3f59c8ec3fe32bf1944148377799b20b8177f0025114689ff5f73a88d08fe14171bdbd48189ab4123ba9129113
7
+ data.tar.gz: 617efce051bc51bbb88ada26a83ded0e158b8e0af28ab2bad8af1478aac05a156833aa3bfd95b59e7b07daf1075c08e60f7f6165cc28b76908cb13eb694c4f4b
@@ -3,6 +3,7 @@ name: CI
3
3
  on: [ push, pull_request ]
4
4
 
5
5
  jobs:
6
+ # run tests
6
7
  tests:
7
8
  runs-on: ubuntu-latest
8
9
  strategy:
@@ -27,3 +28,17 @@ jobs:
27
28
  run: bundle install --jobs 4 --retry 3
28
29
  - name: Run tests
29
30
  run: bundle exec rake test
31
+
32
+ # rubocop linting
33
+ rubocop:
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - uses: actions/checkout@v2
37
+ - name: Set up Ruby
38
+ uses: ruby/setup-ruby@v1
39
+ with:
40
+ ruby-version: 2.7
41
+ - name: Install dependencies
42
+ run: bundle install --jobs 4 --retry 3
43
+ - name: Run rubocop
44
+ run: bundle exec rubocop --parallel
data/.rubocop.yml ADDED
@@ -0,0 +1,141 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ SuggestExtensions: false
4
+ TargetRubyVersion: 2.7
5
+
6
+ #
7
+ # our rules
8
+ #
9
+
10
+ Layout/FirstArrayElementIndentation: { EnforcedStyle: consistent }
11
+ Layout/LineLength: { Enabled: false }
12
+ Layout/SpaceAroundEqualsInParameterDefault: { EnforcedStyle: no_space }
13
+ Lint/ConstantDefinitionInBlock: { Exclude: ['spec/**/*'] }
14
+ Metrics: { Enabled: false }
15
+ Style/SymbolArray: { EnforcedStyle: brackets }
16
+ Style/IfInsideElse: { Enabled: false } # Offense count: 1
17
+ Style/PercentLiteralDelimiters:
18
+ Enabled: true
19
+ PreferredDelimiters:
20
+ default: '{}'
21
+ '%i': '[]'
22
+ '%I': '[]'
23
+ '%w': '[]'
24
+ '%W': '[]'
25
+
26
+ #
27
+ # rules that are in flux
28
+ #
29
+
30
+ # consider enabling these and autocorrecting?
31
+ # Layout/SpaceAfterComma
32
+ # Layout/SpaceAroundKeyword
33
+ # Layout/SpaceBeforeComma
34
+ # Layout/SpaceInsideHashLiteralBraces
35
+ # Layout/SpaceInsideParens
36
+ # Layout/TrailingWhitespace
37
+ # Lint/UnreachableLoop
38
+ # Lint/UnusedBlockArgument
39
+ # Style/ClassCheck
40
+ # Style/Documentation
41
+ # Style/ExpandPathArguments
42
+ # Style/GlobalStdStream
43
+ # Style/HashSyntax
44
+ # Style/KeywordParametersOrder
45
+ # Style/MethodCallWithoutArgsParentheses
46
+ # Style/MutableConstant
47
+ # Style/QuotedSymbols: { EnforcedStyle: double_quotes }
48
+ # Style/RedundantReturn
49
+ # Style/SafeNavigation
50
+ # Style/SpecialGlobalVars
51
+ # Style/StringLiterals: { EnforcedStyle: double_quotes }
52
+ # Style/WordArray
53
+
54
+ # these have been fixed
55
+ # Gemspec/DuplicatedAssignment: { Enabled: false } # Offense count: 1
56
+ # Layout/ElseAlignment: { Enabled: false } # Offense count: 1
57
+ # Layout/EndAlignment: { Enabled: false } # Offense count: 1
58
+ # Lint/DuplicateMethods: { Enabled: false } # Offense count: 1
59
+ # Lint/UselessAssignment: { Enabled: false } # Offense count: 1
60
+ # Style/Encoding: { Enabled: false } # Offense count: 2
61
+ # Style/RedundantBegin: { Enabled: false } # Offense count: 2
62
+ # Style/RedundantInterpolation: { Enabled: false } # Offense count: 1
63
+ # Style/TrailingCommaInArrayLiteral: { Enabled: false } # Offense count: 1
64
+
65
+ #
66
+ # This list was generated with:
67
+ # bundle exec rubocop --auto-gen-config --exclude-limit 1
68
+ #
69
+
70
+ # > 10 violations
71
+ Layout/AssignmentIndentation: { Enabled: false } # Offense count: 11
72
+ Layout/EmptyLinesAroundClassBody: { Enabled: false } # Offense count: 76
73
+ Layout/HashAlignment: { Enabled: false } # Offense count: 28
74
+ Layout/SpaceAfterComma: { Enabled: false } # Offense count: 141
75
+ Layout/SpaceInsideHashLiteralBraces: { Enabled: false } # Offense count: 57
76
+ Layout/TrailingWhitespace: { Enabled: false } # Offense count: 50
77
+ Naming/RescuedExceptionsVariableName: { Enabled: false } # Offense count: 11
78
+ Style/BlockDelimiters: { Enabled: false } # Offense count: 17
79
+ Style/ClassCheck: { Enabled: false } # Offense count: 10
80
+ Style/ClassEqualityComparison: { Enabled: false } # Offense count: 16
81
+ Style/FrozenStringLiteralComment: { Enabled: false } # Offense count: 77
82
+ Style/GlobalStdStream: { Enabled: false } # Offense count: 13
83
+ Style/GuardClause: { Enabled: false } # Offense count: 10
84
+ Style/IfUnlessModifier: { Enabled: false } # Offense count: 13
85
+ Style/MethodCallWithoutArgsParentheses: { Enabled: false } # Offense count: 10
86
+ Style/SpecialGlobalVars: { Enabled: false } # Offense count: 28
87
+ Style/StringLiterals: { Enabled: false } # Offense count: 774
88
+ Lint/ElseLayout: { Enabled: false } # Offense count: 22
89
+
90
+ # < 10 violations
91
+ Layout/EmptyLinesAroundModuleBody: { Enabled: false } # Offense count: 5
92
+ Layout/ExtraSpacing: { Enabled: false } # Offense count: 6
93
+ Layout/FirstHashElementIndentation: { Enabled: false } # Offense count: 4
94
+ Layout/ParameterAlignment: { Enabled: false } # Offense count: 9
95
+ Layout/SpaceAroundKeyword: { Enabled: false } # Offense count: 7
96
+ Layout/SpaceBeforeComma: { Enabled: false } # Offense count: 4
97
+ Layout/SpaceInsideParens: { Enabled: false } # Offense count: 4
98
+ Lint/EmptyClass: { Enabled: false } # Offense count: 3
99
+ Lint/SuppressedException: { Enabled: false } # Offense count: 4
100
+ Lint/UnusedMethodArgument: { Enabled: false } # Offense count: 5
101
+ Style/AccessorGrouping: { Enabled: false } # Offense count: 7
102
+ Style/Documentation: { Enabled: false } # Offense count: 3
103
+ Style/ExpandPathArguments: { Enabled: false } # Offense count: 8
104
+ Style/HashSyntax: { Exclude: ['Rakefile'] } # Offense count: 3
105
+ Style/KeywordParametersOrder: { Enabled: false } # Offense count: 8
106
+ Style/Lambda: { Enabled: false } # Offense count: 3
107
+ Style/MutableConstant: { Enabled: false } # Offense count: 4
108
+ Style/RaiseArgs: { Enabled: false } # Offense count: 4
109
+ Style/RedundantReturn: { Enabled: false } # Offense count: 7
110
+ Style/SafeNavigation: { Enabled: false } # Offense count: 5
111
+ Style/StringConcatenation: { Enabled: false } # Offense count: 8
112
+ Style/WordArray: { Enabled: false } # Offense count: 4
113
+
114
+ # 1 or 2 violations
115
+ Layout/ArgumentAlignment: { Enabled: false } # Offense count: 1
116
+ Layout/BlockAlignment: { Enabled: false } # Offense count: 1
117
+ Layout/IndentationWidth: { Enabled: false } # Offense count: 2
118
+ Layout/SpaceAroundOperators: { Enabled: false } # Offense count: 1
119
+ Layout/SpaceBeforeBlockBraces: { Enabled: false } # Offense count: 1
120
+ Lint/MissingSuper: { Enabled: false } # Offense count: 2
121
+ Lint/RescueException: { Enabled: false } # Offense count: 1
122
+ Lint/UnreachableLoop: { Enabled: false } # Offense count: 1
123
+ Lint/UnusedBlockArgument: { Enabled: false } # Offense count: 1
124
+ Naming/MethodParameterName: { Enabled: false } # Offense count: 1
125
+ Style/EmptyMethod: { Enabled: false } # Offense count: 2
126
+ Style/HashConversion: { Enabled: false } # Offense count: 1
127
+ Style/MultilineMemoization: { Enabled: false } # Offense count: 1
128
+ Style/NumericPredicate: { Enabled: false } # Offense count: 1
129
+ Style/OptionalArguments: { Enabled: false } # Offense count: 1
130
+ Style/ParenthesesAroundCondition: { Enabled: false } # Offense count: 1
131
+ Style/PreferredHashMethods: { Enabled: false } # Offense count: 1
132
+ Style/QuotedSymbols: { Enabled: false } # Offense count: 1
133
+ Style/RedundantException: { Enabled: false } # Offense count: 1
134
+ Style/RedundantRegexpEscape: { Enabled: false } # Offense count: 1
135
+ Style/RegexpLiteral: { Enabled: false } # Offense count: 1
136
+ Style/RescueStandardError: { Enabled: false } # Offense count: 1
137
+ Style/SoleNestedConditional: { Enabled: false } # Offense count: 1
138
+ Style/TrailingCommaInHashLiteral: { Enabled: false } # Offense count: 2
139
+
140
+ # rubocop cannot tell that rubygems_mfa_required is enabled in gemspec.yml
141
+ Gemspec/RequireMFA: { Enabled: false }
data/ChangeLog.md CHANGED
@@ -1,13 +1,105 @@
1
- ### 0.1.0 / 2021-05-XX
1
+ ### 0.2.1 / 2021-11-16
2
+
3
+ * Ensure that all error messages end with a period.
4
+ * Documentation fixes.
5
+ * Opt-in to [rubygem.org MFA requirement](https://guides.rubygems.org/mfa-requirement-opt-in/).
6
+
7
+ #### CommandKit::Printing
8
+
9
+ * Auto-detect whether {CommandKit::CommandName#command_name #command_name} is
10
+ available, and if so, prepend the command name to all error messages.
11
+
12
+ #### CommandKit::Help::Man
13
+
14
+ * Expand the path given to
15
+ {CommandKit::Help::Man::ClassMethods#man_dir man_dir}.
16
+ * If {CommandKit::Help::Man::ClassMethods#man_dir man_dir} is not set, fallback
17
+ to regular `--help` output.
18
+
19
+ #### CommandKit::Arguments
20
+
21
+ * Include {CommandKit::Usage} and {CommandKit::Printing} into
22
+ {CommandKit::Arguments}.
23
+
24
+ #### CommandKit::Options
25
+
26
+ * Include {CommandKit::Arguments} into {CommandKit::Options}.
27
+ * Ensure that {CommandKit::Options::Parser#main} runs before
28
+ {CommandKit::Arguments#main}.
29
+ * Ensure that {CommandKit::Options#help} also calls
30
+ {CommandKit::Arguments#help_arguments}.
31
+ * Always prepopulate {CommandKit::Options#options #options} with option's
32
+ default values.
33
+ * Note: if an option has a default value but the option's value is not
34
+ required (ex: `value: {required: false, default: "foo"}`), and the option's
35
+ flag is given but no value is given (ex: `--option-flag --some-other-flag`),
36
+ the option's value in {CommandKit::Options#options #options} will be `nil`
37
+ _not_ the option's default value (`"foo"`). This helps indicate that the
38
+ option's flag was given but no value was given with it.
39
+
40
+ #### CommandKit::Options::OptionValue
41
+
42
+ * When a `Class` is passed to {CommandKit::Options::OptionValue.default_usage},
43
+ demodularize the class name before converting it to underscored/uppercase.
44
+
45
+ #### CommandKit::Command
46
+
47
+ * Fixed the inclusion order of {CommandKit::Options} and
48
+ {CommandKit::Arguments}.
49
+
50
+ #### CommandKit::Commands
51
+
52
+ * Define the `COMMAND` and `ARGS` arguments.
53
+ * Correctly duplicate the {CommandKit::Env#env env} (which can be either `ENV`
54
+ or a `Hash`) to work on ruby-3.1.0-preview1.
55
+ * Print command aliases that were set explicitly
56
+ (ex: `command_aliases['rm'] = 'remove'`) in {CommandKit::Commands#help}.
57
+ * Print help and exit with status `1` if no command is given. This matches the
58
+ behavior of the `git` command.
59
+
60
+ #### CommandKit::Commands::AutoLoad
61
+
62
+ * Ensure that any explicit command aliases are added to the command's
63
+ {CommandKit::Commands::ClassMethods#command_aliases command_aliases}.
64
+
65
+ ### 0.2.0 / 2021-08-31
66
+
67
+ * Added {CommandKit::Colors::ANSI#on_black}.
68
+ * Added {CommandKit::Colors::ANSI#on_red}.
69
+ * Added {CommandKit::Colors::ANSI#on_green}.
70
+ * Added {CommandKit::Colors::ANSI#on_yellow}.
71
+ * Added {CommandKit::Colors::ANSI#on_blue}.
72
+ * Added {CommandKit::Colors::ANSI#on_magenta}.
73
+ * Added {CommandKit::Colors::ANSI#on_cyan}.
74
+ * Added {CommandKit::Colors::ANSI#on_white}.
75
+ * Added {CommandKit::Man}.
76
+ * Added {CommandKit::OS#bsd?}.
77
+ * Added {CommandKit::OS#freebsd?}.
78
+ * Added {CommandKit::OS#netbsd?}.
79
+ * Added {CommandKit::OS#openbsd?}.
80
+ * Added {CommandKit::OS#os}.
81
+ * Added {CommandKit::OS#unix?}.
82
+ * Added {CommandKit::OS::Linux}.
83
+ * Added {CommandKit::OpenApp}.
84
+ * Added {CommandKit::PackageManager}.
85
+ * Added {CommandKit::Pager#pipe_to_pager}.
86
+ * Added {CommandKit::Sudo}.
87
+ * Added {CommandKit::Terminal#tty?}.
88
+ * Refactor {CommandKit::Inflector.camelize} and
89
+ {CommandKit::Inflector.underscore} to use StringScanner.
90
+ * Allow {CommandKit::OS#initialize} to accept an `os:` keyword to override the
91
+ detected OS.
92
+
93
+ ### 0.1.0 / 2021-07-16
2
94
 
3
95
  * Initial release:
4
96
  * {CommandKit::Arguments}
5
97
  * {CommandKit::Colors}
98
+ * {CommandKit::Command}
6
99
  * {CommandKit::CommandName}
7
100
  * {CommandKit::Commands}
8
101
  * {CommandKit::Commands::AutoLoad}
9
102
  * {CommandKit::Commands::AutoRequire}
10
- * {CommandKit::Console}
11
103
  * {CommandKit::Description}
12
104
  * {CommandKit::Env}
13
105
  * {CommandKit::Env::Home}
@@ -16,14 +108,18 @@
16
108
  * {CommandKit::ExceptionHandler}
17
109
  * {CommandKit::Help}
18
110
  * {CommandKit::Help::Man}
111
+ * {CommandKit::Interactive}
19
112
  * {CommandKit::Main}
20
113
  * {CommandKit::Options}
21
114
  * {CommandKit::Options::Quiet}
22
115
  * {CommandKit::Options::Verbose}
116
+ * {CommandKit::OS}
23
117
  * {CommandKit::Pager}
24
118
  * {CommandKit::Printing}
119
+ * {CommandKit::Printing::Indent}
25
120
  * {CommandKit::ProgramName}
26
121
  * {CommandKit::Stdio}
122
+ * {CommandKit::Terminal}
27
123
  * {CommandKit::Usage}
28
124
  * {CommandKit::XDG}
29
125
 
data/Gemfile CHANGED
@@ -6,9 +6,12 @@ group :development do
6
6
  gem 'rake'
7
7
  gem 'rubygems-tasks', '~> 0.2'
8
8
 
9
+ gem 'rubocop', '~> 1.18'
10
+
9
11
  gem 'rspec', '~> 3.0'
10
12
  gem 'simplecov', '~> 0.20', require: false
11
13
 
12
14
  gem 'kramdown'
13
15
  gem 'yard', '~> 0.9'
16
+ gem 'yard-spellcheck', require: false
14
17
  end
data/README.md CHANGED
@@ -1,60 +1,41 @@
1
1
  # command_kit
2
2
 
3
- * [Homepage](https://github.com/postmodern/command_kit#readme)
4
- * [Issues](https://github.com/postmodern/command_kit/issues)
3
+ [![Build Status](https://github.com/postmodern/command_kit.rb/workflows/CI/badge.svg?branch=main)](https://github.com/postmodern/command_kit.rb/actions)
4
+ [![Code Climate](https://codeclimate.com/github/postmodern/command_kit.rb.svg)](https://codeclimate.com/github/postmodern/command_kit.rb)
5
+ [![Gem Version](https://badge.fury.io/rb/command_kit.svg)](https://badge.fury.io/rb/command_kit)
6
+
7
+ * [Homepage](https://github.com/postmodern/command_kit.rb#readme)
8
+ * [Forum](https://github.com/postmodern/command_kit.rb/discussions) |
9
+ [Issues](https://github.com/postmodern/command_kit.rb/issues)
5
10
  * [Documentation](http://rubydoc.info/gems/command_kit/frames)
6
- * [Email](mailto:postmodern.mod3 at gmail.com)
7
11
 
8
12
  ## Description
9
13
 
10
- A Ruby toolkit for building clean, correct, and robust CLI commands as Ruby
11
- classes.
14
+ A Ruby toolkit for building clean, correct, and robust CLI commands as
15
+ plain-old Ruby classes.
12
16
 
13
17
  ## Features
14
18
 
15
- * Supports defining commands as Classes.
16
- * Supports defining options and arguments as attributes.
17
- * Supports extending commands via inheritance.
18
- * Supports subcommands (explicit or lazy-loaded) and command aliases.
19
- * Correctly handles Ctrl^C and SIGINT interrupts (aka exit 130).
20
- * Correctly handles broken pipes (aka `mycmd | head`).
21
- * Uses [OptionParser][optparse] for option parsing.
22
- * Provides ANSI coloring support.
23
- * Supports optionally displaying a man-page instead of `--help`
24
- (see {CommandKit::Help::Man}).
25
- * Supports XDG directories (`~/.config/`, `~/.local/share/`, `~/.cache/`).
26
- * Easy to test (ex: `MyCmd.main(arg1, arg2, options: {foo: foo}) # => 0`)
27
- * Modular design (everything is a module).
28
-
29
- ### API
30
-
31
- * [CommandKit::Arguments](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Arguments)
32
- * [CommandKit::Colors](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Colors)
33
- * [CommandKit::Command](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Command)
34
- * [CommandKit::CommandName](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/CommandName)
35
- * [CommandKit::Commands](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Commands)
36
- * [CommandKit::Commands::AutoLoad](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Commands/AutoLoad)
37
- * [CommandKit::Commands::AutoRequire](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Commands/AutoRequire)
38
- * [CommandKit::Console](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Console)
39
- * [CommandKit::Description](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Description)
40
- * [CommandKit::Env](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Env)
41
- * [CommandKit::Env::Home](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Env/Home)
42
- * [CommandKit::Env::Path](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Env/Path)
43
- * [CommandKit::Examples](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Examples)
44
- * [CommandKit::ExceptionHandler](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/ExceptionHandler)
45
- * [CommandKit::Help](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Help)
46
- * [CommandKit::Help::Man](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Help/Man)
47
- * [CommandKit::Main](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Main)
48
- * [CommandKit::Options](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Options)
49
- * [CommandKit::Options::Quiet](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Options/Quiet)
50
- * [CommandKit::Options::Verbose](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Options/Verbose)
51
- * [CommandKit::Pager](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Pager)
52
- * [CommandKit::Printing](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Printing)
53
- * [CommandKit::Printing::Indent](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Printing/Indent)
54
- * [CommandKit::ProgramName](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/ProgramName)
55
- * [CommandKit::Stdio](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Stdio)
56
- * [CommandKit::Usage](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Usage)
57
- * [CommandKit::XDG](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/XDG)
19
+ * **Simple** - Commands are plain-old ruby classes, with options and
20
+ arguments declared as attributes. All features are ruby modules that can be
21
+ included into command classes.
22
+ * **Correct** - CommandKit behaves like a standard UNIX command.
23
+ * Safely handles Ctrl^C / SIGINT interrupts and [exits with 130](https://tldp.org/LDP/abs/html/exitcodes.html).
24
+ * Safely handles broken pipes (aka `mycmd | head`).
25
+ * Respects common environment variables (ex: `TERM=dumb`).
26
+ * Uses [OptionParser][optparse] for POSIX option parsing.
27
+ * Disables ANSI color when output is redirected to a file.
28
+ * **Complete** - Provides many additional CLI features.
29
+ * OS detection.
30
+ * Terminal size detection.
31
+ * ANSI coloring support.
32
+ * Interactive input.
33
+ * Subcommands (explicit or lazy-loaded) and command aliases.
34
+ * Displaying man pages for `--help`/`help`.
35
+ * Using the pager (aka `less`).
36
+ * [XDG directories](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) (aka `~/.config/`, `~/.local/share/`, `~/.cache/`).
37
+ * **Testable** - Since commands are plain-old ruby classes, it's easy to
38
+ initialize them and call `#main` or `#run`.
58
39
 
59
40
  ## Anti-Features
60
41
 
@@ -62,103 +43,194 @@ classes.
62
43
  * Does not implement it's own option parser.
63
44
  * Not named after a comic-book Superhero.
64
45
 
46
+ ## Requirements
47
+
48
+ * [ruby] >= 2.7.0
49
+
50
+ ## Install
51
+
52
+ ```sh
53
+ $ gem install command_kit
54
+ ```
55
+
56
+ ### gemspec
57
+
58
+ ```ruby
59
+ gem.add_dependency 'command_kit', '~> 0.2'
60
+ ```
61
+
62
+ ### Gemfile
63
+
64
+ ```ruby
65
+ gem 'command_kit', '~> 0.2'
66
+ ```
67
+
65
68
  ## Examples
66
69
 
67
70
  ### lib/foo/cli/my_cmd.rb
68
71
 
69
- require 'command_kit'
70
-
71
- module Foo
72
- module CLI
73
- class MyCmd < CommandKit::Command
74
-
75
- usage '[OPTIONS] [-o OUTPUT] FILE'
76
-
77
- option :count, short: '-c',
78
- value: {
79
- type: Integer,
80
- default: 1
81
- },
82
- desc: "Number of times"
83
-
84
- option :output, short: '-o',
85
- value: {
86
- type: String,
87
- usage: 'FILE'
88
- },
89
- desc: "Optional output file"
90
-
91
- option :verbose, short: '-v', desc: "Increase verbose level" do
92
- @verbose += 1
93
- end
94
-
95
- argument :file, 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
72
+ ```ruby
73
+ require 'command_kit'
74
+
75
+ module Foo
76
+ module CLI
77
+ class MyCmd < CommandKit::Command
78
+
79
+ usage '[OPTIONS] [-o OUTPUT] FILE'
80
+
81
+ option :count, short: '-c',
82
+ value: {
83
+ type: Integer,
84
+ default: 1
85
+ },
86
+ desc: "Number of times"
87
+
88
+ option :output, short: '-o',
89
+ value: {
90
+ type: String,
91
+ usage: 'FILE'
92
+ },
93
+ desc: "Optional output file"
94
+
95
+ option :verbose, short: '-v', desc: "Increase verbose level" do
96
+ @verbose += 1
120
97
  end
98
+
99
+ argument :file, required: true,
100
+ usage: 'FILE',
101
+ desc: "Input file"
102
+
103
+ examples [
104
+ '-o path/to/output.txt path/to/input.txt',
105
+ '-v -c 2 -o path/to/output.txt path/to/input.txt',
106
+ ]
107
+
108
+ description 'Example command'
109
+
110
+ def initialize(**kwargs)
111
+ super(**kwargs)
112
+
113
+ @verbose = 0
114
+ end
115
+
116
+ def run(file)
117
+ puts "count=#{options[:count].inspect}"
118
+ puts "output=#{options[:output].inspect}"
119
+ puts "file=#{file.inspect}"
120
+ puts "verbose=#{@verbose.inspect}"
121
+ end
122
+
121
123
  end
124
+ end
125
+ end
126
+ ```
122
127
 
123
128
  ### bin/my_cmd
124
129
 
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
130
+ ```ruby
131
+ #!/usr/bin/env ruby
132
+
133
+ $LOAD_PATH.unshift(File.expand_path('../../lib',__FILE__))
134
+ require 'foo/cli/my_cmd'
135
+
136
+ Foo::CLI::MyCmd.start
137
+ ```
131
138
 
132
139
  ### --help
133
140
 
134
141
  Usage: my_cmd [OPTIONS] [-o OUTPUT] FILE
135
-
142
+
136
143
  Options:
137
144
  -c, --count INT Number of times (Default: 1)
138
145
  -o, --output FILE Optional output file
139
146
  -v, --verbose Increase verbose level
140
147
  -h, --help Print help information
141
-
148
+
142
149
  Arguments:
143
150
  FILE Input file
144
-
151
+
145
152
  Examples:
146
153
  my_cmd -o path/to/output.txt path/to/input.txt
147
154
  my_cmd -v -c 2 -o path/to/output.txt path/to/input.txt
148
-
149
- Example command
150
155
 
151
- ## Requirements
152
-
153
- * [ruby] >= 2.7.0
156
+ Example command
154
157
 
155
- ## Install
158
+ ## Testing
159
+
160
+ ### RSpec
161
+
162
+ ```ruby
163
+ require 'spec_helper'
164
+ require 'stringio'
165
+ require 'foo/cli/my_cmd'
166
+
167
+ describe Foo::CLI::MyCmd do
168
+ let(:stdin) { StringIO.new }
169
+ let(:stdout) { StringIO.new }
170
+ let(:stderr) { StringIO.new }
171
+ let(:env) { ENV }
172
+
173
+ subject do
174
+ described_class.new(
175
+ stdin: stdin,
176
+ stdout: stdout,
177
+ stderr: stderr,
178
+ env: env
179
+ )
180
+ end
181
+
182
+ # testing with raw options/arguments
183
+ describe "#main" do
184
+ context "when executed with no arguments" do
185
+ it "must exit with -1" do
186
+ expect(subject.main([])).to eq(-1)
187
+ end
188
+ end
156
189
 
157
- $ gem install command_kit
190
+ context "when executed with -o OUTPUT" do
191
+ let(:file) { ... }
192
+ let(:output) { ... }
158
193
 
159
- ### Gemfile
194
+ before { subject.main(["-o", output, file]) }
160
195
 
161
- gem 'command_kit', '~> 0.1'
196
+ it "must create the output file" do
197
+ ...
198
+ end
199
+ end
200
+ end
201
+ end
202
+ ```
203
+
204
+ ### Reference
205
+
206
+ * [CommandKit::Arguments](https://rubydoc.info/gems/command_kit/CommandKit/Arguments)
207
+ * [CommandKit::Colors](https://rubydoc.info/gems/command_kit/CommandKit/Colors)
208
+ * [CommandKit::Command](https://rubydoc.info/gems/command_kit/CommandKit/Command)
209
+ * [CommandKit::CommandName](https://rubydoc.info/gems/command_kit/CommandKit/CommandName)
210
+ * [CommandKit::Commands](https://rubydoc.info/gems/command_kit/CommandKit/Commands)
211
+ * [CommandKit::Commands::AutoLoad](https://rubydoc.info/gems/command_kit/CommandKit/Commands/AutoLoad)
212
+ * [CommandKit::Commands::AutoRequire](https://rubydoc.info/gems/command_kit/CommandKit/Commands/AutoRequire)
213
+ * [CommandKit::Description](https://rubydoc.info/gems/command_kit/CommandKit/Description)
214
+ * [CommandKit::Env](https://rubydoc.info/gems/command_kit/CommandKit/Env)
215
+ * [CommandKit::Env::Home](https://rubydoc.info/gems/command_kit/CommandKit/Env/Home)
216
+ * [CommandKit::Env::Path](https://rubydoc.info/gems/command_kit/CommandKit/Env/Path)
217
+ * [CommandKit::Examples](https://rubydoc.info/gems/command_kit/CommandKit/Examples)
218
+ * [CommandKit::ExceptionHandler](https://rubydoc.info/gems/command_kit/CommandKit/ExceptionHandler)
219
+ * [CommandKit::Help](https://rubydoc.info/gems/command_kit/CommandKit/Help)
220
+ * [CommandKit::Help::Man](https://rubydoc.info/gems/command_kit/CommandKit/Help/Man)
221
+ * [CommandKit::Interactive](https://rubydoc.info/gems/command_kit/CommandKit/Interactive)
222
+ * [CommandKit::Main](https://rubydoc.info/gems/command_kit/CommandKit/Main)
223
+ * [CommandKit::Options](https://rubydoc.info/gems/command_kit/CommandKit/Options)
224
+ * [CommandKit::Options::Quiet](https://rubydoc.info/gems/command_kit/CommandKit/Options/Quiet)
225
+ * [CommandKit::Options::Verbose](https://rubydoc.info/gems/command_kit/CommandKit/Options/Verbose)
226
+ * [CommandKit::Pager](https://rubydoc.info/gems/command_kit/CommandKit/Pager)
227
+ * [CommandKit::Printing](https://rubydoc.info/gems/command_kit/CommandKit/Printing)
228
+ * [CommandKit::Printing::Indent](https://rubydoc.info/gems/command_kit/CommandKit/Printing/Indent)
229
+ * [CommandKit::ProgramName](https://rubydoc.info/gems/command_kit/CommandKit/ProgramName)
230
+ * [CommandKit::Stdio](https://rubydoc.info/gems/command_kit/CommandKit/Stdio)
231
+ * [CommandKit::Terminal](https://rubydoc.info/gems/command_kit/CommandKit/Terminal)
232
+ * [CommandKit::Usage](https://rubydoc.info/gems/command_kit/CommandKit/Usage)
233
+ * [CommandKit::XDG](https://rubydoc.info/gems/command_kit/CommandKit/XDG)
162
234
 
163
235
  ## Alternatives
164
236