command_kit 0.1.0 → 0.3.0

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +18 -3
  3. data/.rubocop.yml +141 -0
  4. data/ChangeLog.md +165 -0
  5. data/Gemfile +3 -0
  6. data/README.md +186 -118
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/command.rb +1 -1
  10. data/gemspec.yml +7 -0
  11. data/lib/command_kit/arguments/argument.rb +2 -2
  12. data/lib/command_kit/arguments.rb +36 -7
  13. data/lib/command_kit/colors.rb +702 -53
  14. data/lib/command_kit/command.rb +2 -3
  15. data/lib/command_kit/commands/auto_load.rb +8 -1
  16. data/lib/command_kit/commands/help.rb +3 -2
  17. data/lib/command_kit/commands/subcommand.rb +1 -1
  18. data/lib/command_kit/commands.rb +24 -9
  19. data/lib/command_kit/env/path.rb +1 -1
  20. data/lib/command_kit/file_utils.rb +46 -0
  21. data/lib/command_kit/help/man.rb +17 -33
  22. data/lib/command_kit/inflector.rb +47 -17
  23. data/lib/command_kit/interactive.rb +9 -0
  24. data/lib/command_kit/main.rb +7 -9
  25. data/lib/command_kit/man.rb +44 -0
  26. data/lib/command_kit/open_app.rb +69 -0
  27. data/lib/command_kit/options/option.rb +41 -27
  28. data/lib/command_kit/options/option_value.rb +3 -2
  29. data/lib/command_kit/options/parser.rb +17 -22
  30. data/lib/command_kit/options.rb +102 -14
  31. data/lib/command_kit/os/linux.rb +157 -0
  32. data/lib/command_kit/os.rb +159 -11
  33. data/lib/command_kit/package_manager.rb +200 -0
  34. data/lib/command_kit/pager.rb +46 -4
  35. data/lib/command_kit/printing/indent.rb +4 -4
  36. data/lib/command_kit/printing.rb +14 -3
  37. data/lib/command_kit/program_name.rb +9 -0
  38. data/lib/command_kit/sudo.rb +40 -0
  39. data/lib/command_kit/terminal.rb +5 -0
  40. data/lib/command_kit/version.rb +1 -1
  41. data/spec/arguments/argument_spec.rb +1 -1
  42. data/spec/arguments_spec.rb +84 -1
  43. data/spec/colors_spec.rb +357 -70
  44. data/spec/command_spec.rb +77 -6
  45. data/spec/commands/auto_load_spec.rb +33 -2
  46. data/spec/commands_spec.rb +101 -29
  47. data/spec/env/path_spec.rb +6 -0
  48. data/spec/exception_handler_spec.rb +1 -1
  49. data/spec/file_utils_spec.rb +59 -0
  50. data/spec/fixtures/template.erb +5 -0
  51. data/spec/help/man_spec.rb +54 -57
  52. data/spec/inflector_spec.rb +70 -8
  53. data/spec/man_spec.rb +46 -0
  54. data/spec/open_app_spec.rb +85 -0
  55. data/spec/options/option_spec.rb +38 -2
  56. data/spec/options/option_value_spec.rb +55 -0
  57. data/spec/options/parser_spec.rb +0 -10
  58. data/spec/options_spec.rb +328 -0
  59. data/spec/os/linux_spec.rb +164 -0
  60. data/spec/os_spec.rb +200 -13
  61. data/spec/package_manager_spec.rb +806 -0
  62. data/spec/pager_spec.rb +71 -6
  63. data/spec/printing/indent_spec.rb +7 -5
  64. data/spec/printing_spec.rb +23 -1
  65. data/spec/program_name_spec.rb +8 -0
  66. data/spec/sudo_spec.rb +51 -0
  67. data/spec/terminal_spec.rb +30 -0
  68. data/spec/usage_spec.rb +1 -1
  69. metadata +23 -4
data/spec/pager_spec.rb CHANGED
@@ -19,8 +19,8 @@ describe CommandKit::Pager do
19
19
 
20
20
  subject { command_class.new(env: {'PAGER' => pager}) }
21
21
 
22
- it "must set @pager to the PAGER env variable" do
23
- expect(subject.instance_variable_get('@pager')).to eq(pager)
22
+ it "must set @pager_command to the PAGER env variable" do
23
+ expect(subject.instance_variable_get('@pager_command')).to eq(pager)
24
24
  end
25
25
  end
26
26
 
@@ -29,7 +29,7 @@ describe CommandKit::Pager do
29
29
  subject { command_class.new(env: {'PATH' => ENV['PATH']}) }
30
30
 
31
31
  it "must search PATH for one of the pagers" do
