command_kit 0.1.0.pre1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +15 -0
  3. data/.rubocop.yml +138 -0
  4. data/ChangeLog.md +34 -2
  5. data/Gemfile +3 -0
  6. data/README.md +135 -214
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/colors.rb +30 -0
  10. data/examples/command.rb +65 -0
  11. data/examples/pager.rb +30 -0
  12. data/gemspec.yml +10 -2
  13. data/lib/command_kit/arguments/argument.rb +16 -44
  14. data/lib/command_kit/arguments/argument_value.rb +3 -30
  15. data/lib/command_kit/arguments.rb +66 -20
  16. data/lib/command_kit/colors.rb +253 -45
  17. data/lib/command_kit/command.rb +50 -3
  18. data/lib/command_kit/command_name.rb +9 -0
  19. data/lib/command_kit/commands/auto_load/subcommand.rb +3 -0
  20. data/lib/command_kit/commands/auto_load.rb +16 -0
  21. data/lib/command_kit/commands/auto_require.rb +16 -0
  22. data/lib/command_kit/commands/command.rb +3 -0
  23. data/lib/command_kit/commands/help.rb +2 -0
  24. data/lib/command_kit/commands/parent_command.rb +7 -0
  25. data/lib/command_kit/commands/subcommand.rb +15 -0
  26. data/lib/command_kit/commands.rb +40 -4
  27. data/lib/command_kit/description.rb +15 -2
  28. data/lib/command_kit/env/home.rb +9 -0
  29. data/lib/command_kit/env/path.rb +15 -0
  30. data/lib/command_kit/env.rb +4 -0
  31. data/lib/command_kit/examples.rb +15 -2
  32. data/lib/command_kit/exception_handler.rb +4 -0
  33. data/lib/command_kit/help/man.rb +74 -47
  34. data/lib/command_kit/help.rb +10 -1
  35. data/lib/command_kit/inflector.rb +49 -17
  36. data/lib/command_kit/interactive.rb +239 -0
  37. data/lib/command_kit/main.rb +20 -9
  38. data/lib/command_kit/man.rb +44 -0
  39. data/lib/command_kit/open_app.rb +69 -0
  40. data/lib/command_kit/options/option.rb +36 -9
  41. data/lib/command_kit/options/option_value.rb +42 -3
  42. data/lib/command_kit/options/parser.rb +44 -17
  43. data/lib/command_kit/options/quiet.rb +3 -0
  44. data/lib/command_kit/options/verbose.rb +5 -0
  45. data/lib/command_kit/options/version.rb +6 -0
  46. data/lib/command_kit/options.rb +59 -10
  47. data/lib/command_kit/os/linux.rb +157 -0
  48. data/lib/command_kit/os.rb +165 -11
  49. data/lib/command_kit/package_manager.rb +200 -0
  50. data/lib/command_kit/pager.rb +84 -9
  51. data/lib/command_kit/printing/indent.rb +25 -2
  52. data/lib/command_kit/printing.rb +23 -0
  53. data/lib/command_kit/program_name.rb +7 -0
  54. data/lib/command_kit/stdio.rb +24 -0
  55. data/lib/command_kit/sudo.rb +40 -0
  56. data/lib/command_kit/terminal.rb +159 -0
  57. data/lib/command_kit/usage.rb +14 -0
  58. data/lib/command_kit/version.rb +1 -1
  59. data/lib/command_kit/xdg.rb +21 -1
  60. data/lib/command_kit.rb +1 -0
  61. data/spec/arguments/argument_spec.rb +5 -41
  62. data/spec/arguments/argument_value_spec.rb +1 -61
  63. data/spec/arguments_spec.rb +8 -25
  64. data/spec/colors_spec.rb +277 -13
  65. data/spec/command_name_spec.rb +1 -1
  66. data/spec/command_spec.rb +4 -1
  67. data/spec/commands/auto_load/subcommand_spec.rb +1 -1
  68. data/spec/commands/auto_load_spec.rb +1 -1
  69. data/spec/commands/auto_require_spec.rb +2 -2
  70. data/spec/commands/help_spec.rb +1 -1
  71. data/spec/commands/parent_command_spec.rb +1 -1
  72. data/spec/commands/subcommand_spec.rb +1 -1
  73. data/spec/commands_spec.rb +2 -2
  74. data/spec/description_spec.rb +1 -25
  75. data/spec/env/home_spec.rb +1 -1
  76. data/spec/env/path_spec.rb +1 -1
  77. data/spec/examples_spec.rb +1 -25
  78. data/spec/exception_handler_spec.rb +1 -1
  79. data/spec/help/man_spec.rb +316 -0
  80. data/spec/help_spec.rb +0 -25
  81. data/spec/inflector_spec.rb +71 -9
  82. data/spec/interactive_spec.rb +415 -0
  83. data/spec/main_spec.rb +7 -7
  84. data/spec/man_spec.rb +46 -0
  85. data/spec/open_app_spec.rb +85 -0
  86. data/spec/options/option_spec.rb +48 -9
  87. data/spec/options/option_value_spec.rb +53 -4
  88. data/spec/options_spec.rb +1 -1
  89. data/spec/os/linux_spec.rb +154 -0
  90. data/spec/os_spec.rb +201 -14
  91. data/spec/package_manager_spec.rb +806 -0
  92. data/spec/pager_spec.rb +78 -15
  93. data/spec/printing/indent_spec.rb +1 -1
  94. data/spec/printing_spec.rb +10 -2
  95. data/spec/program_name_spec.rb +1 -1
  96. data/spec/spec_helper.rb +0 -3
  97. data/spec/sudo_spec.rb +51 -0
  98. data/spec/{console_spec.rb → terminal_spec.rb} +65 -35
  99. data/spec/usage_spec.rb +2 -2
  100. data/spec/xdg_spec.rb +1 -1
  101. metadata +32 -13
  102. data/lib/command_kit/arguments/usage.rb +0 -6
  103. data/lib/command_kit/console.rb +0 -141
  104. data/lib/command_kit/options/usage.rb +0 -6
