jls-clamp 0.3.1
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/.gitignore +8 -0
- data/.travis.yml +5 -0
- data/Gemfile +9 -0
- data/README.markdown +274 -0
- data/Rakefile +12 -0
- data/clamp.gemspec +24 -0
- data/examples/flipflop +31 -0
- data/examples/fubar +23 -0
- data/examples/gitdown +61 -0
- data/examples/speak +33 -0
- data/lib/clamp/attribute.rb +40 -0
- data/lib/clamp/attribute_declaration.rb +40 -0
- data/lib/clamp/command.rb +142 -0
- data/lib/clamp/errors.rb +26 -0
- data/lib/clamp/help.rb +100 -0
- data/lib/clamp/option/declaration.rb +57 -0
- data/lib/clamp/option/parsing.rb +59 -0
- data/lib/clamp/option.rb +80 -0
- data/lib/clamp/parameter/declaration.rb +28 -0
- data/lib/clamp/parameter/parsing.rb +24 -0
- data/lib/clamp/parameter.rb +80 -0
- data/lib/clamp/subcommand/declaration.rb +44 -0
- data/lib/clamp/subcommand/parsing.rb +41 -0
- data/lib/clamp/subcommand.rb +23 -0
- data/lib/clamp/version.rb +3 -0
- data/lib/clamp.rb +3 -0
- data/spec/clamp/command_group_spec.rb +267 -0
- data/spec/clamp/command_spec.rb +766 -0
- data/spec/clamp/option_module_spec.rb +37 -0
- data/spec/clamp/option_spec.rb +149 -0
- data/spec/clamp/parameter_spec.rb +201 -0
- data/spec/spec_helper.rb +45 -0
- metadata +84 -0
data/lib/clamp.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Clamp::Command do
|
4
|
+
|
5
|
+
extend CommandFactory
|
6
|
+
include OutputCapture
|
7
|
+
|
8
|
+
describe "with subcommands" do
|
9
|
+
|
10
|
+
given_command "flipflop" do
|
11
|
+
|
12
|
+
subcommand "flip", "flip it" do
|
13
|
+
def execute
|
14
|
+
puts "FLIPPED"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
subcommand "flop", "flop it\nfor extra flop" do
|
19
|
+
def execute
|
20
|
+
puts "FLOPPED"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
it "delegates to sub-commands" do
|
27
|
+
|
28
|
+
@command.run(["flip"])
|
29
|
+
stdout.should =~ /FLIPPED/
|
30
|
+
|
31
|
+
@command.run(["flop"])
|
32
|
+
stdout.should =~ /FLOPPED/
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context "executed with no subcommand" do
|
37
|
+
|
38
|
+
it "triggers help" do
|
39
|
+
lambda do
|
40
|
+
@command.run([])
|
41
|
+
end.should raise_error(Clamp::HelpWanted)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#parse" do
|
47
|
+
|
48
|
+
describe "with too many arguments" do
|
49
|
+
|
50
|
+
it "raises a UsageError" do
|
51
|
+
lambda do
|
52
|
+
@command.parse(["flip", "extra", "args"])
|
53
|
+
end.should raise_error(Clamp::UsageError, "too many arguments")
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#help" do
|
61
|
+
|
62
|
+
it "shows subcommand parameters in usage" do
|
63
|
+
@command.help.should include("flipflop [OPTIONS] SUBCOMMAND [ARGS] ...")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "lists subcommands" do
|
67
|
+
@help = @command.help
|
68
|
+
@help.should =~ /Subcommands:/
|
69
|
+
@help.should =~ /flip +flip it/
|
70
|
+
@help.should =~ /flop +flop it/
|
71
|
+
end
|
72
|
+
|
73
|
+
it "handles new lines in subcommand descriptions" do
|
74
|
+
@command.help.should =~ /flop +flop it\n +for extra flop/
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "with an aliased subcommand" do
|
82
|
+
|
83
|
+
given_command "blah" do
|
84
|
+
|
85
|
+
subcommand ["say", "talk"], "Say something" do
|
86
|
+
|
87
|
+
parameter "WORD ...", "stuff to say"
|
88
|
+
|
89
|
+
def execute
|
90
|
+
puts word_list
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
it "responds to both aliases" do
|
98
|
+
|
99
|
+
@command.run(["say", "boo"])
|
100
|
+
stdout.should =~ /boo/
|
101
|
+
|
102
|
+
@command.run(["talk", "jive"])
|
103
|
+
stdout.should =~ /jive/
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#help" do
|
108
|
+
|
109
|
+
it "lists all aliases" do
|
110
|
+
@help = @command.help
|
111
|
+
@help.should =~ /say, talk .* Say something/
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "with nested subcommands" do
|
119
|
+
|
120
|
+
given_command "fubar" do
|
121
|
+
|
122
|
+
subcommand "foo", "Foo!" do
|
123
|
+
|
124
|
+
subcommand "bar", "Baaaa!" do
|
125
|
+
def execute
|
126
|
+
puts "FUBAR"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
it "delegates multiple levels" do
|
135
|
+
@command.run(["foo", "bar"])
|
136
|
+
stdout.should =~ /FUBAR/
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "with a default subcommand" do
|
142
|
+
|
143
|
+
given_command "admin" do
|
144
|
+
|
145
|
+
subcommand "status", "Show status" do
|
146
|
+
|
147
|
+
def execute
|
148
|
+
puts "All good!"
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
self.default_subcommand = "status"
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
context "executed with no subcommand" do
|
158
|
+
|
159
|
+
it "invokes the default subcommand" do
|
160
|
+
@command.run([])
|
161
|
+
stdout.should =~ /All good/
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "with a default subcommand, declared the old way" do
|
169
|
+
|
170
|
+
given_command "admin" do
|
171
|
+
|
172
|
+
default_subcommand "status", "Show status" do
|
173
|
+
|
174
|
+
def execute
|
175
|
+
puts "All good!"
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
context "executed with no subcommand" do
|
183
|
+
|
184
|
+
it "invokes the default subcommand" do
|
185
|
+
@command.run([])
|
186
|
+
stdout.should =~ /All good/
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "each subcommand" do
|
194
|
+
|
195
|
+
before do
|
196
|
+
|
197
|
+
speed_options = Module.new do
|
198
|
+
extend Clamp::Option::Declaration
|
199
|
+
option "--speed", "SPEED", "how fast", :default => "slowly"
|
200
|
+
end
|
201
|
+
|
202
|
+
@command_class = Class.new(Clamp::Command) do
|
203
|
+
|
204
|
+
option "--direction", "DIR", "which way", :default => "home"
|
205
|
+
|
206
|
+
include speed_options
|
207
|
+
|
208
|
+
subcommand "move", "move in the appointed direction" do
|
209
|
+
|
210
|
+
def execute
|
211
|
+
motion = context[:motion] || "walking"
|
212
|
+
puts "#{motion} #{direction} #{speed}"
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
@command = @command_class.new("go")
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
it "accepts options defined in superclass (specified after the subcommand)" do
|
224
|
+
@command.run(["move", "--direction", "north"])
|
225
|
+
stdout.should =~ /walking north/
|
226
|
+
end
|
227
|
+
|
228
|
+
it "accepts options defined in superclass (specified before the subcommand)" do
|
229
|
+
@command.run(["--direction", "north", "move"])
|
230
|
+
stdout.should =~ /walking north/
|
231
|
+
end
|
232
|
+
|
233
|
+
it "accepts options defined in included modules" do
|
234
|
+
@command.run(["move", "--speed", "very quickly"])
|
235
|
+
stdout.should =~ /walking home very quickly/
|
236
|
+
end
|
237
|
+
|
238
|
+
it "has access to command context" do
|
239
|
+
@command = @command_class.new("go", :motion => "wandering")
|
240
|
+
@command.run(["move"])
|
241
|
+
stdout.should =~ /wandering home/
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "with a subcommand, with options" do
|
247
|
+
|
248
|
+
given_command 'weeheehee' do
|
249
|
+
option '--json', 'JSON', 'a json blob' do |option|
|
250
|
+
print "parsing!"
|
251
|
+
option
|
252
|
+
end
|
253
|
+
|
254
|
+
subcommand 'woohoohoo', 'like weeheehee but with more o' do
|
255
|
+
def execute
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
it "only parses options once" do
|
261
|
+
@command.run(['--json', '{"a":"b"}', 'woohoohoo'])
|
262
|
+
stdout.should == 'parsing!'
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|