32
- expect(subject.instance_variable_get('@pager')).to eq("less -r")
32
+ expect(subject.instance_variable_get('@pager_command')).to eq("less -r")
33
33
  end
34
34
  end
35
35
  end
@@ -46,9 +46,9 @@ describe CommandKit::Pager do
46
46
  end
47
47
  end
48
48
 
49
- context "when @pager isn't initialized" do
49
+ context "when @pager_command isn't initialized" do
50
50
  before do
51
- subject.instance_variable_set('@pager',nil)
51
+ subject.instance_variable_set('@pager_command',nil)
52
52
  end
53
53
 
54
54
  it "must yield stdout" do
@@ -58,7 +58,7 @@ describe CommandKit::Pager do
58
58
  end
59
59
  end
60
60
 
61
- context "when stdout is a TTY and @pager is initialized" do
61
+ context "when stdout is a TTY and @pager_command is initialized" do
62
62
  let(:pager) { 'less -r' }
63
63
 
64
64
  subject { command_class.new(env: {'PAGER' => pager}) }
@@ -149,4 +149,69 @@ describe CommandKit::Pager do
149
149
  end
150
150
  end
151
151
  end
152
+
153
+ describe "#pipe_to_pager" do
154
+ context "when @pager_command is initialized" do
155
+ let(:pager) { 'less' }
156
+
157
+ subject do
158
+ command_class.new(env: {'PAGER' => pager})
159
+ end
160
+
161
+ context "and when given a single String" do
162
+ let(:command) { "find ." }
163
+
164
+ it "must run the command but piped into the pager" do
165
+ expect(subject).to receive(:system).with("#{command} | #{pager}")
166
+
167
+ subject.pipe_to_pager(command)
168
+ end
169
+ end
170
+
171
+ context "and when given a String and additional arguments" do
172
+ let(:command) { 'find' }
173
+ let(:arguments) { %w[. -name *.md] }
174
+
175
+ let(:escaped_command) do
176
+ Shellwords.shelljoin([command,*arguments])
177
+ end
178
+
179
+ it "must shell escape the command and arguments" do
180
+ expect(subject).to receive(:system).with(
181
+ "#{escaped_command} | #{pager}"
182
+ )
183
+
184
+ subject.pipe_to_pager(command,*arguments)
185
+ end
186
+ end
187
+ end
188
+
189
+ context "when @pager_command is not initialized" do
190
+ before do
191
+ subject.instance_variable_set('@pager_command',nil)
192
+ end
193
+
194
+ let(:command) { 'find' }
195
+ let(:arguments) { %w[. -name *.md] }
196
+
197
+ it "must pass the command and any additional arguments to #system" do
198
+ expect(subject).to receive(:system).with(command,*arguments)
199
+
200
+ subject.pipe_to_pager(command,*arguments)
201
+ end
202
+
203
+ context "when one of the arguments is not a String" do
204
+ let(:command) { :find }
205
+ let(:arguments) { ['.', :"-name", "*.md"] }
206
+
207
+ it "must convert the command to a String before calling #system" do
208
+ expect(subject).to receive(:system).with(
209
+ command.to_s, *arguments.map(&:to_s)
210
+ )
211
+
212
+ subject.pipe_to_pager(command,*arguments)
213
+ end
214
+ end
215
+ end
216
+ end
152
217
  end
@@ -12,14 +12,16 @@ describe CommandKit::Printing::Indent do
12
12
  subject { command_class.new }
13
13
 
14
14
  describe "#initialize" do
15
- it "must initialize @indent to 0" do
16
- expect(subject.instance_variable_get('@indent')).to eq(0)
15
+ it "must initialize #indent to 0" do
16
+ expect(subject.indent).to eq(0)
17
17
  end
18
18
 
19
19
  context "when the class has a superclass" do
20
20
  module TestIndent
21
21
  class TestSuperCommand
22
22
 
23
+ attr_reader :var
24
+
23
25
  def initialize(var: 'default')
24
26
  @var = var
25
27
  end
@@ -36,11 +38,11 @@ describe CommandKit::Printing::Indent do
36
38
  let(:command_class) { TestIndent::TestSubCommand }
37
39
 
38
40
  it "must initialize @indent to 0" do
39
- expect(subject.instance_variable_get('@indent')).to eq(0)
41
+ expect(subject.indent).to eq(0)
40
42
  end
41
43
 
42
44
  it "must call super()" do
43
- expect(subject.instance_variable_get('@var')).to eq('default')
45
+ expect(subject.var).to eq('default')
44
46
  end
45
47
 
46
48
  context "and additional keyword arguments are given" do
@@ -49,7 +51,7 @@ describe CommandKit::Printing::Indent do
49
51
  subject { command_class.new(var: var) }
50
52
 
51
53
  it "must call super() with the additional keyword arguments" do
