command_kit 0.1.0.rc1 → 0.2.2

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