clive 0.8.1 → 1.0.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.
- data/LICENSE +1 -1
- data/README.md +328 -227
- data/lib/clive.rb +130 -50
- data/lib/clive/argument.rb +170 -0
- data/lib/clive/arguments.rb +139 -0
- data/lib/clive/arguments/parser.rb +210 -0
- data/lib/clive/base.rb +189 -0
- data/lib/clive/command.rb +342 -444
- data/lib/clive/error.rb +66 -0
- data/lib/clive/formatter.rb +57 -141
- data/lib/clive/formatter/colour.rb +37 -0
- data/lib/clive/formatter/plain.rb +172 -0
- data/lib/clive/option.rb +185 -75
- data/lib/clive/option/runner.rb +163 -0
- data/lib/clive/output.rb +141 -16
- data/lib/clive/parser.rb +180 -87
- data/lib/clive/struct_hash.rb +109 -0
- data/lib/clive/type.rb +117 -0
- data/lib/clive/type/definitions.rb +170 -0
- data/lib/clive/type/lookup.rb +23 -0
- data/lib/clive/version.rb +3 -3
- data/spec/clive/a_cli_spec.rb +245 -0
- data/spec/clive/argument_spec.rb +148 -0
- data/spec/clive/arguments/parser_spec.rb +35 -0
- data/spec/clive/arguments_spec.rb +191 -0
- data/spec/clive/command_spec.rb +276 -209
- data/spec/clive/formatter/colour_spec.rb +129 -0
- data/spec/clive/formatter/plain_spec.rb +129 -0
- data/spec/clive/option/runner_spec.rb +92 -0
- data/spec/clive/option_spec.rb +149 -23
- data/spec/clive/output_spec.rb +86 -2
- data/spec/clive/parser_spec.rb +201 -81
- data/spec/clive/struct_hash_spec.rb +82 -0
- data/spec/clive/type/definitions_spec.rb +312 -0
- data/spec/clive/type_spec.rb +107 -0
- data/spec/clive_spec.rb +60 -0
- data/spec/extras/expectations.rb +86 -0
- data/spec/extras/focus.rb +22 -0
- data/spec/helper.rb +35 -0
- metadata +56 -36
- data/lib/clive/bool.rb +0 -67
- data/lib/clive/exceptions.rb +0 -54
- data/lib/clive/flag.rb +0 -199
- data/lib/clive/switch.rb +0 -31
- data/lib/clive/tokens.rb +0 -141
- data/spec/clive/bool_spec.rb +0 -54
- data/spec/clive/flag_spec.rb +0 -117
- data/spec/clive/formatter_spec.rb +0 -108
- data/spec/clive/switch_spec.rb +0 -14
- data/spec/clive/tokens_spec.rb +0 -38
- data/spec/shared_specs.rb +0 -16
- data/spec/spec_helper.rb +0 -12
data/spec/clive/output_spec.rb
CHANGED
@@ -1,5 +1,89 @@
|
|
1
|
-
|
1
|
+
$: << File.dirname(__FILE__) + '/..'
|
2
|
+
require 'helper'
|
2
3
|
|
3
4
|
describe Clive::Output do
|
5
|
+
subject { Clive::Output }
|
6
|
+
|
7
|
+
describe '#pad' do
|
8
|
+
it 'pads a string to the right' do
|
9
|
+
subject.pad('a', 5, '-').must_equal 'a----'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'ignores colour codes' do
|
13
|
+
subject.pad('a'.red, 5,'-').must_equal 'a'.red + '----'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns the string if it exceeds the length' do
|
17
|
+
subject.pad('abcdef', 5, '-').must_equal 'abcdef'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#wrap_text' do
|
22
|
+
let(:text) { "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." }
|
23
|
+
|
24
|
+
let :result do
|
25
|
+
"Lorem ipsum dolor
|
26
|
+
sit amet,
|
27
|
+
consectetur
|
28
|
+
adipisicing elit,
|
29
|
+
sed do eiusmod
|
30
|
+
tempor incididunt ut
|
31
|
+
labore et dolore
|
32
|
+
magna aliqua."
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'wraps the text' do
|
36
|
+
subject.wrap_text(text, 6, 26).must_equal result
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#terminal_width' do
|
41
|
+
it 'returns the width of the terminal' do
|
42
|
+
ENV['COLUMNS'] = '80'
|
43
|
+
subject.terminal_width.must_equal 80
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
4
48
|
|
5
|
-
|
49
|
+
describe String do
|
50
|
+
|
51
|
+
describe '#colour' do
|
52
|
+
it 'adds the correct escape codes' do
|
53
|
+
"str".colour(5).must_equal "\e[5mstr\e[0m"
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'adds multiple codes' do
|
57
|
+
"str".l_red.bold.green_bg.blink.underline.must_equal "\e[4m\e[5m\e[42m\e[1m\e[91mstr\e[0m"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#clear_colours' do
|
62
|
+
it 'removes all colour codes' do
|
63
|
+
"str".red.blue_bg.bold.blink.clear_colours.must_equal "str"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#colour!' do
|
68
|
+
it 'adds the correct escape codes' do
|
69
|
+
s = "str"
|
70
|
+
s.colour!(5)
|
71
|
+
s.must_equal "\e[5mstr\e[0m"
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'adds multiple codes' do
|
75
|
+
s = "str"
|
76
|
+
s.l_red!.bold!.green_bg!.blink!.underline!
|
77
|
+
s.must_equal "\e[4m\e[5m\e[42m\e[1m\e[91mstr\e[0m"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#clear_colours!' do
|
82
|
+
it 'removes all colours' do
|
83
|
+
s = "str".red.green_bg.bold.blink
|
84
|
+
s.clear_colours!
|
85
|
+
s.must_equal "str"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
data/spec/clive/parser_spec.rb
CHANGED
@@ -1,106 +1,226 @@
|
|
1
|
-
|
1
|
+
$: << File.dirname(__FILE__) + '/..'
|
2
|
+
require 'helper'
|
2
3
|
|
3
4
|
describe Clive::Parser do
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
describe 'help command' do
|
7
|
+
subject { Clive.new(:name => '...') { command(:new) { opt :f, :force } } }
|
8
|
+
|
9
|
+
describe 'with no argument' do
|
10
|
+
it 'prints the help' do
|
11
|
+
this {
|
12
|
+
subject.run s 'help'
|
13
|
+
}.must_output <<EOS
|
14
|
+
Usage: ... [command] [options]
|
15
|
+
|
16
|
+
Commands:
|
17
|
+
new
|
18
|
+
help [<command>] \e[90m# \e[0m\e[90mDisplay help\e[0m
|
19
|
+
|
20
|
+
Options:
|
21
|
+
-h, --help \e[90m# \e[0m\e[90mDisplay this help message\e[0m
|
22
|
+
EOS
|
23
|
+
end
|
19
24
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
25
|
+
|
26
|
+
describe 'with an argument' do
|
27
|
+
it 'prints help for that command' do
|
28
|
+
this {
|
29
|
+
subject.run s 'help new'
|
30
|
+
}.must_output <<EOS
|
31
|
+
Usage: ... new [options]
|
32
|
+
|
33
|
+
Options:
|
34
|
+
-f, --force
|
35
|
+
-h, --help \e[90m# \e[0m\e[90mDisplay this help message\e[0m
|
36
|
+
EOS
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'prints an error if command does not exist' do
|
40
|
+
this {
|
41
|
+
subject.run s 'help missing'
|
42
|
+
}.must_output "Error: command missing could not be found. Try `help` to see the available commands.\n"
|
43
|
+
end
|
26
44
|
end
|
45
|
+
|
27
46
|
end
|
28
|
-
|
29
|
-
describe
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
}
|
47
|
+
|
48
|
+
describe 'single option' do
|
49
|
+
subject {
|
50
|
+
Clive.new {
|
51
|
+
opt :force
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
it 'runs the option' do
|
56
|
+
r = subject.run s '--force'
|
57
|
+
r.must_have :force
|
34
58
|
end
|
35
59
|
end
|
36
|
-
|
37
|
-
describe
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
}
|
60
|
+
|
61
|
+
describe 'single option with block' do
|
62
|
+
subject {
|
63
|
+
Clive.new {
|
64
|
+
opt(:force) { print "forced" }
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
it 'runs the option' do
|
69
|
+
this {
|
70
|
+
subject.run s '--force'
|
71
|
+
}.must_output "forced"
|
42
72
|
end
|
43
73
|
end
|
44
74
|
|
45
|
-
describe
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
}
|
75
|
+
describe 'single option with argument' do
|
76
|
+
subject {
|
77
|
+
Clive.new {
|
78
|
+
opt :name, :arg => '<name>'
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
it 'runs the option' do
|
83
|
+
r = subject.run s '--name "John Doe"'
|
84
|
+
r.name.must_equal 'John Doe'
|
50
85
|
end
|
51
86
|
end
|
52
|
-
|
53
|
-
describe
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
}
|
87
|
+
|
88
|
+
describe 'single option with argument with block' do
|
89
|
+
subject {
|
90
|
+
Clive.new {
|
91
|
+
opt(:name, :arg => '<name>') {|name| print "I am #{name}" }
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
it 'runs the option' do
|
96
|
+
this {
|
97
|
+
subject.run s '--name "John Doe"'
|
98
|
+
}.must_output "I am John Doe"
|
58
99
|
end
|
59
100
|
end
|
60
|
-
|
61
|
-
describe
|
62
|
-
|
63
|
-
|
64
|
-
|
101
|
+
|
102
|
+
describe 'single option with arguments' do
|
103
|
+
subject {
|
104
|
+
Clive.new {
|
105
|
+
opt :name, :arg => '<first> <last>'
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
it 'runs the option' do
|
110
|
+
r = subject.run s '--name John Doe'
|
111
|
+
r.name.must_equal ['John', 'Doe']
|
65
112
|
end
|
66
113
|
end
|
67
|
-
|
68
|
-
describe
|
69
|
-
|
70
|
-
|
71
|
-
|
114
|
+
|
115
|
+
describe 'single option with arguments with block' do
|
116
|
+
subject {
|
117
|
+
Clive.new {
|
118
|
+
opt(:name, :arg => '<first> <last>') { print "I am #{first} #{last}" }
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
it 'runs the option' do
|
123
|
+
this {
|
124
|
+
subject.run s '--name John Doe'
|
125
|
+
}.must_output "I am John Doe"
|
72
126
|
end
|
73
127
|
end
|
74
|
-
|
75
|
-
describe
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
subject.
|
85
|
-
|
86
|
-
|
87
|
-
it "sets a default value" do
|
88
|
-
subject.test.should == 1
|
128
|
+
|
129
|
+
describe 'Boolean options' do
|
130
|
+
subject {
|
131
|
+
Clive.new {
|
132
|
+
bool :force
|
133
|
+
bool :auto
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
it 'runs the options' do
|
138
|
+
r = subject.run s '--no-force --auto'
|
139
|
+
r.wont_have :force
|
140
|
+
r.must_have :auto
|
89
141
|
end
|
90
142
|
end
|
91
|
-
|
92
|
-
describe
|
93
|
-
|
94
|
-
subject
|
95
|
-
|
143
|
+
|
144
|
+
describe 'Commands' do
|
145
|
+
describe 'with options and arguments' do
|
146
|
+
subject {
|
147
|
+
Clive.new {
|
148
|
+
command :new, :args => '<dir>' do
|
149
|
+
bool :force
|
150
|
+
opt :name, :arg => '<name>'
|
151
|
+
end
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
let(:result) { {:new => {:args => '~/somewhere', :force => true, :name => 'New'}} }
|
156
|
+
|
157
|
+
it 'runs properly with arguments after options' do
|
158
|
+
r = subject.run s 'new --force --name New ~/somewhere'
|
159
|
+
r.must_equal result
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'runs properly with options after arguments' do
|
163
|
+
r = subject.run s 'new ~/somewhere --force --name New'
|
164
|
+
r.must_equal result
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'runs properly with arguments between options' do
|
168
|
+
r = subject.run s 'new --force ~/somewhere --name New'
|
169
|
+
r2 = subject.run s 'new --name New ~/somewhere --force'
|
170
|
+
r2.must_equal r
|
171
|
+
r2.args.must_equal r.args
|
172
|
+
r.must_equal result
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'runs properly with excessive arguemnts' do
|
176
|
+
r = subject.run s 'new ~/somewhere Hello Other'
|
177
|
+
r.new.args.must_equal '~/somewhere'
|
178
|
+
r.args.must_equal ['Hello', 'Other']
|
179
|
+
end
|
96
180
|
end
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
181
|
+
|
182
|
+
describe 'with options and optional arguments' do
|
183
|
+
subject {
|
184
|
+
Clive.new {
|
185
|
+
command :new, :args => '[<dir>]' do
|
186
|
+
bool :force
|
187
|
+
opt :name, :arg => '<name>'
|
188
|
+
end
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
let(:result) { {:new => {:force => true, :name => 'New', :args => nil}} }
|
193
|
+
let(:with_argument) { {:new => {:force => true, :name => 'New', :args => '~/somewhere'}} }
|
194
|
+
|
195
|
+
it 'runs properly with just options' do
|
196
|
+
r = subject.run s 'new --force --name New'
|
197
|
+
r.must_equal result
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'runs properly with arguments after options' do
|
201
|
+
r = subject.run s 'new --force --name New ~/somewhere'
|
202
|
+
r.must_equal with_argument
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'runs properly with options after arguments' do
|
206
|
+
r = subject.run s 'new ~/somewhere --force --name New'
|
207
|
+
r.must_equal with_argument
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'runs properly with arguments between options' do
|
211
|
+
r = subject.run s 'new --force ~/somewhere --name New'
|
212
|
+
r2 = subject.run s 'new --name New ~/somewhere --force'
|
213
|
+
r2.must_equal r
|
214
|
+
r2.args.must_equal r.args
|
215
|
+
r.must_equal with_argument
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'runs properly with excessive arguemnts' do
|
219
|
+
r = subject.run s 'new ~/somewhere Hello Other'
|
220
|
+
r[:new][:args].must_equal '~/somewhere'
|
221
|
+
r.args.must_equal ['Hello', 'Other']
|
222
|
+
end
|
103
223
|
end
|
104
224
|
end
|
105
225
|
|
106
|
-
end
|
226
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + '/..'
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
describe Clive::StructHash do
|
5
|
+
|
6
|
+
subject { Clive::StructHash }
|
7
|
+
|
8
|
+
describe '#[]' do
|
9
|
+
it 'gets the value' do
|
10
|
+
sh = subject.new(:a => 1)
|
11
|
+
sh[:a].must_equal 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#store' do
|
16
|
+
it 'stores a key' do
|
17
|
+
sh = subject.new
|
18
|
+
sh.store :life, 42
|
19
|
+
sh.life.must_equal 42
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'stores multiple keys' do
|
23
|
+
sh = subject.new
|
24
|
+
sh.store %w(life answer), 42
|
25
|
+
sh.life.must_equal 42
|
26
|
+
sh.answer.must_equal 42
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'named getter methods' do
|
31
|
+
it 'gets the value' do
|
32
|
+
sh = subject.new(:L => 42, :life => 42)
|
33
|
+
sh.life.must_equal 42
|
34
|
+
sh.L.must_equal 42
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'named predicate methods' do
|
39
|
+
it 'is true if key exists' do
|
40
|
+
subject.new(:answer => 42).must_have :answer?
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'is false if key does not exist' do
|
44
|
+
subject.new(:answer => 42).wont_have :question?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#to_struct' do
|
49
|
+
it 'uses the first names given to store' do
|
50
|
+
sh = subject.new
|
51
|
+
sub = subject.new(:life => 42)
|
52
|
+
|
53
|
+
sh.store %w(admin a), true
|
54
|
+
sh.store %w(name n), ['John', 'Doe']
|
55
|
+
sh.store %w(sub other command), sub
|
56
|
+
|
57
|
+
test = sh.to_struct('Person')
|
58
|
+
test.admin.must_equal true
|
59
|
+
test.wont_respond_to :a
|
60
|
+
test.name.must_equal ['John', 'Doe']
|
61
|
+
test.wont_respond_to :n
|
62
|
+
test.sub.must_equal sub
|
63
|
+
test.wont_respond_to :other
|
64
|
+
test.wont_respond_to :command
|
65
|
+
test.sub.class.must_equal subject
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#to_h' do
|
70
|
+
it 'uses the first names given to store' do
|
71
|
+
sh = subject.new
|
72
|
+
sub = subject.new(:life => 42)
|
73
|
+
|
74
|
+
sh.store %w(admin a), true
|
75
|
+
sh.store %w(name n), ['John', 'Doe']
|
76
|
+
sh.store %w(sub other command), sub
|
77
|
+
|
78
|
+
sh.to_h.must_equal :admin => true, :name => ['John', 'Doe'], :sub => {:life => 42}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|