data/spec/main_spec.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/main'
3
3
 
4
- describe Main do
4
+ describe CommandKit::Main do
5
5
  module TestMain
6
6
  class TestCommand
7
7
  include CommandKit::Main
@@ -156,16 +156,16 @@ describe Main do
156
156
  describe "#main" do
157
157
  subject { command_class.new }
158
158
 
159
- it "must provide a default #main" do
160
- expect(subject).to respond_to(:main)
161
- end
162
-
163
159
  it "must return 0 by default" do
164
160
  expect(subject.main).to eq(0)
165
161
  end
166
162
 
167
- it "must accept arbitrary argv" do
168
- expect { subject.main([1,2,3]) }.to_not raise_error
163
+ it "must call #run with the given argv" do
164
+ argv = ["foo", "bar", "baz"]
165
+
166
+ expect(subject).to receive(:run).with(*argv)
167
+
168
+ subject.main(argv)
169
169
  end
170
170
 
171
171
  context "when #run raises SystemExit" do
data/spec/man_spec.rb ADDED
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/man'
3
+
4
+ describe CommandKit::Man do
5
+ module TestMan
6
+ class TestCommand
7
+ include CommandKit::Man
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestMan::TestCommand }
12
+
13
+ subject { command_class.new }
14
+
15
+ describe "#man" do
16
+ let(:man_page) { 'foo' }
17
+
18
+ it "must call system() with the given man page" do
19
+ expect(subject).to receive(:system).with('man',man_page)
20
+
21
+ subject.man(man_page)
22
+ end
23
+
24
+ context "when given a non-String man-page argument" do
25
+ let(:man_page_arg) { double(:non_string_arg) }
26
+
27
+ it "must call #to_s on the man-page argument" do
28
+ expect(man_page_arg).to receive(:to_s).and_return(man_page)
29
+
30
+ expect(subject).to receive(:system).with('man',man_page)
31
+
32
+ subject.man(man_page_arg)
33
+ end
34
+ end
35
+
36
+ context "when given the section: keyword argument" do
37
+ let(:section) { 7 }
38
+
39
+ it "must call system() with the given section number and man page" do
40
+ expect(subject).to receive(:system).with('man',section.to_s,man_page)
41
+
42
+ subject.man(man_page, section: section)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/open_app'
3
+
4
+ describe CommandKit::OpenApp do
5
+ module TestOpenApp
6
+ class TestCommand
7
+ include CommandKit::OpenApp
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestOpenApp::TestCommand }
12
+
13
+ subject { command_class.new }
14
+
15
+ describe "#initialize" do
16
+ context "when the OS is macOS" do
17
+ subject { command_class.new(os: :macos) }
18
+
19
+ it "must set @open_command to \"open\"" do
20
+ expect(subject.instance_variable_get("@open_command")).to eq("open")
21
+ end
22
+ end
23
+
24
+ context "when the OS is Linux" do
25
+ subject { command_class.new(os: :linux) }
26
+
27
+ it "must set @open_command to \"xdg-open\"" do
28
+ expect(subject.instance_variable_get("@open_command")).to eq("xdg-open")
29
+ end
30
+ end
31
+
32
+ context "when the OS is FreeBSD" do
33
+ subject { command_class.new(os: :freebsd) }
34
+
35
+ it "must set @open_command to \"xdg-open\"" do
36
+ expect(subject.instance_variable_get("@open_command")).to eq("xdg-open")
37
+ end
38
+ end
39
+
40
+ context "when the OS is OpenBSD" do
41
+ subject { command_class.new(os: :openbsd) }
42
+
43
+ it "must set @open_command to \"xdg-open\"" do
44
+ expect(subject.instance_variable_get("@open_command")).to eq("xdg-open")
45
+ end
46
+ end
47
+
48
+ context "when the OS is NetBSD" do
49
+ subject { command_class.new(os: :openbsd) }
50
+
51
+ it "must set @open_command to \"xdg-open\"" do
52
+ expect(subject.instance_variable_get("@open_command")).to eq("xdg-open")
53
+ end
54
+ end
55
+
56
+ context "when the OS is Windows" do
57
+ subject { command_class.new(os: :windows) }
58
+
59
+ it "must set @open_command to \"start\"" do
60
+ expect(subject.instance_variable_get("@open_command")).to eq("start")
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "#open_app_for" do
66
+ context "when @open_command is set" do
67
+ let(:file_or_uri) { "foo" }
68
+ let(:status) { true }
69
+
70
+ it "must execute the @open_command with the given URI or file" do
71
+ expect(subject).to receive(:system).with(subject.instance_variable_get("@open_command"),file_or_uri).and_return(status)
72
+
73
+ expect(subject.open_app_for(file_or_uri)).to be(status)
74
+ end
75
+ end
76
+
77
+ context "when @open_command is not set" do
78
+ before do
79
+ subject.instance_variable_set("@open_command",nil)
80
+ end
81
+
82
+ it { expect(subject.open_app_for("foo")).to be(nil) }
83
+ end
84
+ end
85
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/options/option'
3
3
 