52
- expect(subject.instance_variable_get('@var')).to eq(var)
54
+ expect(subject.var).to eq(var)
53
55
  end
54
56
  end
55
57
  end
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/printing'
3
+ require 'command_kit/command_name'
3
4
 
4
5
  require 'stringio'
5
6
 
@@ -34,11 +35,32 @@ describe CommandKit::Printing do
34
35
  describe "#print_error" do
35
36
  let(:message) { "oh no!" }
36
37
 
37
- it "must print a line to stderr" do
38
+ it "must print the error message to stderr" do
38
39
  expect {
39
40
  subject.print_error(message)
40
41
  }.to output("#{message}#{nl}").to_stderr
41
42
  end
43
+
44
+ context "and when CommandKit::CommandName is included" do
45
+ module TestPrinting
46
+ class TestCmdWithCommandName
47
+
48
+ include CommandKit::CommandName
49
+ include CommandKit::Printing
50
+
51
+ command_name 'foo'
52
+
53
+ end
54
+ end
55
+
56
+ let(:command_class) { TestPrinting::TestCmdWithCommandName }
57
+
58
+ it "must print the command_name and the error message" do
59
+ expect {
60
+ subject.print_error(message)
61
+ }.to output("#{subject.command_name}: #{message}#{nl}").to_stderr
62
+ end
63
+ end
42
64
  end
43
65
 
44
66
  describe "#print_exception" do
@@ -56,6 +56,14 @@ describe CommandKit::ProgramName do
56
56
  end
57
57
  end
58
58
 
59
+ describe "#command_name" do
60
+ subject { command_class.new }
61
+
62
+ it "should be the same as #program_name" do
63
+ expect(subject.command_name).to eq(subject.program_name)
64
+ end
65
+ end
66
+
59
67
  after do
60
68
  $PROGRAM_NAME = @original_program_name
61
69
  end
data/spec/sudo_spec.rb ADDED
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/sudo'
3
+
4
+ describe CommandKit::Sudo do
5
+ module TestSudo
6
+ class TestCommand
7
+ include CommandKit::Sudo
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestSudo::TestCommand }
12
+ subject { command_class.new }
13
+
14
+ describe "#sudo" do
15
+ let(:command) { "ls" }
16
+ let(:arguments) { ["-la", "~root"] }
17
+ let(:status) { double(:status) }
18
+
19
+ context "on UNIX" do
20
+ context "when the UID is 0" do
21
+ before { allow(Process).to receive(:uid).and_return(0) }
22
+
23
+ it "must execute the command without sudo" do
24
+ expect(subject).to receive(:system).with(command,*arguments).and_return(status)
25
+
26
+ expect(subject.sudo(command,*arguments)).to be(status)
27
+ end
28
+ end
29
+
30
+ context "when the UID is not 0" do
31
+ before { allow(Process).to receive(:uid).and_return(1000) }
32
+
33
+ it "must execute the command with 'sudo ...'" do
34
+ expect(subject).to receive(:system).with('sudo',command,*arguments).and_return(status)
35
+
36
+ expect(subject.sudo(command,*arguments)).to be(status)
37
+ end
38
+ end
39
+ end
40
+
41
+ context "on Windows" do
42
+ subject { command_class.new(os: :windows) }
43
+
44
+ it "must execute the command with 'runas /user:administrator ...'" do
45
+ expect(subject).to receive(:system).with('runas','/user:administrator',command,*arguments).and_return(status)
46
+
47
+ expect(subject.sudo(command,*arguments)).to be(status)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -43,6 +43,36 @@ describe CommandKit::Terminal do
43
43
  end
44
44
  end
45
45
 
46
+ describe "#tty?" do
47
+ context "when stdout is connected to a TTY" do
48
+ subject { command_class.new(stdout: STDOUT) }
49
+
50
+ it do
51
+ skip "STDOUT is not a TTY" unless STDOUT.tty?
52
+
53
+ expect(subject.tty?).to be(true)
54
+ end
55
+ end
56
+
57
+ context "when stdout is not connected to a TTY" do
58
+ subject { command_class.new(stdout: StringIO.new) }
59
+
60
+ it do
61
+ expect(subject.tty?).to be(false)
62
+ end
63
+ end
64
+
65
+ context "when IO.console is missing" do
66
+ before do
67
+ expect(IO).to receive(:respond_to?).with(:console).and_return(false)
68
+ end
69
+
70
+ it do
71
+ expect(subject.tty?).to be(false)
72
+ end
73
+ end
74
+ end
75
+
46
76
  describe "#terminal" do
47
77
  context "when stdout is connected to a TTY" do
48
78
  subject { command_class.new(stdout: STDOUT) }
