command_kit 0.1.0.pre1

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 +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