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
@@ -0,0 +1,159 @@
1
+ require 'command_kit/stdio'
2
+ require 'command_kit/env'
3
+
4
+ begin
5
+ require 'io/console'
6
+ rescue LoadError
7
+ end
8
+
9
+ module CommandKit
10
+ #
11
+ # Provides direct access to the terminal.
12
+ #
13
+ # ## Environment Variables
14
+ #
15
+ # * `LINES` - The explicit number of lines or rows the console should have.
16
+ # * `COLUMNS` - The explicit number of columns the console should have.
17
+ #
18
+ # @see https://rubydoc.info/gems/io-console/IO
19
+ #
20
+ module Terminal
21
+ include Stdio
22
+ include Env
23
+
24
+ # The default terminal height to fallback to.
25
+ DEFAULT_TERMINAL_HEIGHT = 25
26
+
27
+ # The default terminal width to fallback to.
28
+ DEFAULT_TERMINAL_WIDTH = 80
29
+
30
+ #
31
+ # Initializes any terminal settings.
32
+ #
33
+ # @param [Hash{Symbol => Object}] kwargs
34
+ # Additional keyword arguments.
35
+ #
36
+ # @note
37
+ # If the `$LINES` env variable is set, and is non-zero, it will be
38
+ # returned by {#terminal_height}.
39
+ #
40
+ # @note
41
+ # If the `$COLUMNS` env variable is set, and is non-zero, it will be
42
+ # returned by {#terminal_width}.
43
+ #
44
+ # @api public
45
+ #
46
+ def initialize(**kwargs)
47
+ super(**kwargs)
48
+
49
+ @terminal_height = if (lines = env['LINES'])
50
+ lines.to_i
51
+ else
52
+ DEFAULT_TERMINAL_HEIGHT
53
+ end
54
+
55
+ @terminal_width = if (columns = env['COLUMNS'])
56
+ columns.to_i
57
+ else
58
+ DEFAULT_TERMINAL_WIDTH
59
+ end
60
+ end
61
+
62
+ #
63
+ # Determines if program is running in a terminal.
64
+ #
65
+ # @return [Boolean]
66
+ # Specifies whether {Stdio#stdout stdout} is connected to a terminal.
67
+ #
68
+ # @api public
69
+ #
70
+ def terminal?
71
+ IO.respond_to?(:console) && stdout.tty?
72
+ end
73
+
74
+ #
75
+ # @since 0.2.0
76
+ #
77
+ alias tty? terminal?
78
+
79
+ #
80
+ # Returns the terminal object, if {Stdio#stdout stdout} is connected to a
81
+ # terminal.
82
+ #
83
+ # @return [IO, nil]
84
+ # The IO objects or `nil` if {Stdio#stdout stdout} is not connected to a
85
+ # terminal.
86
+ #
87
+ # @example
88
+ # terminal
89
+ # # => #<File:/dev/tty>
90
+ #
91
+ # @see https://rubydoc.info/gems/io-console/IO
92
+ #
93
+ # @api semipublic
94
+ #
95
+ def terminal
96
+ IO.console if terminal?
97
+ end
98
+
99
+ #
100
+ # Returns the terminal's height in number of lines.
101
+ #
102
+ # @return [Integer]
103
+ # The terminal's height in number of lines.
104
+ #
105
+ # @example
106
+ # terminal_height
107
+ # # => 22
108
+ #
109
+ # @api public
110
+ #
111
+ def terminal_height
112
+ if (terminal = self.terminal)
113
+ terminal.winsize[0]
114
+ else
115
+ @terminal_height
116
+ end
117
+ end
118
+
119
+ #
120
+ # Returns the terminal's width in number of lines.
121
+ #
122
+ # @return [Integer]
123
+ # The terminal's width in number of columns.
124
+ #
125
+ # @example
126
+ # terminal_width
127
+ # # => 91
128
+ #
129
+ # @api public
130
+ #
131
+ def terminal_width
132
+ if (terminal = self.terminal)
133
+ terminal.winsize[1]
134
+ else
135
+ @terminal_width
136
+ end
137
+ end
138
+
139
+ #
140
+ # The terminal height (lines) and width (columns).
141
+ #
142
+ # @return [(Integer, Integer)]
143
+ # Returns the height and width of the terminal.
144
+ #
145
+ # @example
146
+ # terminal_size
147
+ # # => [23, 91]
148
+ #
149
+ # @api public
150
+ #
151
+ def terminal_size
152
+ if (terminal = self.terminal)
153
+ terminal.winsize
154
+ else
155
+ [@terminal_height, @terminal_width]
156
+ end
157
+ end
158
+ end
159
+ end
@@ -15,6 +15,9 @@ module CommandKit
15
15
  include CommandName
