command_kit 0.1.0.pre2 → 0.2.1

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