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
data/spec/pager_spec.rb CHANGED
@@ -3,7 +3,7 @@ require 'command_kit/pager'
3
3
 
4
4
  require 'stringio'
5
5
 
6
- describe Pager do
6
+ describe CommandKit::Pager do
7
7
  module TestPager
8
8
  class TestCommand
9
9
  include CommandKit::Pager
@@ -19,8 +19,8 @@ describe 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 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 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 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 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
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/printing/indent'
3
3
 
4
- describe Printing::Indent do
4
+ describe CommandKit::Printing::Indent do
5
5
  module TestIndent
6
6
  class TestCommand
7
7
  include CommandKit::Printing::Indent
@@ -12,14 +12,16 @@ describe 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 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 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,9 +1,10 @@
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
 
6
- describe Printing do
7
+ describe CommandKit::Printing do
7
8
  module TestPrinting
8
9
  class TestCmd
9
10
 
@@ -14,11 +15,19 @@ describe Printing do
14
15
 
15
16
  let(:command_class) { TestPrinting::TestCmd }
16
17
  subject { command_class.new }
18
+
19
+ describe "EOL" do
20
+ subject { described_class::EOL }
21
+
22
+ it "must equal $/" do
23
+ expect(subject).to eq($/)
24
+ end
25
+ end
17
26
 
18
27
  describe ".included" do
19
28
  subject { command_class }
20
29
 
21
- it { expect(command_class).to include(Stdio) }
30
+ it { expect(command_class).to include(CommandKit::Stdio) }
22
31
  end
23
32
 
24
33
  let(:nl) { $/ }
@@ -26,11 +35,32 @@ describe Printing do
26
35
  describe "#print_error" do
27
36
  let(:message) { "oh no!" }
28
37
 
29
- it "must print a line to stderr" do
38
+ it "must print the error message to stderr" do
30
39
  expect {
31
40
  subject.print_error(message)
32
41
  }.to output("#{message}#{nl}").to_stderr
33
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
34
64
  end
35
65
 
36
66
  describe "#print_exception" do
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/program_name'
3
3
 
4
- describe ProgramName do
4
+ describe CommandKit::ProgramName do
5
5
  module TestProgramName
6
6
  class TestCmd
7
7
  include CommandKit::ProgramName
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,3 @@
1
1
  require 'rspec'
2
2
  require 'simplecov'
3
3
  SimpleCov.start
4
-
5
- require 'command_kit/version'
6
- include CommandKit
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
@@ -3,7 +3,7 @@ require 'command_kit/terminal'
3
3
 
4
4
  require 'stringio'
5
5
 
6
- describe Terminal do
6
+ describe CommandKit::Terminal do
7
7
  module TestTerminal
8
8
  class TestCommand
9
9
  include CommandKit::Terminal
@@ -43,6 +43,36 @@ describe 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
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/usage'
3
3
 
4
- describe Usage do
4
+ describe CommandKit::Usage do
5
5
  module TestUsage
6
6
  class ImplicitCmd
7
7
  include CommandKit::Usage
@@ -182,7 +182,7 @@ describe 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
data/spec/xdg_spec.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/xdg'
3
3
 
4
- describe XDG do
4
+ describe CommandKit::XDG do
5
5
  module TestXDG
6
6
  class TestCommand
7
7
  include CommandKit::XDG
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.rc1
4
+ version: 0.2.2
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-02 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
@@ -75,6 +76,8 @@ files:
75
76
  - lib/command_kit/inflector.rb
76
77
  - lib/command_kit/interactive.rb
77
78
  - lib/command_kit/main.rb
79
+ - lib/command_kit/man.rb
80
+ - lib/command_kit/open_app.rb
78
81
  - lib/command_kit/options.rb
79
82
  - lib/command_kit/options/option.rb
80
83
  - lib/command_kit/options/option_value.rb
@@ -83,11 +86,14 @@ files:
83
86
  - lib/command_kit/options/verbose.rb
84
87
  - lib/command_kit/options/version.rb
85
88
  - lib/command_kit/os.rb
89
+ - lib/command_kit/os/linux.rb
90
+ - lib/command_kit/package_manager.rb
86
91
  - lib/command_kit/pager.rb
87
92
  - lib/command_kit/printing.rb
88
93
  - lib/command_kit/printing/indent.rb
89
94
  - lib/command_kit/program_name.rb
90
95
  - lib/command_kit/stdio.rb
96
+ - lib/command_kit/sudo.rb
91
97
  - lib/command_kit/terminal.rb
92
98
  - lib/command_kit/usage.rb
93
99
  - lib/command_kit/version.rb
@@ -120,24 +126,34 @@ files:
120
126
  - spec/inflector_spec.rb
121
127
  - spec/interactive_spec.rb
122
128
  - spec/main_spec.rb
129
+ - spec/man_spec.rb
130
+ - spec/open_app_spec.rb
123
131
  - spec/options/option_spec.rb
124
132
  - spec/options/option_value_spec.rb
125
133
  - spec/options/parser_spec.rb
126
134
  - spec/options_spec.rb
135
+ - spec/os/linux_spec.rb
127
136
  - spec/os_spec.rb
137
+ - spec/package_manager_spec.rb
128
138
  - spec/pager_spec.rb
129
139
  - spec/printing/indent_spec.rb
130
140
  - spec/printing_spec.rb
131
141
  - spec/program_name_spec.rb
132
142
  - spec/spec_helper.rb
133
143
  - spec/stdio_spec.rb
144
+ - spec/sudo_spec.rb
134
145
  - spec/terminal_spec.rb
135
146
  - spec/usage_spec.rb
136
147
  - spec/xdg_spec.rb
137
- homepage: https://github.com/postmodern/command_kit#readme
148
+ homepage: https://github.com/postmodern/command_kit.rb#readme
138
149
  licenses:
139
150
  - MIT
140
- metadata: {}
151
+ metadata:
152
+ documentation_uri: https://rubydoc.info/gems/command_kit
153
+ source_code_uri: https://github.com/postmodern/command_kit.rb
154
+ bug_tracker_uri: https://github.com/postmodern/command_kit.rb/issues
155
+ changelog_uri: https://github.com/postmodern/command_kit.rb/blob/main/ChangeLog.md
156
+ rubygems_mfa_required: 'true'
141
157
  post_install_message:
142
158
  rdoc_options: []
143
159
  require_paths:
@@ -153,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
169
  - !ruby/object:Gem::Version
154
170
  version: '0'
155
171
  requirements: []
156
- rubygems_version: 3.2.15
172
+ rubygems_version: 3.2.22
157
173
  signing_key:
158
174
  specification_version: 4
159
175
  summary: A toolkit for building Ruby CLI commands