command_kit 0.1.0.pre2 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +15 -0
  3. data/.rubocop.yml +141 -0
  4. data/ChangeLog.md +98 -2
  5. data/Gemfile +3 -0
  6. data/README.md +189 -117
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/command.rb +1 -1
  10. data/gemspec.yml +10 -2
  11. data/lib/command_kit/arguments/argument.rb +2 -0
  12. data/lib/command_kit/arguments/argument_value.rb +2 -0
  13. data/lib/command_kit/arguments.rb +23 -4
  14. data/lib/command_kit/colors.rb +253 -45
  15. data/lib/command_kit/command.rb +6 -1
  16. data/lib/command_kit/command_name.rb +9 -0
  17. data/lib/command_kit/commands/auto_load.rb +24 -1
  18. data/lib/command_kit/commands/auto_require.rb +16 -0
  19. data/lib/command_kit/commands/command.rb +3 -0
  20. data/lib/command_kit/commands/help.rb +5 -2
  21. data/lib/command_kit/commands/parent_command.rb +7 -0
  22. data/lib/command_kit/commands/subcommand.rb +13 -1
  23. data/lib/command_kit/commands.rb +54 -9
  24. data/lib/command_kit/description.rb +12 -1
  25. data/lib/command_kit/env/home.rb +9 -0
  26. data/lib/command_kit/env/path.rb +16 -1
  27. data/lib/command_kit/env.rb +4 -0
  28. data/lib/command_kit/examples.rb +12 -1
  29. data/lib/command_kit/exception_handler.rb +4 -0
  30. data/lib/command_kit/help/man.rb +26 -30
  31. data/lib/command_kit/help.rb +7 -1
  32. data/lib/command_kit/inflector.rb +49 -17
  33. data/lib/command_kit/interactive.rb +248 -0
  34. data/lib/command_kit/main.rb +18 -9
  35. data/lib/command_kit/man.rb +44 -0
  36. data/lib/command_kit/open_app.rb +69 -0
  37. data/lib/command_kit/options/option.rb +3 -6
  38. data/lib/command_kit/options/option_value.rb +5 -2
  39. data/lib/command_kit/options/parser.rb +46 -19
  40. data/lib/command_kit/options/quiet.rb +3 -0
  41. data/lib/command_kit/options/verbose.rb +5 -0
  42. data/lib/command_kit/options/version.rb +6 -0
  43. data/lib/command_kit/options.rb +32 -7
  44. data/lib/command_kit/os/linux.rb +157 -0
  45. data/lib/command_kit/os.rb +165 -11
  46. data/lib/command_kit/package_manager.rb +200 -0
  47. data/lib/command_kit/pager.rb +80 -11
  48. data/lib/command_kit/printing/indent.rb +27 -4
  49. data/lib/command_kit/printing.rb +35 -1
  50. data/lib/command_kit/program_name.rb +7 -0
  51. data/lib/command_kit/stdio.rb +24 -0
  52. data/lib/command_kit/sudo.rb +40 -0
  53. data/lib/command_kit/terminal.rb +159 -0
  54. data/lib/command_kit/usage.rb +14 -0
  55. data/lib/command_kit/version.rb +1 -1
  56. data/lib/command_kit/xdg.rb +13 -0
  57. data/lib/command_kit.rb +1 -0
  58. data/spec/arguments/argument_spec.rb +2 -2
  59. data/spec/arguments_spec.rb +53 -27
  60. data/spec/colors_spec.rb +277 -13
  61. data/spec/command_name_spec.rb +1 -1
  62. data/spec/command_spec.rb +79 -5
  63. data/spec/commands/auto_load/subcommand_spec.rb +1 -1
  64. data/spec/commands/auto_load_spec.rb +34 -3
  65. data/spec/commands/auto_require_spec.rb +2 -2
  66. data/spec/commands/help_spec.rb +1 -1
  67. data/spec/commands/parent_command_spec.rb +1 -1
  68. data/spec/commands/subcommand_spec.rb +1 -1
  69. data/spec/commands_spec.rb +103 -29
  70. data/spec/description_spec.rb +1 -25
  71. data/spec/env/home_spec.rb +1 -1
  72. data/spec/env/path_spec.rb +7 -1
  73. data/spec/examples_spec.rb +1 -25
  74. data/spec/exception_handler_spec.rb +1 -1
  75. data/spec/help/man_spec.rb +45 -58
  76. data/spec/help_spec.rb +0 -25
  77. data/spec/inflector_spec.rb +71 -9
  78. data/spec/interactive_spec.rb +415 -0
  79. data/spec/main_spec.rb +7 -7
  80. data/spec/man_spec.rb +46 -0
  81. data/spec/open_app_spec.rb +85 -0
  82. data/spec/options/option_spec.rb +5 -5
  83. data/spec/options/option_value_spec.rb +56 -1
  84. data/spec/options_spec.rb +283 -1
  85. data/spec/os/linux_spec.rb +164 -0
  86. data/spec/os_spec.rb +201 -14
  87. data/spec/package_manager_spec.rb +806 -0
  88. data/spec/pager_spec.rb +76 -11
  89. data/spec/printing/indent_spec.rb +8 -6
  90. data/spec/printing_spec.rb +33 -3
  91. data/spec/program_name_spec.rb +1 -1
  92. data/spec/spec_helper.rb +0 -3
  93. data/spec/sudo_spec.rb +51 -0
  94. data/spec/{console_spec.rb → terminal_spec.rb} +65 -35
  95. data/spec/usage_spec.rb +2 -2
  96. data/spec/xdg_spec.rb +1 -1
  97. metadata +26 -8
  98. data/lib/command_kit/console.rb +0 -141
@@ -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