16
16
  include Help
17
17
 
18
+ #
19
+ # @api private
20
+ #
18
21
  module ModuleMethods
19
22
  #
20
23
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether {Usage}
@@ -50,6 +53,11 @@ module CommandKit
50
53
  # @return [String, Array<String>]
51
54
  # The class'es or superclass'es usage string(s).
52
55
  #
56
+ # @example
57
+ # usage "[options] ARG1 ARG2 [ARG3 ...]"
58
+ #
59
+ # @api public
60
+ #
53
61
  def usage(new_usage=nil)
54
62
  if new_usage
55
63
  @usage = new_usage
@@ -65,6 +73,8 @@ module CommandKit
65
73
  #
66
74
  # @return [Array<String>, String, nil]
67
75
  #
76
+ # @api public
77
+ #
68
78
  def usage
69
79
  case (usage = self.class.usage)
70
80
  when Array
@@ -77,6 +87,8 @@ module CommandKit
77
87
  #
78
88
  # Prints the `usage: ...` output.
79
89
  #
90
+ # @api semipublic
91
+ #
80
92
  def help_usage
81
93
  case (usage = self.usage)
82
94
  when Array
@@ -95,6 +107,8 @@ module CommandKit
95
107
  #
96
108
  # @see #help_usage
97
109
  #
110
+ # @api public
111
+ #
98
112
  def help
99
113
  help_usage
100
114
  end
@@ -1,4 +1,4 @@
1
1
  module CommandKit
2
2
  # command_kit version
3
- VERSION = "0.1.0.pre2"
3
+ VERSION = "0.2.1"
4
4
  end
@@ -24,6 +24,9 @@ module CommandKit
24
24
  include CommandName
25
25
  include Env::Home
26
26
 
27
+ #
28
+ # @api private
29
+ #
27
30
  module ModuleMethods
28
31
  #
29
32
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether {XDG} is
@@ -61,6 +64,8 @@ module CommandKit
61
64
  # {CommandName::ClassMethods#command_name} if no {#xdg_namespace} has
62
65
  # been defined.
63
66
  #
67
+ # @api public
68
+ #
64
69
  def xdg_namespace(new_namespace=nil)
65
70
  if new_namespace
66
71
  @xdg_namespace = new_namespace.to_s
@@ -77,16 +82,22 @@ module CommandKit
77
82
  # The `~/.config/<xdg_namespace>` directory.
78
83
  #
79
84
  # @return [String]
85
+ #
86
+ # @api public
80
87
  attr_reader :config_dir
81
88
 
82
89
  # The `~/.local/share/<xdg_namespace>` directory.
83
90
  #
84
91
  # @return [String]
92
+ #
93
+ # @api public
85
94
  attr_reader :local_share_dir
86
95
 
87
96
  # The `~/.cache/<xdg_namespace>` directory.
88
97
  #
89
98
  # @return [String]
99
+ #
100
+ # @api public
90
101
  attr_reader :cache_dir
91
102
 
92
103
  #
@@ -138,6 +149,8 @@ module CommandKit
138
149
  #
139
150
  # @see ClassMethods#xdg_namespace
140
151
  #
152
+ # @api semipublic
153
+ #
141
154
  def xdg_namespace
142
155
  self.class.xdg_namespace
143
156
  end
data/lib/command_kit.rb CHANGED
@@ -1 +1,2 @@
1
+ require 'command_kit/command'
1
2
  require 'command_kit/version'
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/arguments/argument'
3
3
 
