command_kit 0.1.0.rc1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +17 -3
  3. data/.rubocop.yml +141 -0
  4. data/ChangeLog.md +102 -1
  5. data/Gemfile +3 -0
  6. data/README.md +187 -116
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/command.rb +1 -1
  10. data/gemspec.yml +8 -1
  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 +25 -6
  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 +28 -31
  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 +71 -1
  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 +73 -4
  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 +17 -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 +54 -28
  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 +102 -30
  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 +55 -58
  76. data/spec/help_spec.rb +0 -25
  77. data/spec/inflector_spec.rb +71 -9
  78. data/spec/main_spec.rb +7 -7
  79. data/spec/man_spec.rb +46 -0
  80. data/spec/open_app_spec.rb +85 -0
  81. data/spec/options/option_spec.rb +5 -5
  82. data/spec/options/option_value_spec.rb +56 -1
  83. data/spec/options_spec.rb +283 -1
  84. data/spec/os/linux_spec.rb +164 -0
  85. data/spec/os_spec.rb +201 -14
  86. data/spec/package_manager_spec.rb +806 -0
  87. data/spec/pager_spec.rb +72 -7
  88. data/spec/printing/indent_spec.rb +8 -6
  89. data/spec/printing_spec.rb +33 -3
  90. data/spec/program_name_spec.rb +1 -1
  91. data/spec/spec_helper.rb +0 -3
  92. data/spec/sudo_spec.rb +51 -0
  93. data/spec/terminal_spec.rb +31 -1
  94. data/spec/usage_spec.rb +2 -2
  95. data/spec/xdg_spec.rb +1 -1
  96. metadata +21 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c8944ddc49470bbe03b23cdf0df4cc9e5f94010208e15b373430453556ff2ed
4
- data.tar.gz: bfebe1ddb5ae5a078e5910de55f626ae69dc8716f3d5152690e9002b68878bfe
3
+ metadata.gz: d04119403fa9585e0258d2b20bb7c307d124aafb70c88e03749395c7b4eedb97
4
+ data.tar.gz: 3ec992adf12bbc98313bb6ff8f3038091b50b31abbf8950765a71edd771378fb
5
5
  SHA512:
6
- metadata.gz: a8f00bba78443b945ea4c41efeaf4488eb6c65867ac637b4ae368d6a53724802395f0f9d5d464241fbe9d7f22fd1657e50161df4846fcdcce174f4abf206abb7
7
- data.tar.gz: 497da95cec49705d426da9dfa27aa5f7ca9e462375dba94375244e5180f9ac9f1735850022fdfd61876cb6661a5988c568eda518dac010859be69589c3780843
6
+ metadata.gz: faeeaa08921ab315a825d4b1ff69f6b254c53b6675dd4278478b77200d6d2afb688d110757685ed33d74b90a6b1e9394c12135c3ab5f08e2ca3ca5834ead248d
7
+ data.tar.gz: 6b4ba3da0434c9fbd93d92a4c9d91e8eb53f1185df15edb21283c09b2ddf9ec4dc9fcb3708bdf83a1158cef92c988ccb5133ec5664ac9dcb213bdcde45aacf3d
@@ -3,19 +3,19 @@ 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:
9
10
  fail-fast: false
10
11
  matrix:
11
12
  ruby:
12
- # - 2.4
13
- # - 2.5
14
- # - 2.6
15
13
  - 2.7
16
14
  - 3.0
17
15
  # TODO: uncomment when jruby supports ruby >= 2.7
18
16
  # - jruby
17
+ # TODO: uncomment when truffleruby supports splatting empty kwargs
18
+ # - truffleruby
19
19
  name: Ruby ${{ matrix.ruby }}
20
20
  steps:
21
21
  - uses: actions/checkout@v2
@@ -27,3 +27,17 @@ jobs:
27
27
  run: bundle install --jobs 4 --retry 3
28
28
  - name: Run tests
29
29
  run: bundle exec rake test
