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