clive 0.6.2 → 0.7.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/README.md +129 -126
- data/lib/clive.rb +30 -119
- data/lib/clive/bool.rb +32 -34
- data/lib/clive/command.rb +171 -54
- data/lib/clive/exceptions.rb +2 -2
- data/lib/clive/ext.rb +21 -3
- data/lib/clive/flag.rb +80 -67
- data/lib/clive/formatter.rb +180 -0
- data/lib/clive/option.rb +41 -25
- data/lib/clive/output.rb +14 -18
- data/lib/clive/parser.rb +79 -16
- data/lib/clive/switch.rb +8 -17
- data/lib/clive/tokens.rb +1 -1
- data/lib/clive/version.rb +2 -2
- data/spec/clive/bool_spec.rb +54 -0
- data/spec/clive/command_spec.rb +260 -0
- data/spec/clive/exceptions_spec.rb +1 -0
- data/spec/clive/ext_spec.rb +1 -0
- data/spec/clive/flag_spec.rb +84 -0
- data/spec/clive/formatter_spec.rb +108 -0
- data/spec/clive/option_spec.rb +34 -0
- data/spec/clive/output_spec.rb +5 -0
- data/spec/clive/parser_spec.rb +106 -0
- data/spec/clive/switch_spec.rb +14 -0
- data/spec/clive/tokens_spec.rb +38 -0
- data/spec/shared_specs.rb +16 -0
- data/spec/spec_helper.rb +12 -0
- metadata +34 -8
data/lib/clive/output.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
#
|
7
7
|
#
|
8
8
|
|
9
|
-
|
9
|
+
module Clive
|
10
10
|
module Output
|
11
11
|
|
12
12
|
end
|
@@ -68,23 +68,19 @@ class String
|
|
68
68
|
colour("\e[4#{code}m")
|
69
69
|
end
|
70
70
|
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
define_method "#{_name}_bg" do
|
84
|
-
colour("\e[10#{code}m")
|
85
|
-
end
|
71
|
+
# Change name to grey instead of l_black
|
72
|
+
l_name = "l_#{name}"
|
73
|
+
if name == "black"
|
74
|
+
l_name = "grey"
|
75
|
+
end
|
76
|
+
|
77
|
+
define_method "#{l_name}" do
|
78
|
+
colour("\e[9#{code}m")
|
79
|
+
end
|
80
|
+
|
81
|
+
define_method "#{l_name}_bg" do
|
82
|
+
colour("\e[10#{code}m")
|
86
83
|
end
|
87
|
-
end
|
88
84
|
|
85
|
+
end
|
89
86
|
end
|
90
|
-
|
data/lib/clive/parser.rb
CHANGED
@@ -8,7 +8,11 @@ require 'clive/switch'
|
|
8
8
|
require 'clive/flag'
|
9
9
|
require 'clive/bool'
|
10
10
|
|
11
|
-
|
11
|
+
require 'clive/output'
|
12
|
+
require 'clive/formatter'
|
13
|
+
|
14
|
+
|
15
|
+
module Clive
|
12
16
|
|
13
17
|
# A module wrapping the command line parsing of clive. In the future this
|
14
18
|
# will be the only way of using clive.
|
@@ -19,11 +23,7 @@ class Clive
|
|
19
23
|
#
|
20
24
|
# class CLI
|
21
25
|
# include Clive::Parser
|
22
|
-
#
|
23
|
-
# @@opts ||= {}
|
24
|
-
# def self.opts
|
25
|
-
# @@opts
|
26
|
-
# end
|
26
|
+
# option_hash :opts
|
27
27
|
#
|
28
28
|
# switch :v, :verbose, "Run verbosely" do
|
29
29
|
# opts[:verbose] = true
|
@@ -34,32 +34,95 @@ class Clive
|
|
34
34
|
# p CLI.opts
|
35
35
|
#
|
36
36
|
module Parser
|
37
|
-
|
37
|
+
|
38
|
+
# When the module is included we need to keep track of the new class it
|
39
|
+
# is now in and we need to create a new base command. So here instance
|
40
|
+
# variables are set directly in the new class, and the class is made to
|
41
|
+
# extend the methods in Parser so they are available as class methods.
|
42
|
+
#
|
38
43
|
def self.included(klass)
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
klass.instance_variable_set("@klass", klass)
|
45
|
+
klass.extend(self)
|
46
|
+
klass.instance_variable_set "@base", Clive::Command.new(true)
|
42
47
|
end
|
43
|
-
|
48
|
+
|
49
|
+
# @return [Clive::Command]
|
50
|
+
# The base command to forward method calls to.
|
51
|
+
#
|
52
|
+
def base; @base; end
|
53
|
+
|
54
|
+
# @see Clive::Command#run
|
44
55
|
def parse(argv)
|
45
|
-
|
56
|
+
base.run(argv)
|
46
57
|
end
|
47
58
|
|
59
|
+
# @see Clive::Command#flag
|
48
60
|
def flag(*args, &block)
|
49
|
-
|
61
|
+
base.flag(*args, &block)
|
50
62
|
end
|
51
63
|
|
64
|
+
# @see Clive::Command#switch
|
52
65
|
def switch(*args, &block)
|
53
|
-
|
66
|
+
base.switch(*args, &block)
|
54
67
|
end
|
55
68
|
|
69
|
+
# @see Clive::Command#command
|
56
70
|
def command(*args, &block)
|
57
|
-
|
71
|
+
base.command(*args, &block)
|
58
72
|
end
|
59
73
|
|
74
|
+
# @see Clive::Command#bool
|
60
75
|
def bool(*args, &block)
|
61
|
-
|
76
|
+
base.bool(*args, &block)
|
77
|
+
end
|
78
|
+
|
79
|
+
# @see Clive::Command#desc
|
80
|
+
def desc(*args)
|
81
|
+
base.desc(*args)
|
82
|
+
end
|
83
|
+
|
84
|
+
# @see Clive::Command#help_formatter
|
85
|
+
def help_formatter(*args, &block)
|
86
|
+
base.help_formatter(*args, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
# @see http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
|
91
|
+
# or because that doesn't exist anymore from this mirror
|
92
|
+
# http://viewsourcecode.org/why/hacking/seeingMetaclassesClearly.html
|
93
|
+
#
|
94
|
+
def meta_def(name, &blk)
|
95
|
+
(class << self; self; end).instance_eval { define_method(name, &blk) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def option_var(name, value=nil)
|
99
|
+
@klass.meta_def(name) do
|
100
|
+
instance_variable_get("@#{name}")
|
101
|
+
end
|
102
|
+
@klass.meta_def("#{name}=") do |val|
|
103
|
+
instance_variable_set("@#{name}", val)
|
104
|
+
end
|
105
|
+
instance_variable_set("@#{name}", value)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Create a new hash which is accessible to the options in the new class
|
109
|
+
# but can also be accessed from outside the class. Defines getters and
|
110
|
+
# setters for the symbols given, and sets their initial value to +{}+.
|
111
|
+
#
|
112
|
+
# @param args [Symbol]
|
113
|
+
#
|
114
|
+
def option_hash(*args)
|
115
|
+
args.each do |arg|
|
116
|
+
option_var(arg, {})
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def option_array(*args)
|
121
|
+
args.each do |arg|
|
122
|
+
option_var(arg, [])
|
123
|
+
end
|
62
124
|
end
|
125
|
+
alias_method :option_list, :option_array
|
63
126
|
|
64
127
|
end
|
65
128
|
end
|
data/lib/clive/switch.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
module Clive
|
2
2
|
|
3
3
|
# A string that takes no argument, beginning with one or two dashes
|
4
4
|
# eg. ruby --version
|
@@ -8,26 +8,17 @@ class Clive
|
|
8
8
|
|
9
9
|
# Create a new Switch instance.
|
10
10
|
#
|
11
|
-
#
|
11
|
+
# @param names [Array[Symbol]]
|
12
|
+
# An array of names the option can be invoked by.
|
12
13
|
#
|
13
|
-
# @
|
14
|
-
#
|
15
|
-
# @param [Symbol] short single character for short switch, eg. +:v+ => +-v+
|
16
|
-
# @param [Symbol] long longer switch to be used, eg. +:verbose+ => +--verbose+
|
17
|
-
# @param [String] desc the description for the switch
|
14
|
+
# @param desc [String]
|
15
|
+
# A description of what the option does.
|
18
16
|
#
|
19
17
|
# @yield A block to run if the switch is triggered
|
20
18
|
#
|
21
|
-
def initialize(
|
22
|
-
@names =
|
23
|
-
|
24
|
-
case i
|
25
|
-
when Symbol
|
26
|
-
@names << i.to_s
|
27
|
-
when String
|
28
|
-
@desc = i
|
29
|
-
end
|
30
|
-
end
|
19
|
+
def initialize(names, desc, &block)
|
20
|
+
@names = names.map(&:to_s)
|
21
|
+
@desc = desc
|
31
22
|
@block = block
|
32
23
|
end
|
33
24
|
|
data/lib/clive/tokens.rb
CHANGED
data/lib/clive/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = '0.
|
1
|
+
module Clive
|
2
|
+
VERSION = '0.7.0'
|
3
3
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Clive::Bool do
|
4
|
+
|
5
|
+
subject { Clive::Bool.new([:n, :name], "A test", true) {|arg| puts arg } }
|
6
|
+
let(:falsey) { Clive::Bool.new([:n, :name], "A test", false) {|arg| puts arg } }
|
7
|
+
|
8
|
+
describe "#truth" do
|
9
|
+
it "returns the truth" do
|
10
|
+
subject.truth.should == true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it_behaves_like "an option"
|
15
|
+
|
16
|
+
context "when no long name is given" do
|
17
|
+
it "raises an error" do
|
18
|
+
expect {
|
19
|
+
Clive::Bool.new([:n], "Short test", true) {}
|
20
|
+
}.should raise_error Clive::MissingLongName
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#run" do
|
25
|
+
context "when truth is true" do
|
26
|
+
it "passes true to the block" do
|
27
|
+
$stdout.should_receive(:puts).with(true)
|
28
|
+
subject.run
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when truth is false" do
|
33
|
+
it "passes false to the block" do
|
34
|
+
$stdout.should_receive(:puts).with(false)
|
35
|
+
falsey.run
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#to_h" do
|
41
|
+
context "when truth is true" do
|
42
|
+
it "returns hash for help formatter" do
|
43
|
+
hsh = {'names' => subject.names_to_strings(true),
|
44
|
+
'desc' => subject.desc}
|
45
|
+
subject.to_h.should == hsh
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when truth is false" do
|
50
|
+
specify { falsey.to_h.should be_nil }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Clive::Command do
|
4
|
+
|
5
|
+
context "when creating a base command" do
|
6
|
+
subject { Clive::Command.new(true) {} }
|
7
|
+
end
|
8
|
+
|
9
|
+
subject {
|
10
|
+
Clive::Command.new(:co, :comm, "A command") do
|
11
|
+
bool(:boo) {}
|
12
|
+
switch(:swi) {}
|
13
|
+
flag(:fla) {}
|
14
|
+
command(:com) {}
|
15
|
+
end
|
16
|
+
}
|
17
|
+
|
18
|
+
it_behaves_like "an option"
|
19
|
+
|
20
|
+
describe "#initialize" do
|
21
|
+
subject {
|
22
|
+
Clive::Command.new(:com, "A command") do
|
23
|
+
flag(:test)
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
it "generates a help header" do
|
28
|
+
File.stub!(:basename).and_return("test")
|
29
|
+
subject.instance_variable_get("@header").should == "Usage: test com [options]"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "generates an option missing proc" do
|
33
|
+
proc = subject.instance_variable_get("@option_missing")
|
34
|
+
expect {
|
35
|
+
proc.call("hey")
|
36
|
+
}.should raise_error Clive::NoOptionError
|
37
|
+
end
|
38
|
+
|
39
|
+
it "sets the help formatter to :default" do
|
40
|
+
formatter = subject.instance_variable_get("@help_formatter")
|
41
|
+
formatter.should == subject.help_formatter(:default)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "doesn't run the block given" do
|
45
|
+
subject.flags.size.should == 0
|
46
|
+
end
|
47
|
+
|
48
|
+
it "generates a help switch" do
|
49
|
+
subject.switches.map {|i| i.names}.should include ["h", "help"]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#bools" do
|
54
|
+
it "returns an array of bools" do
|
55
|
+
subject.find
|
56
|
+
subject.bools.each do |i|
|
57
|
+
i.should be_kind_of Clive::Bool
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#switches" do
|
63
|
+
it "returns an array of switches" do
|
64
|
+
subject.find
|
65
|
+
subject.switches.each do |i|
|
66
|
+
i.should be_kind_of Clive::Switch
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#flags" do
|
72
|
+
it "returns an array of flags" do
|
73
|
+
subject.find
|
74
|
+
subject.flags.each do |i|
|
75
|
+
i.should be_kind_of Clive::Flag
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#find" do
|
81
|
+
it "runs the block for the command" do
|
82
|
+
subject.flags.size.should == 0
|
83
|
+
subject.find
|
84
|
+
subject.flags.size.should == 1
|
85
|
+
end
|
86
|
+
|
87
|
+
it "sets the block to nil" do
|
88
|
+
subject.find
|
89
|
+
subject.block.should be_nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#run" do
|
94
|
+
it "returns an array of unused arguments" do
|
95
|
+
subject.find
|
96
|
+
subject.run(%w(--swi what)).should == ['what']
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#tokenize" do
|
101
|
+
it "returns an array of tokens" do
|
102
|
+
subject.find
|
103
|
+
res = [
|
104
|
+
[:switch, subject.bools.find {|i| i.names == ["boo"] }],
|
105
|
+
[:argument, "what"]
|
106
|
+
]
|
107
|
+
subject.tokenize(%w(--boo what)).should == res
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "#parse" do
|
112
|
+
it "returns an array of tokens"
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#to_h" do
|
116
|
+
it "returns a hash of data for help formatting" do
|
117
|
+
hsh = {'names' => subject.names, 'desc' => subject.desc}
|
118
|
+
subject.to_h.should == hsh
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "#command" do
|
123
|
+
it "creates a new command" do
|
124
|
+
expect {
|
125
|
+
subject.command(:comm)
|
126
|
+
}.should change {subject.commands.size}.by(1)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "resets the current description" do
|
130
|
+
subject.desc 'A command'
|
131
|
+
subject.command(:comm)
|
132
|
+
subject.current_desc.should == ""
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "#switch" do
|
137
|
+
it "creates a new switch" do
|
138
|
+
expect {
|
139
|
+
subject.switch(:s, :switch)
|
140
|
+
}.should change {subject.switches.size}.by(1)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "resets the current description" do
|
144
|
+
subject.desc 'A switch'
|
145
|
+
subject.switch(:s, :switch)
|
146
|
+
subject.current_desc.should == ""
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "#flag" do
|
151
|
+
it "creates a new flag" do
|
152
|
+
expect {
|
153
|
+
subject.flag(:f, :flag)
|
154
|
+
}.should change {subject.flags.size}.by(1)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "resets the current description" do
|
158
|
+
subject.desc 'A flag'
|
159
|
+
subject.flag(:f, :flag)
|
160
|
+
subject.current_desc.should == ""
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "#bool" do
|
165
|
+
it "creates two bool switches" do
|
166
|
+
expect {
|
167
|
+
subject.bool(:b, :bool)
|
168
|
+
}.should change {subject.bools.size}.by(2)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "resets the current description" do
|
172
|
+
subject.desc 'A bool'
|
173
|
+
subject.bool(:b, :bool)
|
174
|
+
subject.current_desc.should == ""
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "#desc" do
|
179
|
+
context "when called with no arguments" do
|
180
|
+
it "returns the description for the command" do
|
181
|
+
subject.desc.should == "A command"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "when called with an argument" do
|
186
|
+
it "sets the current description" do
|
187
|
+
subject.desc "A new desc"
|
188
|
+
subject.instance_variable_get("@current_desc").should == "A new desc"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "#option_missing" do
|
194
|
+
it "sets the option missing proc" do
|
195
|
+
proc = lambda {|n| puts "What? #{name} doesn't exist" }
|
196
|
+
subject.option_missing(&proc)
|
197
|
+
subject.instance_variable_get("@option_missing").should == proc
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe "#header" do
|
202
|
+
it "sets the header" do
|
203
|
+
subject.header "A header"
|
204
|
+
subject.instance_variable_get("@header").should == "A header"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "#footer" do
|
209
|
+
it "sets the footer" do
|
210
|
+
subject.footer "A footer"
|
211
|
+
subject.instance_variable_get("@footer").should == "A footer"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "#build_help" do
|
216
|
+
it "adds a switch for help" do
|
217
|
+
subject.options = Clive::Array.new
|
218
|
+
subject.options.should be_empty
|
219
|
+
subject.build_help
|
220
|
+
subject.options.map(&:names).should include ['h', 'help']
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe "#help" do
|
225
|
+
it "returns a string of help" do
|
226
|
+
help = <<EOS
|
227
|
+
Usage: rspec co, comm [options]
|
228
|
+
|
229
|
+
Options:
|
230
|
+
-h, --help \e[90mDisplay help\e[0m
|
231
|
+
EOS
|
232
|
+
|
233
|
+
subject.help.should == help
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "#help_formatter" do
|
238
|
+
context "when called with a symbol" do
|
239
|
+
it "uses the named formatter" do
|
240
|
+
before = subject.instance_variable_get("@formatter")
|
241
|
+
subject.help_formatter(:white)
|
242
|
+
subject.instance_variable_get("@formatter").should_not == before
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "when called with argumentss and a block" do
|
247
|
+
it "creates a new help formatter" do
|
248
|
+
subject.help_formatter :width => 40, :prepend => 5 do |h|
|
249
|
+
h.switch "switch"
|
250
|
+
h.bool "bool"
|
251
|
+
h.flag "flag"
|
252
|
+
h.command "command"
|
253
|
+
end
|
254
|
+
formatter = subject.instance_variable_get("@formatter")
|
255
|
+
formatter.width.should == 40
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|