command_kit 0.1.0.pre1

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 +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +29 -0
  4. data/.gitignore +7 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +29 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +283 -0
  11. data/Rakefile +23 -0
  12. data/command_kit.gemspec +60 -0
  13. data/gemspec.yml +14 -0
  14. data/lib/command_kit.rb +1 -0
  15. data/lib/command_kit/arguments.rb +161 -0
  16. data/lib/command_kit/arguments/argument.rb +111 -0
  17. data/lib/command_kit/arguments/argument_value.rb +81 -0
  18. data/lib/command_kit/arguments/usage.rb +6 -0
  19. data/lib/command_kit/colors.rb +355 -0
  20. data/lib/command_kit/command.rb +42 -0
  21. data/lib/command_kit/command_name.rb +95 -0
  22. data/lib/command_kit/commands.rb +299 -0
  23. data/lib/command_kit/commands/auto_load.rb +153 -0
  24. data/lib/command_kit/commands/auto_load/subcommand.rb +90 -0
  25. data/lib/command_kit/commands/auto_require.rb +138 -0
  26. data/lib/command_kit/commands/command.rb +12 -0
  27. data/lib/command_kit/commands/help.rb +43 -0
  28. data/lib/command_kit/commands/parent_command.rb +21 -0
  29. data/lib/command_kit/commands/subcommand.rb +51 -0
  30. data/lib/command_kit/console.rb +141 -0
  31. data/lib/command_kit/description.rb +89 -0
  32. data/lib/command_kit/env.rb +43 -0
  33. data/lib/command_kit/env/home.rb +71 -0
  34. data/lib/command_kit/env/path.rb +71 -0
  35. data/lib/command_kit/examples.rb +99 -0
  36. data/lib/command_kit/exception_handler.rb +55 -0
  37. data/lib/command_kit/help.rb +62 -0
  38. data/lib/command_kit/help/man.rb +125 -0
  39. data/lib/command_kit/inflector.rb +84 -0
  40. data/lib/command_kit/main.rb +103 -0
  41. data/lib/command_kit/options.rb +179 -0
  42. data/lib/command_kit/options/option.rb +171 -0
  43. data/lib/command_kit/options/option_value.rb +90 -0
  44. data/lib/command_kit/options/parser.rb +227 -0
  45. data/lib/command_kit/options/quiet.rb +53 -0
  46. data/lib/command_kit/options/usage.rb +6 -0
  47. data/lib/command_kit/options/verbose.rb +55 -0
  48. data/lib/command_kit/options/version.rb +62 -0
  49. data/lib/command_kit/os.rb +47 -0
  50. data/lib/command_kit/pager.rb +115 -0
  51. data/lib/command_kit/printing.rb +32 -0
  52. data/lib/command_kit/printing/indent.rb +78 -0
  53. data/lib/command_kit/program_name.rb +57 -0
  54. data/lib/command_kit/stdio.rb +138 -0
  55. data/lib/command_kit/usage.rb +102 -0
  56. data/lib/command_kit/version.rb +4 -0
  57. data/lib/command_kit/xdg.rb +138 -0
  58. data/spec/arguments/argument_spec.rb +169 -0
  59. data/spec/arguments/argument_value_spec.rb +126 -0
  60. data/spec/arguments_spec.rb +213 -0
  61. data/spec/colors_spec.rb +470 -0
  62. data/spec/command_kit_spec.rb +8 -0
  63. data/spec/command_name_spec.rb +130 -0
  64. data/spec/command_spec.rb +49 -0
  65. data/spec/commands/auto_load/subcommand_spec.rb +82 -0
  66. data/spec/commands/auto_load_spec.rb +128 -0
  67. data/spec/commands/auto_require_spec.rb +142 -0
  68. data/spec/commands/fixtures/test_auto_load/cli/commands/test1.rb +10 -0
  69. data/spec/commands/fixtures/test_auto_load/cli/commands/test2.rb +10 -0
  70. data/spec/commands/fixtures/test_auto_require/lib/test_auto_require/cli/commands/test1.rb +10 -0
  71. data/spec/commands/help_spec.rb +66 -0
  72. data/spec/commands/parent_command_spec.rb +40 -0
  73. data/spec/commands/subcommand_spec.rb +99 -0
  74. data/spec/commands_spec.rb +767 -0
  75. data/spec/console_spec.rb +201 -0
  76. data/spec/description_spec.rb +203 -0
  77. data/spec/env/home_spec.rb +46 -0
  78. data/spec/env/path_spec.rb +78 -0
  79. data/spec/env_spec.rb +123 -0
  80. data/spec/examples_spec.rb +235 -0
  81. data/spec/exception_handler_spec.rb +103 -0
  82. data/spec/help_spec.rb +119 -0
  83. data/spec/inflector_spec.rb +104 -0
  84. data/spec/main_spec.rb +179 -0
  85. data/spec/options/option_spec.rb +258 -0
  86. data/spec/options/option_value_spec.rb +67 -0
  87. data/spec/options/parser_spec.rb +265 -0
  88. data/spec/options_spec.rb +137 -0
  89. data/spec/os_spec.rb +46 -0
  90. data/spec/pager_spec.rb +154 -0
  91. data/spec/printing/indent_spec.rb +130 -0
  92. data/spec/printing_spec.rb +76 -0
  93. data/spec/program_name_spec.rb +62 -0
  94. data/spec/spec_helper.rb +6 -0
  95. data/spec/stdio_spec.rb +264 -0
  96. data/spec/usage_spec.rb +237 -0
  97. data/spec/xdg_spec.rb +191 -0
  98. metadata +156 -0