4
- describe Options::Option do
4
+ describe CommandKit::Options::Option do
5
5
  let(:name) { :foo }
6
6
  let(:short) { nil }
7
7
  let(:long) { '--foo' }
@@ -18,12 +18,12 @@ describe Options::Option do
18
18
  end
19
19
 
20
20
  subject do
21
- described_class.new name, short: short,
21
+ described_class.new(name, short: short,
22
22
  long: long,
23
23
  equals: equals,
24
24
  desc: desc,
25
25
  value: value,
26
- &block
26
+ &block)
27
27
  end
28
28
 
29
29
  describe "#initialize" do
@@ -65,22 +65,61 @@ describe Options::Option do
65
65
  subject { described_class.new(name, equals: equals, desc: desc) }
66
66
 
67
67
  it "must set #equals" do
68
- expect(subject.equals).to eq(equals)
68
+ expect(subject.equals?).to eq(equals)
69
69
  end
70
70
  end
71
71
 
72
72
  context "when the equals: keyword is not given" do
73
73
  subject { described_class.new(name, desc: desc) }
74
74
 
75
- it { expect(subject.equals).to be(false) }
75
+ it { expect(subject.equals?).to be(false) }
76
76
  end
77
77
 
78
78
  context "when the values: keyword is given" do
79
- let(:value) { {} }
80
- subject { described_class.new(name, value: {}, desc: desc) }
79
+ context "and it is a Hash" do
80
+ let(:type) { Integer }
81
+
82
+ subject { described_class.new(name, value: {type: type}, desc: desc) }
83
+
84
+ it "must initialize #value" do
85
+ expect(subject.value).to be_kind_of(CommandKit::Options::OptionValue)
86
+ expect(subject.value.type).to eq(type)
87
+ end
88
+ end
89
+
90
+ context "and it is true" do
91
+ subject { described_class.new(name, value: true, desc: desc) }
92
+
93
+ it "must initialize #value with defaults" do
94
+ expect(subject.value).to be_kind_of(CommandKit::Options::OptionValue)
95
+ expect(subject.value.type).to eq(String)
96
+ expect(subject.value.required?).to be(true)
97
+ expect(subject.value.usage).to eq('STR')
98
+ end
99
+ end
81
100
 