30
+
31
+ # rubocop linting
32
+ rubocop:
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v2
36
+ - name: Set up Ruby
37
+ uses: ruby/setup-ruby@v1
38
+ with:
39
+ ruby-version: 2.7
40
+ - name: Install dependencies
41
+ run: bundle install --jobs 4 --retry 3
42
+ - name: Run rubocop
43
+ 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,4 +1,104 @@
1
- ### 0.1.0 / 2021-07-XX
1
+ ### 0.2.2 / 2021-12-26
2
+
3
+ #### CommandKit::Help::Man
4
+
5
+ * Raise a `NotImplementedError` exception in {CommandKit::Help::Man#help_man
6
+ #help_man} if {CommandKit::Help::Man::ClassMethods#man_dir .man_dir} was not
7
+ set.
8
+
9
+ ### 0.2.1 / 2021-11-16
10
+
11
+ * Ensure that all error messages end with a period.
12
+ * Documentation fixes.
13
+ * Opt-in to [rubygem.org MFA requirement](https://guides.rubygems.org/mfa-requirement-opt-in/).
14
+
15
+ #### CommandKit::Printing
16
+
17
+ * Auto-detect whether {CommandKit::CommandName#command_name #command_name} is
18
+ available, and if so, prepend the command name to all error messages.
19
+
20
+ #### CommandKit::Help::Man
21
+
22
+ * Expand the path given to
23
+ {CommandKit::Help::Man::ClassMethods#man_dir man_dir}.
24
+ * If {CommandKit::Help::Man::ClassMethods#man_dir man_dir} is not set, fallback
25
+ to regular `--help` output.
26
+
27
+ #### CommandKit::Arguments
28
+
29
+ * Include {CommandKit::Usage} and {CommandKit::Printing} into
30
+ {CommandKit::Arguments}.
31
+
32
+ #### CommandKit::Options
33
+
34
+ * Include {CommandKit::Arguments} into {CommandKit::Options}.
35
+ * Ensure that {CommandKit::Options::Parser#main} runs before
36
+ {CommandKit::Arguments#main}.
37
+ * Ensure that {CommandKit::Options#help} also calls
38
+ {CommandKit::Arguments#help_arguments}.
39
+ * Always prepopulate {CommandKit::Options#options #options} with option's
40
+ default values.
41
+ * Note: if an option has a default value but the option's value is not
42
+ required (ex: `value: {required: false, default: "foo"}`), and the option's
43
+ flag is given but no value is given (ex: `--option-flag --some-other-flag`),
44
+ the option's value in {CommandKit::Options#options #options} will be `nil`
45
+ _not_ the option's default value (`"foo"`). This helps indicate that the
46
+ option's flag was given but no value was given with it.
47
+
48
+ #### CommandKit::Options::OptionValue
49
+
50
+ * When a `Class` is passed to {CommandKit::Options::OptionValue.default_usage},
51
+ demodularize the class name before converting it to underscored/uppercase.
52
+
53
+ #### CommandKit::Command
54
+
55
+ * Fixed the inclusion order of {CommandKit::Options} and
56
+ {CommandKit::Arguments}.
57
+
58
+ #### CommandKit::Commands
59
+
60
+ * Define the `COMMAND` and `ARGS` arguments.
61
+ * Correctly duplicate the {CommandKit::Env#env env} (which can be either `ENV`
62
+ or a `Hash`) to work on ruby-3.1.0-preview1.
63
+ * Print command aliases that were set explicitly
64
+ (ex: `command_aliases['rm'] = 'remove'`) in {CommandKit::Commands#help}.
65
+ * Print help and exit with status `1` if no command is given. This matches the
66
+ behavior of the `git` command.
67
+
68
+ #### CommandKit::Commands::AutoLoad
69
+
70
+ * Ensure that any explicit command aliases are added to the command's
71
+ {CommandKit::Commands::ClassMethods#command_aliases command_aliases}.
72
+
73
+ ### 0.2.0 / 2021-08-31
74
+
75
+ * Added {CommandKit::Colors::ANSI#on_black}.
76
+ * Added {CommandKit::Colors::ANSI#on_red}.
77
+ * Added {CommandKit::Colors::ANSI#on_green}.
78
+ * Added {CommandKit::Colors::ANSI#on_yellow}.
79
+ * Added {CommandKit::Colors::ANSI#on_blue}.
80
+ * Added {CommandKit::Colors::ANSI#on_magenta}.
81
+ * Added {CommandKit::Colors::ANSI#on_cyan}.
82
+ * Added {CommandKit::Colors::ANSI#on_white}.
83
+ * Added {CommandKit::Man}.
84
+ * Added {CommandKit::OS#bsd?}.
85
+ * Added {CommandKit::OS#freebsd?}.
86
+ * Added {CommandKit::OS#netbsd?}.
87
+ * Added {CommandKit::OS#openbsd?}.
88
+ * Added {CommandKit::OS#os}.
89
+ * Added {CommandKit::OS#unix?}.
90
+ * Added {CommandKit::OS::Linux}.
91
+ * Added {CommandKit::OpenApp}.
92
+ * Added {CommandKit::PackageManager}.
93
+ * Added {CommandKit::Pager#pipe_to_pager}.
94
+ * Added {CommandKit::Sudo}.
95
+ * Added {CommandKit::Terminal#tty?}.
96
+ * Refactor {CommandKit::Inflector.camelize} and
97
+ {CommandKit::Inflector.underscore} to use StringScanner.
98
+ * Allow {CommandKit::OS#initialize} to accept an `os:` keyword to override the
99
+ detected OS.
100
+
101
+ ### 0.1.0 / 2021-07-16
2
102
 
3
103
  * Initial release:
4
104
  * {CommandKit::Arguments}
@@ -21,6 +121,7 @@
21
121
  * {CommandKit::Options}
22
122
  * {CommandKit::Options::Quiet}
23
123
  * {CommandKit::Options::Verbose}
124
+ * {CommandKit::OS}
24
125
  * {CommandKit::Pager}
25
126
  * {CommandKit::Printing}
26
127
  * {CommandKit::Printing::Indent}
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,9 +1,13 @@
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
 
@@ -12,50 +16,26 @@ 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
- * Correctly handles when stdout or stdin is redirected to a file.
22
- * Uses [OptionParser][optparse] for POSIX option parsing.
23
- * Supports optional ANSI coloring.
24
- * Supports optionally displaying a man-page instead of `--help`
25
- (see {CommandKit::Help::Man}).
26
- * Supports XDG directories (`~/.config/`, `~/.local/share/`, `~/.cache/`).
27
- * Easy to test (ex: `MyCmd.main(arg1, arg2, options: {foo: foo}) # => 0`)
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::Description](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Description)
39
- * [CommandKit::Env](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Env)
40
- * [CommandKit::Env::Home](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Env/Home)
41
- * [CommandKit::Env::Path](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Env/Path)
42
- * [CommandKit::Examples](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Examples)
43
- * [CommandKit::ExceptionHandler](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/ExceptionHandler)
44
- * [CommandKit::Help](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Help)
45
- * [CommandKit::Help::Man](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Help/Man)
46
- * [CommandKit::Interactive](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Interactive)
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::Terminal](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Terminal)
57
- * [CommandKit::Usage](https://rubydoc.info/github/postmodern/command_kit/main/CommandKit/Usage)
58
- * [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`.
59
39
 
60
40
  ## Anti-Features
61
41
 
@@ -63,103 +43,194 @@ plain-old Ruby classes.
63
43
  * Does not implement it's own option parser.
64
44
  * Not named after a comic-book Superhero.
65
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
+
66
68
  ## Examples
67
69
 
68
70
  ### lib/foo/cli/my_cmd.rb
69
71
 
70
- require 'command_kit'
71
-
72
- module Foo
73
- module CLI
74
- class MyCmd < CommandKit::Command
75
-
76
- usage '[OPTIONS] [-o OUTPUT] FILE'
77
-
78
- option :count, short: '-c',
79
- value: {
80
- type: Integer,
81
- default: 1
82
- },
83
- desc: "Number of times"
84
-
85
- option :output, short: '-o',
86
- value: {
87
- type: String,
88
- usage: 'FILE'
89
- },
90
- desc: "Optional output file"
91
-
92
- option :verbose, short: '-v', desc: "Increase verbose level" do
93
- @verbose += 1
94
- end
95
-
96
- argument :file, required: true,
97
- usage: 'FILE',
98
- desc: "Input file"
99
-
100
- examples [
101
- '-o path/to/output.txt path/to/input.txt',
102
- '-v -c 2 -o path/to/output.txt path/to/input.txt',
103
- ]
104
-
105
- description 'Example command'
106
-
107
- def initialize(**kwargs)
108
- super(**kwargs)
109
-
110
- @verbose = 0
111
- end
112
-
113
- def run(file)
114
- puts "count=#{options[:count].inspect}"
115
- puts "output=#{options[:output].inspect}"
116
- puts "file=#{file.inspect}"
117
- puts "verbose=#{@verbose.inspect}"
118
- end
119
-
120
- 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
121
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
+
122
123
  end
124
+ end
125
+ end
126
+ ```
123
127
 
124
128
  ### bin/my_cmd
125
129
 
126
- #!/usr/bin/env ruby
127
-
128
- $LOAD_PATH.unshift(File.expand_path('../../lib',__FILE__))
129
- require 'foo/cli/my_cmd'
130
-
131
- 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
+ ```
132
138
 
133
139
  ### --help
134
140
 
135
141
  Usage: my_cmd [OPTIONS] [-o OUTPUT] FILE
136
-
142
+
137
143
  Options:
138
144
  -c, --count INT Number of times (Default: 1)
139
145
  -o, --output FILE Optional output file
140
146
  -v, --verbose Increase verbose level
141
147
  -h, --help Print help information
142
-
148
+
143
149
  Arguments:
144
150
  FILE Input file
145
-
151
+
146
152
  Examples:
147
153
  my_cmd -o path/to/output.txt path/to/input.txt
148
154
  my_cmd -v -c 2 -o path/to/output.txt path/to/input.txt
149
-
150
- Example command
151
155
 
152
- ## Requirements
153
-
154
- * [ruby] >= 2.7.0
156
+ Example command
155
157
 
156
- ## 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
157
189
 
158
- $ gem install command_kit
190
+ context "when executed with -o OUTPUT" do
191
+ let(:file) { ... }
192
+ let(:output) { ... }
159
193
 
160
- ### Gemfile
194
+ before { subject.main(["-o", output, file]) }
161
195
 
162
- 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)
163
234
 
164
235
  ## Alternatives
165
236