data/spec/os_spec.rb ADDED
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/os'
3
+
4
+ describe OS do
5
+ class TestOS
6
+ include CommandKit::OS
7
+ end
8
+
9
+ subject { TestOS.new }
10
+
11
+ describe "#linux?" do
12
+ context "when RUBY_PLATFORM contains 'linux'" do
13
+ before { stub_const('RUBY_PLATFORM','x86_64-linux') }
14
+
15
+ it { expect(subject.linux?).to be(true) }
16
+ end
17
+
18
+ context "when RUBY_PLATFORM does not contain 'linux'" do
19
+ before { stub_const('RUBY_PLATFORM','aarch64-darwin') }
20
+
21
+ it { expect(subject.linux?).to be(false) }
22
+ end
23
+ end
24
+
25
+ describe "#macos?" do
26
+ context "when RUBY_PLATFORM contains 'darwin'" do
27
+ before { stub_const('RUBY_PLATFORM','aarch64-darwin') }
28
+
29
+ it { expect(subject.macos?).to be(true) }
30
+ end
31
+
32
+ context "when RUBY_PLATFORM does not contain 'darwin'" do
33
+ before { stub_const('RUBY_PLATFORM','mswin') }
34
+
35
+ it { expect(subject.macos?).to be(false) }
36
+ end
37
+ end
38
+
39
+ describe "#windows?" do
40
+ it "must call Gem.win_platform?" do
41
+ expect(Gem).to receive(:win_platform?)
42
+
43
+ subject.windows?
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/pager'
3
+
4
+ require 'stringio'
5
+
6
+ describe Pager do
7
+ module TestPager
8
+ class TestCommand
9
+ include CommandKit::Pager
10
+ end
11
+ end
12
+
13
+ let(:command_class) { TestPager::TestCommand }
14
+ subject { command_class.new }
15
+
16
+ describe "#initialize" do
17
+ context "when the PAGER env variable is set" do
18
+ let(:pager) { 'foo' }
19
+
20
+ subject { command_class.new(env: {'PAGER' => pager}) }
21
+
22
+ it "must set @pager to the PAGER env variable" do
23
+ expect(subject.instance_variable_get('@pager')).to eq(pager)
24
+ end
25
+ end
26
+
27
+ context "when the PAGER env variable is not set" do
28
+ context "but the PATH env variable is" do
29
+ subject { command_class.new(env: {'PATH' => ENV['PATH']}) }
30
+
31
+ it "must search PATH for one of the pagers" do
32
+ expect(subject.instance_variable_get('@pager')).to eq("less -r")
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "#pager" do
39
+ context "when stdout is not a TTY" do
40
+ subject { command_class.new(stdout: StringIO.new) }
41
+
42
+ it "must yield stdout" do
43
+ expect { |b|
44
+ subject.pager(&b)
45
+ }.to yield_with_args(subject.stdout)
46
+ end
47
+ end
48
+
49
+ context "when @pager isn't initialized" do
50
+ before do
51
+ subject.instance_variable_set('@pager',nil)
52
+ end
53
+
54
+ it "must yield stdout" do
55
+ expect { |b|
56
+ subject.pager(&b)
57
+ }.to yield_with_args(subject.stdout)
58
+ end
59
+ end
60
+
61
+ context "when stdout is a TTY and @pager is initialized" do
62
+ let(:pager) { 'less -r' }
63
+
64
+ subject { command_class.new(env: {'PAGER' => pager}) }
65
+
66
+ let(:pager_io) { double('less') }
67
+ let(:pager_pid) { double('pid') }
68
+
69
+ before do
70
+ expect(IO).to receive(:popen).with(pager,'w').and_return(pager_io)
71
+ expect(pager_io).to receive(:pid).and_return(pager_pid)
72
+
73
+ expect(pager_io).to receive(:close)
74
+ expect(Process).to receive(:waitpid).with(pager_pid)
75
+ end
76
+
77
+ it "must spawn a new process and yield an IO object" do
78
+ skip "STDOUT is not a TTY" unless STDOUT.tty?
79
+
80
+ expect { |b|
81
+ subject.pager(&b)
82
+ }.to yield_with_args(pager_io)
83
+ end
84
+
85
+ context "when Errno::EPIPE is raised" do
86
+ it "must return gracefully" do
87
+ skip "STDOUT is not a TTY" unless STDOUT.tty?
88
+
89
+ expect {
90
+ subject.pager do
91
+ raise(Errno::EPIPE,"pipe broken")
92
+ end
93
+ }.to_not raise_error
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ describe "#print_or_page" do
100
+ let(:console_height) { 10 }
101
+
102
+ before do
103
+ expect(subject).to receive(:console_height).and_return(console_height)
104
+ end
105
+
106
+ context "when given a String" do
107
+ context "and the number of lines is less than the console's height" do
108
+ let(:string) { "foo#{$/}bar#{$/}" }
109
+
110
+ it "must puts the String to stdout" do
111
+ expect(subject.stdout).to receive(:puts).with(string)
112
+
113
+ subject.print_or_page(string)
114
+ end
115
+ end
116
+
117
+ context "and the number of lines is greater than the console's height" do
118
+ let(:string) { "foo#{$/}bar#{$/}" * console_height }
119
+
120
+ it "must spawn a pager and puts the String to the pager" do
121
+ pager = double('pager')
122
+ expect(subject).to receive(:pager).and_yield(pager)
123
+ expect(pager).to receive(:puts).with(string)
124
+
125
+ subject.print_or_page(string)
126
+ end
127
+ end
128
+ end
129
+
130
+ context "when given an Array" do
131
+ context "and the number of lines is less than the console's height" do
132
+ let(:array) { ['foo', 'bar'] }
133
+
134
+ it "must puts the Array of Strings to stdout" do
135
+ expect(subject.stdout).to receive(:puts).with(array)
136
+
137
+ subject.print_or_page(array)
138
+ end
139
+ end
140
+
141
+ context "and the number of lines is greater than the console's height" do
142
+ let(:array) { ['foo', 'bar'] * console_height }
143
+
144
+ it "must spawn a pager and puts the Array of Strings to the pager" do
145
+ pager = double('pager')
146
+ expect(subject).to receive(:pager).and_yield(pager)
147
+ expect(pager).to receive(:puts).with(array)
148
+
149
+ subject.print_or_page(array)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/printing/indent'
3
+
4
+ describe Printing::Indent do
5
+ module TestIndent
6
+ class TestCommand
7
+ include CommandKit::Printing::Indent
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestIndent::TestCommand }
12
+ subject { command_class.new }
13
+
14
+ describe "#initialize" do
15
+ it "must initialize @indent to 0" do
16
+ expect(subject.instance_variable_get('@indent')).to eq(0)
17
+ end
18
+
19
+ context "when the class has a superclass" do
20
+ module TestIndent
21
+ class TestSuperCommand
22
+
23
+ def initialize(var: 'default')
24
+ @var = var
25
+ end
26
+
27
+ end
28
+
29
+ class TestSubCommand < TestSuperCommand
30
+
31
+ include CommandKit::Printing::Indent
32
+
33
+ end
34
+ end
35
+
36
+ let(:command_class) { TestIndent::TestSubCommand }
37
+
38
+ it "must initialize @indent to 0" do
39
+ expect(subject.instance_variable_get('@indent')).to eq(0)
40
+ end
41
+
42
+ it "must call super()" do
43
+ expect(subject.instance_variable_get('@var')).to eq('default')
44
+ end
45
+
46
+ context "and additional keyword arguments are given" do
47
+ let(:var) { 'foo' }
48
+
49
+ subject { command_class.new(var: var) }
50
+
51
+ it "must call super() with the additional keyword arguments" do
52
+ expect(subject.instance_variable_get('@var')).to eq(var)
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "#indent" do
59
+ context "when no block is given" do
60
+ it "must return the indentation level" do
61
+ expect(subject.indent).to eq(0)
62
+ end
63
+ end
64
+
65
+ context "when a block is given" do
66
+ it do
67
+ expect { |b| subject.indent(&b) }.to yield_control
68
+ end
69
+
70
+ it "must increase the indent level by 2 then yield" do
71
+ expect(subject.indent).to eq(0)
72
+
73
+ subject.indent do
74
+ expect(subject.indent).to eq(2)
75
+ end
76
+ end
77
+
78
+ it "must restore the indententation level to it's original value" do
79
+ subject.indent do
80
+ expect(subject.indent).to eq(2)
81
+ end
82
+
83
+ expect(subject.indent).to eq(0)
84
+ end
85
+
86
+ context "when an exception occurrs within the given block" do
87
+ it "must not rescue the exception" do
88
+ expect {
89
+ subject.indent do
90
+ raise("error")
91
+ end
92
+ }.to raise_error(RuntimeError,"error")
93
+ end
94
+
95
+ it "must restore the indententation level to it's original value" do
96
+ begin
97
+ subject.indent do
98
+ expect(subject.indent).to eq(2)
99
+ raise("error")
100
+ end
101
+ rescue
102
+ end
103
+
104
+ expect(subject.indent).to eq(0)
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "#puts" do
111
+ let(:nl) { $/ }
112
+ let(:text) { 'hello world' }
113
+
114
+ context "when the indententation level is 0" do
115
+ it "must not add leading indentation" do
116
+ expect { subject.puts(text) }.to output("#{text}#{nl}").to_stdout
117
+ end
118
+ end
119
+
120
+ context "when the indententation level is > 0" do
121
+ it "must add a number of spaces to the text" do
122
+ expect {
123
+ subject.indent do
124
+ subject.puts(text)
125
+ end
126
+ }.to output(" #{text}#{nl}").to_stdout
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/printing'
3
+
4
+ require 'stringio'
5
+
6
+ describe Printing do
7
+ module TestPrinting
8
+ class TestCmd
9
+
10
+ include CommandKit::Printing
11
+
12
+ end
13
+ end
14
+
15
+ let(:command_class) { TestPrinting::TestCmd }
16
+ subject { command_class.new }
17
+
18
+ describe ".included" do
19
+ subject { command_class }
20
+
21
+ it { expect(command_class).to include(Stdio) }
22
+ end
23
+
24
+ let(:nl) { $/ }
25
+
26
+ describe "#print_error" do
27
+ let(:message) { "oh no!" }
28
+
29
+ it "must print a line to stderr" do
30
+ expect {
31
+ subject.print_error(message)
32
+ }.to output("#{message}#{nl}").to_stderr
33
+ end
34
+ end
35
+
36
+ describe "#print_exception" do
37
+ let(:message) { "error!" }
38
+ let(:backtrace) do
39
+ [
40
+ "/path/to/test1.rb:1 in `test1'",
41
+ "/path/to/test2.rb:2 in `test2'",
42
+ "/path/to/test3.rb:3 in `test3'",
43
+ "/path/to/test4.rb:4 in `test4'"
44
+ ]
45
+ end
46
+ let(:exception) do
47
+ error = RuntimeError.new(message)
48
+ error.set_backtrace(backtrace)
49
+ error
50
+ end
51
+
52
+ subject { command_class.new(stderr: StringIO.new) }
53
+
54
+ context "when stderr is a TTY" do
55
+ before { expect(subject.stderr).to receive(:tty?).and_return(true) }
56
+
57
+ it "must print a highlighted exception" do
58
+ subject.print_exception(exception)
59
+
60
+ expect(subject.stderr.string).to eq(
61
+ exception.full_message(highlight: true)
62
+ )
63
+ end
64
+ end
65
+
66
+ context "when stderr is not a TTY" do
67
+ it "must not highlight the exception" do
68
+ subject.print_exception(exception)
69
+
70
+ expect(subject.stderr.string).to eq(
71
+ exception.full_message(highlight: false)
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/program_name'
3
+
4
+ describe ProgramName do
5
+ module TestProgramName
6
+ class TestCmd
7
+ include CommandKit::ProgramName
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestProgramName::TestCmd }
12
+ let(:program_name) { 'foo' }
13
+
14
+ before do
15
+ @original_program_name = $PROGRAM_NAME
16
+ $PROGRAM_NAME = program_name
17
+ end
18
+
19
+ describe ".program_name" do
20
+ subject { command_class }
21
+
22
+ it "must return $PROGRAM_NAME" do
23
+ expect(subject.program_name).to eq(program_name)
24
+ end
25
+
26
+ context "when $PROGRAM_NAME is '-e'" do
27
+ let(:program_name) { '-e' }
28
+
29
+ it "must return nil" do
30
+ expect(subject.program_name).to be(nil)
31
+ end
32
+ end
33
+
34
+ context "when $PROGRAM_NAME is 'irb'" do
35
+ let(:program_name) { 'irb' }
36
+
37
+ it "must return nil" do
38
+ expect(subject.program_name).to be(nil)
39
+ end
40
+ end
41
+
42
+ context "when $PROGRAM_NAME is 'rspec'" do
43
+ let(:program_name) { 'rspec' }
44
+
45
+ it "must return nil" do
46
+ expect(subject.program_name).to be(nil)
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#program_name" do
52
+ subject { command_class.new }
53
+
54
+ it "should be the same as .program_name" do
55
+ expect(subject.program_name).to eq(command_class.program_name)
56
+ end
57
+ end
58
+
59
+ after do
60
+ $PROGRAM_NAME = @original_program_name
61
+ end
62
+ end