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
@@ -0,0 +1,129 @@
1
+ $: << File.dirname(__FILE__) + '/../..'
2
+ require 'helper'
3
+
4
+ describe Clive::Formatter::Colour do
5
+
6
+ let :clive do
7
+ Clive.new {
8
+
9
+ header 'Usage: clive_test.rb [command] [options]'
10
+ footer 'Further help is available online'
11
+
12
+ opt :version, :tail => true
13
+
14
+ opt :S, :super_size, :head => true
15
+ bool :a, :auto
16
+ opt :s, :size, 'Size of thing', :arg => '<size>'
17
+
18
+
19
+ desc 'A super long description for a super stupid option, this should test the _extreme_ wrapping abilities as it should all be aligned. Maybe I should go for another couple of lines just for good measure. That\'s all'
20
+ opt :complex, :arg => '[<one>] <two> [<three>]'
21
+
22
+ command :new, 'Creates new things', :arg => '<dir>' do
23
+ opt :type, :in => %w(post page blog), :default => :page
24
+ opt :force, 'Force overwrite'
25
+ end
26
+ }
27
+ end
28
+
29
+ subject { Clive::Formatter::Colour }
30
+
31
+ describe '#to_s' do
32
+
33
+ it 'builds the help string' do
34
+ this {
35
+ clive.run s('help'), :formatter => subject.new(:width => 80)
36
+ }.must_output <<EOS
37
+ Usage: clive_test.rb [command] [options]
38
+
39
+ Commands:
40
+ new <dir> \e[90m# \e[0m\e[90mCreates new things\e[0m
41
+ help [<command>] \e[90m# \e[0m\e[90mDisplay help\e[0m
42
+
43
+ Options:
44
+ -S, --super-size
45
+ -a, --[no-]auto
46
+ --complex [<one>] <two> [<three>] \e[90m# \e[0m\e[90mA super long description for a
47
+ super stupid option, this should
48
+ test the _extreme_ wrapping
49
+ abilities as it should all be
50
+ aligned. Maybe I should go for
51
+ another couple of lines just for
52
+ good measure. That's all\e[0m
53
+ -s, --size <size> \e[90m# \e[0m\e[90mSize of thing\e[0m
54
+ -h, --help \e[90m# \e[0m\e[90mDisplay this help message\e[0m
55
+ --version
56
+
57
+ Further help is available online
58
+ EOS
59
+ end
60
+
61
+ it 'obeys minimum ratio' do
62
+ this {
63
+ clive.run s('help'), :formatter => subject.new(:width => 80, :min_ratio => 0.7)
64
+ }.must_output r = <<EOS
65
+ Usage: clive_test.rb [command] [options]
66
+
67
+ Commands:
68
+ new <dir> \e[90m# \e[0m\e[90mCreates new things\e[0m
69
+ help [<command>] \e[90m# \e[0m\e[90mDisplay help\e[0m
70
+
71
+ Options:
72
+ -S, --super-size
73
+ -a, --[no-]auto
74
+ --complex [<one>] <two> [<three>] \e[90m# \e[0m\e[90mA super long
75
+ description for a
76
+ super stupid option,
77
+ this should test the
78
+ _extreme_ wrapping
79
+ abilities as it
80
+ should all be
81
+ aligned. Maybe I
82
+ should go for
83
+ another couple of
84
+ lines just for good
85
+ measure. That's all\e[0m
86
+ -s, --size <size> \e[90m# \e[0m\e[90mSize of thing\e[0m
87
+ -h, --help \e[90m# \e[0m\e[90mDisplay this help
88
+ message\e[0m
89
+ --version
90
+
91
+ Further help is available online
92
+ EOS
93
+ #------------------------------------------------------------------------------| 80
94
+ # |======================| 24
95
+
96
+
97
+ end
98
+
99
+ it 'obeys the maximum ratio' do
100
+ this {
101
+ clive.run s('help'), :formatter => subject.new(:width => 80, :min_ratio => 0, :max_ratio => 0.3)
102
+ }.must_output <<EOS
103
+ Usage: clive_test.rb [command] [options]
104
+
105
+ Commands:
106
+ new <dir> \e[90m# \e[0m\e[90mCreates new things\e[0m
107
+ help [<command>] \e[90m# \e[0m\e[90mDisplay help\e[0m
108
+
109
+ Options:
110
+ -S, --super-size
111
+ -a, --[no-]auto
112
+ --complex [<one>] <two> [<three>]
113
+ \e[90m# \e[0m\e[90mA super long description for a super stupid option,
114
+ this should test the _extreme_ wrapping abilities as
115
+ it should all be aligned. Maybe I should go for
116
+ another couple of lines just for good measure.
117
+ That's all\e[0m
118
+ -s, --size <size> \e[90m# \e[0m\e[90mSize of thing\e[0m
119
+ -h, --help \e[90m# \e[0m\e[90mDisplay this help message\e[0m
120
+ --version
121
+
122
+ Further help is available online
123
+ EOS
124
+ #======================| #
125
+ #------------------------------------------------------------------------------|
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,129 @@
1
+ $: << File.dirname(__FILE__) + '/../..'
2
+ require 'helper'
3
+
4
+ describe Clive::Formatter::Plain do
5
+
6
+ let :clive do
7
+ Clive.new {
8
+
9
+ header 'Usage: clive_test.rb [command] [options]'
10
+ footer 'Further help is available online'
11
+
12
+ opt :version, :tail => true
13
+
14
+ opt :S, :super_size, :head => true
15
+ bool :a, :auto
16
+ opt :s, :size, 'Size of thing', :arg => '<size>'
17
+
18
+
19
+ desc 'A super long description for a super stupid option, this should test the _extreme_ wrapping abilities as it should all be aligned. Maybe I should go for another couple of lines just for good measure. That\'s all'
20
+ opt :complex, :arg => '[<one>] <two> [<three>]'
21
+
22
+ command :new, 'Creates new things', :arg => '<dir>' do
23
+ opt :type, :in => %w(post page blog), :default => :page
24
+ opt :force, 'Force overwrite'
25
+ end
26
+ }
27
+ end
28
+
29
+ subject { Clive::Formatter::Plain }
30
+
31
+ describe '#to_s' do
32
+
33
+ it 'builds the help string' do
34
+ this {
35
+ clive.run s('help'), :formatter => subject.new(:width => 80)
36
+ }.must_output <<EOS
37
+ Usage: clive_test.rb [command] [options]
38
+
39
+ Commands:
40
+ new <dir> # Creates new things
41
+ help [<command>] # Display help
42
+
43
+ Options:
44
+ -S, --super-size
45
+ -a, --[no-]auto
46
+ --complex [<one>] <two> [<three>] # A super long description for a
47
+ super stupid option, this should
48
+ test the _extreme_ wrapping
49
+ abilities as it should all be
50
+ aligned. Maybe I should go for
51
+ another couple of lines just for
52
+ good measure. That's all
53
+ -s, --size <size> # Size of thing
54
+ -h, --help # Display this help message
55
+ --version
56
+
57
+ Further help is available online
58
+ EOS
59
+ end
60
+
61
+ it 'obeys minimum ratio' do
62
+ this {
63
+ clive.run s('help'), :formatter => subject.new(:width => 80, :min_ratio => 0.7)
64
+ }.must_output r = <<EOS
65
+ Usage: clive_test.rb [command] [options]
66
+
67
+ Commands:
68
+ new <dir> # Creates new things
69
+ help [<command>] # Display help
70
+
71
+ Options:
72
+ -S, --super-size
73
+ -a, --[no-]auto
74
+ --complex [<one>] <two> [<three>] # A super long
75
+ description for a
76
+ super stupid option,
77
+ this should test the
78
+ _extreme_ wrapping
79
+ abilities as it
80
+ should all be
81
+ aligned. Maybe I
82
+ should go for
83
+ another couple of
84
+ lines just for good
85
+ measure. That's all
86
+ -s, --size <size> # Size of thing
87
+ -h, --help # Display this help
88
+ message
89
+ --version
90
+
91
+ Further help is available online
92
+ EOS
93
+ #------------------------------------------------------------------------------| 80
94
+ # |======================| 24
95
+
96
+
97
+ end
98
+
99
+ it 'obeys the maximum ratio' do
100
+ this {
101
+ clive.run s('help'), :formatter => subject.new(:width => 80, :min_ratio => 0, :max_ratio => 0.3)
102
+ }.must_output <<EOS
103
+ Usage: clive_test.rb [command] [options]
104
+
105
+ Commands:
106
+ new <dir> # Creates new things
107
+ help [<command>] # Display help
108
+
109
+ Options:
110
+ -S, --super-size
111
+ -a, --[no-]auto
112
+ --complex [<one>] <two> [<three>]
113
+ # A super long description for a super stupid option,
114
+ this should test the _extreme_ wrapping abilities as
115
+ it should all be aligned. Maybe I should go for
116
+ another couple of lines just for good measure.
117
+ That's all
118
+ -s, --size <size> # Size of thing
119
+ -h, --help # Display this help message
120
+ --version
121
+
122
+ Further help is available online
123
+ EOS
124
+ #======================| #
125
+ #------------------------------------------------------------------------------|
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,92 @@
1
+ $: << File.dirname(__FILE__) + '/../..'
2
+ require 'helper'
3
+
4
+ describe Clive::Option::Runner do
5
+ subject { Clive::Option::Runner }
6
+
7
+ describe '#_run' do
8
+ it 'executes the function passed within it' do
9
+ this {
10
+ subject._run({}, {}, proc { puts self.name })
11
+ }.must_output "Clive::Option::Runner\n"
12
+ end
13
+ end
14
+
15
+ describe '#get' do
16
+ it 'gets a value from state' do
17
+ this {
18
+ subject._run({}, {:a => 1}, proc { puts get(:a) })
19
+ }.must_output "1\n"
20
+ end
21
+ end
22
+
23
+ describe '#set' do
24
+ it 'sets a value to state' do
25
+ state = {}
26
+ subject._run({}, state, proc { set(:a, 1) })
27
+ state[:a].must_equal 1
28
+ end
29
+ end
30
+
31
+ describe '#update' do
32
+ it 'updates a state value using a method' do
33
+ state = {:name => 'John Doe'}
34
+ subject._run({}, state, proc {
35
+ update :name, :upcase
36
+ })
37
+ state[:name].must_equal "JOHN DOE"
38
+ end
39
+
40
+ it 'updates a state value using a method with arguments' do
41
+ state = {:line => "A man a plan a canal"}
42
+ subject._run({}, state, proc {
43
+ update :line, :gsub, /[Aa]/, 'i'
44
+ })
45
+ state[:line].must_equal "i min i plin i cinil"
46
+ end
47
+
48
+ it 'updates a state value using a block' do
49
+ state = {:list => []}
50
+ subject._run({}, state, proc {
51
+ update(:list) {|l| l << 1 }
52
+ })
53
+ state[:list].must_equal [1]
54
+ end
55
+
56
+ it 'raises an exception if arguments are missing' do
57
+ this {
58
+ subject._run({}, {}, proc { update(:a) })
59
+ }.must_raise ArgumentError
60
+ end
61
+ end
62
+
63
+ describe '#has?' do
64
+ it 'is true if the state contains the key' do
65
+ this {
66
+ subject._run({}, {:key => nil}, proc { puts has?(:key) })
67
+ }.must_output "true\n"
68
+ end
69
+
70
+ it 'is false if the state does not have the key' do
71
+ this {
72
+ subject._run({}, {}, proc { puts has?(:key) })
73
+ }.must_output "false\n"
74
+ end
75
+
76
+ end
77
+
78
+ describe '#method_missing' do
79
+ it 'makes the arguments available by name' do
80
+ this {
81
+ subject._run({:a => 1}, {}, proc { puts a })
82
+ }.must_output "1\n"
83
+ end
84
+
85
+ it 'passes unknown calls to super' do
86
+ this {
87
+ subject.run({}, {}, proc { puts z })
88
+ }.must_raise NoMethodError
89
+ end
90
+ end
91
+
92
+ end
@@ -1,34 +1,160 @@
1
- require 'spec_helper'
1
+ $: << File.dirname(__FILE__) + '/..'
2
+ require 'helper'
2
3
 
3
4
  describe Clive::Option do
4
- subject { Clive::Option.new([:n, :names], "A test option") { $stdout.puts "hi" } }
5
+ subject { Clive::Option }
5
6
 
6
- it_behaves_like "an option"
7
-
8
- describe "#run" do
9
- it "calls the block" do
10
- $stdout.should_receive(:puts).with("hi")
11
- subject.run
7
+ def option_with(config, &block)
8
+ Clive::Option.new [:o, :opt], "", config, &block
9
+ end
10
+
11
+ describe '#initialize' do
12
+ it 'sorts names by size' do
13
+ subject.new([:zz, :a]).names.must_equal [:a, :zz]
14
+ subject.new([:aa, :z]).names.must_equal [:z, :aa]
15
+ end
16
+
17
+ let(:opt) { option_with :head => true, :args => '<a> <b>', :as => [String, Integer] }
18
+
19
+ it 'finds all options' do
20
+ opt.config.must_include :head
21
+ opt.config.wont_include :args
22
+ end
23
+
24
+ it 'uses default options when not set' do
25
+ opt.config.must_include :runner
26
+ end
27
+
28
+ describe 'setting :head' do
29
+ it 'is true if head is set to true' do
30
+ option_with(:head => true).config.must_contain :head => true
31
+ end
32
+
33
+ it 'is false otherwise' do
34
+ option_with({}).config.must_contain :head => false
35
+ end
36
+ end
37
+
38
+ describe 'setting :tail' do
39
+ it 'is true if tail is set to true' do
40
+ option_with(:tail => true).config.must_contain :tail => true
41
+ end
42
+
43
+ it 'is false otherwise' do
44
+ option_with({}).config.must_contain :tail => false
45
+ end
46
+ end
47
+
48
+ describe 'setting :boolean' do
49
+ it 'is true if boolean is set to true' do
50
+ option_with(:boolean => true).config.must_contain :boolean => true
51
+ end
52
+
53
+ it 'is false otherwise' do
54
+ option_with({}).config.must_contain :boolean => false
55
+ end
56
+ end
57
+
58
+ it 'creates the args list' do
59
+ opt.args.first.must_be_argument :name => :a, :type => Clive::Type::String
60
+ opt.args.last.must_be_argument :name => :b, :type => Clive::Type::Integer
12
61
  end
13
62
  end
14
-
15
- describe "#sort_name" do
16
- it "returns the name to sort by" do
17
- subject.sort_name.should == 'n'
63
+
64
+ describe '#name' do
65
+ it 'returns the long name' do
66
+ opt = subject.new [:opt, :o]
67
+ opt.name.must_equal :opt
68
+ end
69
+
70
+ it 'returns the short name if no long name exists' do
71
+ opt = subject.new [:o]
72
+ opt.name.must_equal :o
73
+ end
74
+ end
75
+
76
+ describe '#to_s' do
77
+ it 'returns a string representation of the option' do
78
+ opt = subject.new [:o, :opt]
79
+ opt.to_s.must_equal "-o, --opt"
80
+ end
81
+
82
+ it 'adds a --[no-] prefix to boolean options' do
83
+ opt = subject.new [:o, :opt], "", :boolean => true
84
+ opt.to_s.must_equal "-o, --[no-]opt"
85
+ end
86
+
87
+ it 'uses dashes instead of underscores' do
88
+ opt = subject.new [:S, :super_long_option_name]
89
+ opt.to_s.must_equal "-S, --super-long-option-name"
18
90
  end
19
91
  end
20
-
21
- describe "#<=>" do
22
- it "sorts by #sort_name" do
23
- other = Clive::Option.new([:z, :apple], "Another") {}
24
- (subject <=> other).should == -1
92
+
93
+ describe '#run' do
94
+ let(:block) { proc {} }
95
+
96
+ it 'calls Runner#_run with true if #boolean?' do
97
+ t = mock
98
+ t.expects(:_run).with([[:truth, true]], {}, block)
99
+ ot = option_with :boolean => true, :runner => t, &block
100
+ ot.run({}, [true])
101
+ end
102
+
103
+ it 'calls Runner#_run with false if #boolean?' do
104
+ f = mock
105
+ f.expects(:_run).with([[:truth, false]], {}, block)
106
+ of = option_with :boolean => true, :runner => f, &block
107
+ of.run({}, [false])
108
+ end
109
+
110
+ it 'calls Runner#_run with mapped args' do
111
+ r = mock
112
+ r.expects(:_run).with([[:a, 'a'], [:b, 'b']], {}, block)
113
+ o = option_with :args => '<a> <b>', :runner => r, &block
114
+ o.run({}, ['a', 'b'])
115
+ end
116
+
117
+ it 'sets state if no block exists' do
118
+ o = option_with :args => '<a> <b>'
119
+ state = Clive::StructHash.new
120
+ o.run state, ['a', 'b']
121
+ state[:opt].must_equal ['a', 'b']
25
122
  end
26
123
  end
27
-
28
- describe "#to_h" do
29
- it "returns a hash for help" do
30
- hsh = {"names" => subject.names_to_strings, "desc" => subject.desc}
31
- subject.to_h.should == hsh
124
+
125
+ describe '#<=>' do
126
+ let(:heada) { subject.new [:a], "", :head => true }
127
+ let(:headz) { subject.new [:z], "", :head => true }
128
+ let(:taila) { subject.new [:a], "", :tail => true }
129
+ let(:tailz) { subject.new [:z], "", :tail => true }
130
+ let(:aaa) { subject.new [:aaa] }
131
+ let(:zzz) { subject.new [:zzz] }
132
+
133
+ it 'puts head?s first' do
134
+ heada.must_be :<, aaa
135
+ heada.must_be :<, zzz
136
+ end
137
+
138
+ it 'compares 2 head?s alphabetically' do
139
+ heada.must_be :<, headz
140
+ end
141
+
142
+ it 'puts tail?s last' do
143
+ taila.must_be :>, aaa
144
+ taila.must_be :>, zzz
145
+ end
146
+
147
+ it 'compares 2 tail?s alphabetically' do
148
+ taila.must_be :<, tailz
149
+ end
150
+
151
+ it 'compares options based on #name' do
152
+ aaa.must_be :<, zzz
153
+ end
154
+
155
+ it 'sorts options properly' do
156
+ [taila, headz, tailz, zzz, heada, aaa].sort.
157
+ must_equal [heada, headz, aaa, zzz, taila, tailz]
32
158
  end
33
159
  end
34
- end
160
+ end