4
- describe Arguments::Argument do
4
+ describe CommandKit::Arguments::Argument do
5
5
  let(:name) { :foo }
6
6
  let(:usage) { 'FOO' }
7
7
  let(:required) { true }
@@ -126,7 +126,7 @@ describe Arguments::Argument do
126
126
  let(:repeats) { false }
127
127
 
128
128
  it "must return the usage name unchanged" do
129
- expect(subject.usage).to eq("#{usage}")
129
+ expect(subject.usage).to eq(usage)
130
130
  end
131
131
  end
132
132
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/arguments'
3
3
 
4
- describe Arguments do
4
+ describe CommandKit::Arguments do
5
5
  module TestArguments
6
6
  class ImplicitCmd
7
7
  include CommandKit::Arguments
@@ -13,8 +13,8 @@ describe Arguments do
13
13
  describe ".included" do
14
14
  subject { command_class }
15
15
 
16
- it { expect(subject).to include(Main) }
17
- it { expect(subject).to include(Help) }
16
+ it { expect(subject).to include(CommandKit::Main) }
17
+ it { expect(subject).to include(CommandKit::Help) }
18
18
  end
19
19
 
20
20
  describe ".arguments" do
@@ -137,6 +137,56 @@ describe Arguments do
137
137
 
138
138
  subject { command_class.new }
139
139
 
140
+ describe "#main" do
141
+ module TestArguments
142
+ class TestCommand
143
+
144
+ include CommandKit::Arguments
145
+
146
+ argument :argument1, required: true,
147
+ usage: 'ARG1',
148
+ desc: "Argument 1"
149
+
150
+ argument :argument2, required: false,
151
+ usage: 'ARG2',
152
+ desc: "Argument 2"
153
+
154
+ end
155
+ end
156
+
157
+ let(:command_class) { TestArguments::TestCommand }
158
+
159
+ context "when given the correct number of arguments" do
160
+ let(:argv) { %w[arg1 arg2] }
161
+
162
+ it "must parse options before validating the number of arguments" do
163
+ expect {
164
+ expect(subject.main(argv)).to eq(0)
165
+ }.to_not output.to_stderr
166
+ end
167
+ end
168
+
169
+ context "when given fewer than the required number of arguments" do
170
+ let(:argv) { %w[] }
171
+
172
+ it "must print an error message and return 1" do
173
+ expect {
174
+ expect(subject.main(argv)).to eq(1)
175
+ }.to output("#{subject.command_name}: insufficient number of arguments.#{$/}").to_stderr
176
+ end
177
+ end
178
+
179
+ context "when given more than the total number of arguments" do
180
+ let(:argv) { %w[foo bar baz] }
181
+
182
+ it "must print an error message and return 1" do
183
+ expect {
184
+ expect(subject.main(argv)).to eq(1)
185
+ }.to output("#{subject.command_name}: too many arguments given.#{$/}").to_stderr
186
+ end
187
+ end
188
+ end
189
+
140
190
  describe "#help_arguments" do
141
191
  context "when #arguments returns {}" do
142
192
  module TestArguments
@@ -192,29 +242,5 @@ describe Arguments do
192
242
 
193
243
  subject.help
194
244
  end
195
-
196
- context "when the superclass defines it's own #help method" do
197
- module TestDescription
198
- class SuperclassHelpMethod
199
- def help
200
- puts 'superclass'
201
- end
202
- end
203
-
204
- class InheritedHelpMethod < SuperclassHelpMethod
205
- include CommandKit::Arguments
206
- end
207
- end
208
-
209
- let(:super_command_class) { TestDescription::SuperclassHelpMethod }
210
- let(:command_class) { TestDescription::InheritedHelpMethod }
211
-
212
- it "must call the superclass'es #help method first" do
213
- expect_any_instance_of(super_command_class).to receive(:help)
214
- expect(subject).to receive(:help_arguments)
215
-
216
- subject.help
217
- end
218
- end
219
245
  end
220
246
  end