command_kit 0.1.0.pre1 → 0.2.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 (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