clive 0.8.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +328 -227
  3. data/lib/clive.rb +130 -50
  4. data/lib/clive/argument.rb +170 -0
  5. data/lib/clive/arguments.rb +139 -0
  6. data/lib/clive/arguments/parser.rb +210 -0
  7. data/lib/clive/base.rb +189 -0
  8. data/lib/clive/command.rb +342 -444
  9. data/lib/clive/error.rb +66 -0
  10. data/lib/clive/formatter.rb +57 -141
  11. data/lib/clive/formatter/colour.rb +37 -0
  12. data/lib/clive/formatter/plain.rb +172 -0
  13. data/lib/clive/option.rb +185 -75
  14. data/lib/clive/option/runner.rb +163 -0
  15. data/lib/clive/output.rb +141 -16
  16. data/lib/clive/parser.rb +180 -87
  17. data/lib/clive/struct_hash.rb +109 -0
  18. data/lib/clive/type.rb +117 -0
  19. data/lib/clive/type/definitions.rb +170 -0
  20. data/lib/clive/type/lookup.rb +23 -0
  21. data/lib/clive/version.rb +3 -3
  22. data/spec/clive/a_cli_spec.rb +245 -0
  23. data/spec/clive/argument_spec.rb +148 -0
  24. data/spec/clive/arguments/parser_spec.rb +35 -0
  25. data/spec/clive/arguments_spec.rb +191 -0
  26. data/spec/clive/command_spec.rb +276 -209
  27. data/spec/clive/formatter/colour_spec.rb +129 -0
  28. data/spec/clive/formatter/plain_spec.rb +129 -0
  29. data/spec/clive/option/runner_spec.rb +92 -0
  30. data/spec/clive/option_spec.rb +149 -23
  31. data/spec/clive/output_spec.rb +86 -2
  32. data/spec/clive/parser_spec.rb +201 -81
  33. data/spec/clive/struct_hash_spec.rb +82 -0
  34. data/spec/clive/type/definitions_spec.rb +312 -0
  35. data/spec/clive/type_spec.rb +107 -0
  36. data/spec/clive_spec.rb +60 -0
  37. data/spec/extras/expectations.rb +86 -0
  38. data/spec/extras/focus.rb +22 -0
  39. data/spec/helper.rb +35 -0
  40. metadata +56 -36
  41. data/lib/clive/bool.rb +0 -67
  42. data/lib/clive/exceptions.rb +0 -54
  43. data/lib/clive/flag.rb +0 -199
  44. data/lib/clive/switch.rb +0 -31
  45. data/lib/clive/tokens.rb +0 -141
  46. data/spec/clive/bool_spec.rb +0 -54
  47. data/spec/clive/flag_spec.rb +0 -117
  48. data/spec/clive/formatter_spec.rb +0 -108
  49. data/spec/clive/switch_spec.rb +0 -14
  50. data/spec/clive/tokens_spec.rb +0 -38
  51. data/spec/shared_specs.rb +0 -16
  52. data/spec/spec_helper.rb +0 -12
@@ -1,5 +1,89 @@
1
- require 'spec_helper'
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
- end
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
@@ -1,106 +1,226 @@
1
- require 'spec_helper'
1
+ $: << File.dirname(__FILE__) + '/..'
2
+ require 'helper'
2
3
 
3
4
  describe Clive::Parser do
4
5
 
5
- subject {
6
- class CLI
7
- include Clive::Parser
8
- end
9
- CLI
10
- }
11
-
12
- it "keeps track of its class" do
13
- subject.instance_variable_get("@klass").should == CLI
14
- end
15
-
16
- describe "#base" do
17
- it "has a 'base' command" do
18
- subject.base.should be_kind_of Clive::Command
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
- end
21
-
22
- describe "#parse" do
23
- it "should call #run on 'base'" do
24
- subject.base.should_receive(:run).with([])
25
- subject.parse([])
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 "#flag" do
30
- it "adds a new flag to 'base'" do
31
- expect {
32
- subject.flag(:t, :test, :arg => "NAME") {|i| puts i }
33
- }.should change {subject.base.flags.size}.by(1)
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 "#switch" do
38
- it "adds a new switch to 'base'" do
39
- expect {
40
- subject.switch(:t, :test) {}
41
- }.should change {subject.base.switches.size}.by(1)
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 "#command" do
46
- it "adds a new command to 'base'" do
47
- expect {
48
- subject.command(:command) {}
49
- }.should change {subject.base.commands.size}.by(1)
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 "#bool" do
54
- it "adds a new bool to 'base'" do
55
- expect {
56
- subject.bool(:boo) {}
57
- }.should change {subject.base.bools.size}.by(2)
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 "#desc" do
62
- it "sets current desc for 'base'" do
63
- subject.desc "test"
64
- subject.base.instance_variable_get("@current_desc").should == "test"
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 "#help_formatter" do
69
- it "sets the help formatter for 'base'" do
70
- subject.base.should_receive(:help_formatter).with(:white)
71
- subject.help_formatter(:white)
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 "#option_var" do
76
- before { subject.option_var(:test, 1) }
77
-
78
- it "creates a getter" do
79
- subject.should respond_to :test
80
- end
81
-
82
- it "creates a setter" do
83
- subject.should respond_to :test=
84
- subject.test = 2
85
- end
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 "#option_hash" do
93
- it "uses #option_var with empty hash" do
94
- subject.should_receive(:option_var).with(:test, {})
95
- subject.option_hash(:test)
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
- end
98
-
99
- describe "#option_array" do
100
- it "uses #option_var with empty array" do
101
- subject.should_receive(:option_var).with(:test, [])
102
- subject.option_array(:test)
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