82
- it "must initialize #value" do
83
- expect(subject.value).to be_kind_of(Options::OptionValue)
101
+ context "and it is false" do
102
+ subject { described_class.new(name, value: false, desc: desc) }
103
+
104
+ it "must not initialize #value" do
105
+ expect(subject.value).to be(nil)
106
+ end
107
+ end
108
+
109
+ context "and it is nil" do
110
+ subject { described_class.new(name, value: nil, desc: desc) }
111
+
112
+ it "must not initialize #value" do
113
+ expect(subject.value).to be(nil)
114
+ end
115
+ end
116
+
117
+ context "when it is another type" do
118
+ it do
119
+ expect {
120
+ described_class.new(name, value: 'foo', desc: desc)
121
+ }.to raise_error(TypeError)
122
+ end
84
123
  end
85
124
  end
86
125
 
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/options/option_value'
3
3
 
4
- describe Options::OptionValue do
5
- let(:type) { Integer }
4
+ describe CommandKit::Options::OptionValue do
5
+ let(:type) { Integer }
6
6
  let(:usage) { 'COUNT' }
7
7
  let(:required) { true }
8
8
  let(:default) { 1 }
@@ -26,6 +26,22 @@ describe Options::OptionValue do
26
26
  end
27
27
  end
28
28
 
29
+ context "when the default: keyword is given" do
30
+ subject { described_class.new(default: default) }
31
+
32
+ it "must set #default" do
33
+ expect(subject.default).to eq(default)
34
+ end
35
+ end
36
+
37
+ context "when the default: keyword is not given" do
38
+ subject { described_class.new() }
39
+
40
+ it "default #default to String" do
41
+ expect(subject.default).to eq(nil)
42
+ end
43
+ end
44
+
29
45
  context "when the usage: keyword is given" do
30
46
  let(:usage) { 'FOO' }
31
47
 
@@ -45,11 +61,20 @@ describe Options::OptionValue do
45
61
  end
46
62
  end
47
63
 
64
+ subject do
65
+ described_class.new(
66
+ type: type,
67
+ usage: usage,
68
+ required: required,
69
+ default: default
70
+ )
71
+ end
72
+
48
73
  describe "#usage" do
49
74
  let(:usage) { 'FOO' }
50
75
 
51
76
  context "when #optional? is true" do
52
- subject { described_class.new(usage: usage, required: false) }
77
+ let(:required) { false }
53
78
 
54
79
  it "must wrap the usage within [ ] brackets" do
55
80
  expect(subject.usage).to eq("[#{usage}]")
@@ -57,11 +82,35 @@ describe Options::OptionValue do
57
82
  end
58
83
 
59
84
  context "when #optional? is false" do
60
- subject { described_class.new(usage: usage, required: true) }
85
+ let(:required) { true }
61
86
 
62
87
  it "should return the usage string unchanged" do
63
88
  expect(subject.usage).to eq(usage)
64
89
  end
65
90
  end
66
91
  end
92
+
93
+ describe "#default_value" do
94
+ context "when initialized with a default: that responded to #call" do
95
+ let(:default) do
96
+ ->{ [] }
97
+ end
98
+
99
+ it "must call the default #call method" do
100
+ expect(subject.default_value).to eq(default.call)
101
+ end
102
+ end
103
+
104
+ context "when initialized with a default: that it's an Object" do
105
+ let(:default) { "" }
106
+
107
+ it "must return the default: object" do
108
+ expect(subject.default_value).to eq(default)
109
+ end
110
+
111
+ it "must duplicate the default: object each time" do
112
+ expect(subject.default_value).to_not be(subject.default_value)
113
+ end
114
+ end
115
+ end
67
116
  end
data/spec/options_spec.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'command_kit/options'
3
3
 
4
- describe Options do
4
+ describe CommandKit::Options do
5
5
  module TestOptions
6
6
  class ImplicitCmd