data/spec/usage_spec.rb CHANGED
@@ -182,7 +182,7 @@ describe CommandKit::Usage do
182
182
  [
183
183
  "#{subject.command_name} #{command_class.usage[0]}",
184
184
  "#{subject.command_name} #{command_class.usage[1]}",
185
- "#{subject.command_name} #{command_class.usage[2]}",
185
+ "#{subject.command_name} #{command_class.usage[2]}"
186
186
  ]
187
187
  )
188
188
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-17 00:00:00.000000000 Z
11
+ date: 2021-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,6 +38,7 @@ files:
38
38
  - ".github/workflows/ruby.yml"
39
39
  - ".gitignore"
40
40
  - ".rspec"
41
+ - ".rubocop.yml"
41
42
  - ".yardopts"
42
43
  - ChangeLog.md
43
44
  - Gemfile
@@ -70,11 +71,14 @@ files:
70
71
  - lib/command_kit/env/path.rb
71
72
  - lib/command_kit/examples.rb
72
73
  - lib/command_kit/exception_handler.rb
74
+ - lib/command_kit/file_utils.rb
73
75
  - lib/command_kit/help.rb
74
76
  - lib/command_kit/help/man.rb
75
77
  - lib/command_kit/inflector.rb
76
78
  - lib/command_kit/interactive.rb
77
79
  - lib/command_kit/main.rb
80
+ - lib/command_kit/man.rb
81
+ - lib/command_kit/open_app.rb
78
82
  - lib/command_kit/options.rb
79
83
  - lib/command_kit/options/option.rb
80
84
  - lib/command_kit/options/option_value.rb
@@ -83,11 +87,14 @@ files:
83
87
  - lib/command_kit/options/verbose.rb
84
88
  - lib/command_kit/options/version.rb
85
89
  - lib/command_kit/os.rb
90
+ - lib/command_kit/os/linux.rb
91
+ - lib/command_kit/package_manager.rb
86
92
  - lib/command_kit/pager.rb
87
93
  - lib/command_kit/printing.rb
88
94
  - lib/command_kit/printing/indent.rb
89
95
  - lib/command_kit/program_name.rb
90
96
  - lib/command_kit/stdio.rb
97
+ - lib/command_kit/sudo.rb
91
98
  - lib/command_kit/terminal.rb
92
99
  - lib/command_kit/usage.rb
93
100
  - lib/command_kit/version.rb
@@ -115,29 +122,41 @@ files:
115
122
  - spec/env_spec.rb
116
123
  - spec/examples_spec.rb
117
124
  - spec/exception_handler_spec.rb
125
+ - spec/file_utils_spec.rb
126
+ - spec/fixtures/template.erb
118
127
  - spec/help/man_spec.rb
119
128
  - spec/help_spec.rb
120
129
  - spec/inflector_spec.rb
121
130
  - spec/interactive_spec.rb
122
131
  - spec/main_spec.rb
132
+ - spec/man_spec.rb
133
+ - spec/open_app_spec.rb
123
134
  - spec/options/option_spec.rb
124
135
  - spec/options/option_value_spec.rb
125
136
  - spec/options/parser_spec.rb
126
137
  - spec/options_spec.rb
138
+ - spec/os/linux_spec.rb
127
139
  - spec/os_spec.rb
140
+ - spec/package_manager_spec.rb
128
141
  - spec/pager_spec.rb
129
142
  - spec/printing/indent_spec.rb
130
143
  - spec/printing_spec.rb
131
144
  - spec/program_name_spec.rb
132
145
  - spec/spec_helper.rb
133
146
  - spec/stdio_spec.rb
147
+ - spec/sudo_spec.rb
134
148
  - spec/terminal_spec.rb
135
149
  - spec/usage_spec.rb
136
150
  - spec/xdg_spec.rb
137
151
  homepage: https://github.com/postmodern/command_kit.rb#readme
138
152
  licenses:
139
153
  - MIT
140
- metadata: {}
154
+ metadata:
155
+ documentation_uri: https://rubydoc.info/gems/command_kit
156
+ source_code_uri: https://github.com/postmodern/command_kit.rb
157
+ bug_tracker_uri: https://github.com/postmodern/command_kit.rb/issues
158
+ changelog_uri: https://github.com/postmodern/command_kit.rb/blob/main/ChangeLog.md
159
+ rubygems_mfa_required: 'true'
141
160
  post_install_message:
142
161
  rdoc_options: []
143
162
  require_paths:
@@ -153,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
172
  - !ruby/object:Gem::Version
154
173
  version: '0'
155
174
  requirements: []
156
- rubygems_version: 3.2.15
175
+ rubygems_version: 3.2.22
157
176
  signing_key:
158
177
  specification_version: 4
159
178
  summary: A toolkit for building Ruby CLI commands