7
7
  include CommandKit::Options
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/os/linux'
3
+
4
+ describe CommandKit::OS::Linux do
5
+ module TestOSLinux
6
+ class TestCommand
7
+ include CommandKit::OS::Linux
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestOSLinux::TestCommand }
12
+
13
+ describe ".linux_distro" do
14
+ subject { command_class }
15
+
16
+ context "and the /etc/fedora-release file exists" do
17
+ before do
18
+ expect(File).to receive(:file?).with("/etc/fedora-release").and_return(true)
19
+ end
20
+
21
+ it { expect(subject.linux_distro).to be(:fedora) }
22
+ end
23
+
24
+ context "and the /etc/redhat-release file exists" do
25
+ before do
26
+ allow(File).to receive(:file?).with("/etc/fedora-release").and_return(false)
27
+ expect(File).to receive(:file?).with("/etc/redhat-release").and_return(true)
28
+ end
29
+
30
+ it { expect(subject.linux_distro).to be(:redhat) }
31
+ end
32
+
33
+ context "and the /etc/debian_version file exists" do
34
+ before do
35
+ allow(File).to receive(:file?).with("/etc/fedora-release").and_return(false)
36
+ allow(File).to receive(:file?).with("/etc/redhat-release").and_return(false)
37
+ expect(File).to receive(:file?).with("/etc/debian_version").and_return(true)
38
+ end
39
+
40
+ it { expect(subject.linux_distro).to be(:debian) }
41
+ end
42
+
43
+ context "and the /etc/SuSE-release file exists" do
44
+ before do
45
+ allow(File).to receive(:file?).with("/etc/fedora-release").and_return(false)
46
+ allow(File).to receive(:file?).with("/etc/redhat-release").and_return(false)
47
+ allow(File).to receive(:file?).with("/etc/debian_version").and_return(false)
48
+ expect(File).to receive(:file?).with("/etc/SuSE-release").and_return(true)
49
+ end
50
+
51
+ it { expect(subject.linux_distro).to be(:suse) }
52
+ end
53
+
54
+ context "and the /etc/arch-release file exists" do
55
+ before do
56
+ allow(File).to receive(:file?).with("/etc/fedora-release").and_return(false)
57
+ allow(File).to receive(:file?).with("/etc/redhat-release").and_return(false)
58
+ allow(File).to receive(:file?).with("/etc/debian_version").and_return(false)
59
+ allow(File).to receive(:file?).with("/etc/SuSE-release").and_return(false)
60
+ expect(File).to receive(:file?).with("/etc/arch-release").and_return(true)
61
+ end
62
+
63
+ it { expect(subject.linux_distro).to be(:arch) }
64
+ end
65
+ end
66
+
67
+ subject { command_class.new }
68
+
69
+ describe "#initialize" do
70
+ it "must default #linux_distro to self.class.linux_distro" do
71
+ expect(subject.linux_distro).to eq(command_class.linux_distro)
72
+ end
73
+
74
+ context "when the linux_distro: keyword is given" do
75
+ let(:linux_distro) { :suse }
76
+
77
+ subject { command_class.new(linux_distro: linux_distro) }
78
+
79
+ it "must set #linux_distro" do
80
+ expect(subject.linux_distro).to eq(linux_distro)
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "#redhat_linux?" do
86
+ context "when #linux_distro is :redhat" do
87
+ subject { command_class.new(linux_distro: :redhat) }
88
+
89
+ it { expect(subject.redhat_linux?).to be(true) }
90
+ end
91
+
92
+ context "when #linux_distro is not :redhat" do
93
+ subject { command_class.new(linux_distro: :debian) }
94
+
95
+ it { expect(subject.redhat_linux?).to be(false) }
96
+ end
97
+ end
98
+
99
+ describe "#fedora_linux?" do
100
+ context "when #linux_distro is :fedora" do
101
+ subject { command_class.new(linux_distro: :fedora) }
102
+
103
+ it { expect(subject.fedora_linux?).to be(true) }
104
+ end
105
+
106
+ context "when #linux_distro is not :fedora" do
107
+ subject { command_class.new(linux_distro: :debian) }
108
+
109
+ it { expect(subject.fedora_linux?).to be(false) }
110
+ end
111
+ end
112
+
113
+ describe "#debian_linux?" do
114
+ context "when #linux_distro is :debian" do
115
+ subject { command_class.new(linux_distro: :debian) }
116
+
117
+ it { expect(subject.debian_linux?).to be(true) }
118
+ end
119
+
120
+ context "when #linux_distro is not :fedora" do
121
+ subject { command_class.new(linux_distro: :redhat) }
122
+
123
+ it { expect(subject.debian_linux?).to be(false) }
124
+ end
125
+ end
126
+
127
+ describe "#suse_linux?" do
128
+ context "when #linux_distro is :suse" do
129
+ subject { command_class.new(linux_distro: :suse) }
130
+
131
+ it { expect(subject.suse_linux?).to be(true) }
132
+ end
133
+
134
+ context "when #linux_distro is not :suse" do
135
+ subject { command_class.new(linux_distro: :debian) }
136
+
137
+ it { expect(subject.suse_linux?).to be(false) }
138
+ end
139
+ end
140
+
141
+ describe "#arch_linux?" do
142
+ context "when #linux_distro is :arch" do
143
+ subject { command_class.new(linux_distro: :arch) }
144
+
145
+ it { expect(subject.arch_linux?).to be(true) }
146
+ end
147
+
148
+ context "when #linux_distro is not :arch" do
149
+ subject { command_class.new(linux_distro: :debian) }
150
+
151
+ it { expect(subject.arch_linux?).to be(false) }
152
+ end
153
+ end
154
+ end