cliqr 1.1.0 → 1.2.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -1
  3. data/README.md +97 -71
  4. data/examples/README.md +12 -0
  5. data/examples/hbase +58 -0
  6. data/examples/my-command +63 -0
  7. data/examples/numbers +55 -0
  8. data/examples/vagrant +90 -0
  9. data/lib/cliqr.rb +17 -2
  10. data/lib/cliqr/argument_validation/argument_type_validator.rb +2 -2
  11. data/lib/cliqr/argument_validation/validator.rb +1 -1
  12. data/lib/cliqr/cli/argument_operator.rb +44 -0
  13. data/lib/cliqr/cli/argument_operator_context.rb +20 -0
  14. data/lib/cliqr/cli/command.rb +1 -1
  15. data/lib/cliqr/cli/command_context.rb +93 -12
  16. data/lib/cliqr/cli/command_runner_factory.rb +2 -2
  17. data/lib/cliqr/cli/config.rb +301 -33
  18. data/lib/cliqr/cli/executor.rb +14 -9
  19. data/lib/cliqr/cli/interface.rb +22 -7
  20. data/lib/cliqr/cli/router.rb +6 -2
  21. data/lib/cliqr/cli/shell_command.rb +69 -0
  22. data/lib/cliqr/cli/usage_builder.rb +185 -0
  23. data/lib/cliqr/config_validation/validator_factory.rb +59 -5
  24. data/lib/cliqr/error.rb +10 -4
  25. data/lib/cliqr/parser/action_token.rb +23 -0
  26. data/lib/cliqr/parser/argument_parser.rb +1 -1
  27. data/lib/cliqr/parser/argument_token.rb +1 -4
  28. data/lib/cliqr/parser/argument_tree_walker.rb +40 -8
  29. data/lib/cliqr/parser/option_token.rb +2 -1
  30. data/lib/cliqr/parser/parsed_input.rb +21 -2
  31. data/lib/cliqr/parser/parsed_input_builder.rb +11 -7
  32. data/lib/cliqr/parser/token.rb +3 -9
  33. data/lib/cliqr/parser/token_factory.rb +1 -1
  34. data/lib/cliqr/util.rb +135 -0
  35. data/lib/cliqr/version.rb +1 -1
  36. data/spec/argument_parser_spec_helper.rb +15 -0
  37. data/spec/config/action_config_validator_spec.rb +146 -0
  38. data/spec/config/config_finalize_spec.rb +1 -1
  39. data/spec/config/config_validator_spec.rb +29 -19
  40. data/spec/config/option_config_validator_spec.rb +13 -13
  41. data/spec/dsl/interface_spec.rb +1 -168
  42. data/spec/dsl/usage_spec.rb +705 -0
  43. data/spec/executor/action_executor_spec.rb +205 -0
  44. data/spec/executor/executor_spec.rb +405 -17
  45. data/spec/executor/help_executor_spec.rb +424 -0
  46. data/spec/executor/shell_executor_spec.rb +233 -0
  47. data/spec/fixtures/action_reader_command.rb +12 -0
  48. data/spec/fixtures/csv_argument_operator.rb +8 -0
  49. data/spec/fixtures/test_option_type_checker_command.rb +8 -0
  50. data/spec/parser/action_argument_parser_spec.rb +113 -0
  51. data/spec/parser/argument_parser_spec.rb +37 -44
  52. data/spec/spec_helper.rb +1 -0
  53. data/spec/validation/action_argument_validator_spec.rb +50 -0
  54. data/spec/validation/{argument_validation_spec.rb → command_argument_validation_spec.rb} +36 -18
  55. data/spec/validation/error_spec.rb +1 -1
  56. data/tasks/rdoc.rake +16 -0
  57. data/tasks/rubucop.rake +14 -0
  58. data/tasks/yard.rake +21 -0
  59. data/templates/usage.erb +39 -0
  60. metadata +48 -11
@@ -0,0 +1,424 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'cliqr/error'
6
+
7
+ require 'fixtures/test_command'
8
+
9
+ describe Cliqr::CLI::Executor do
10
+ it 'can execute help action to get help for base command' do
11
+ cli = Cliqr.interface do
12
+ name 'my-command'
13
+ description 'test command has no description'
14
+ handler TestCommand
15
+ end
16
+
17
+ result = cli.execute_internal ['help'], output: :buffer
18
+ expect(result[:stdout]).to eq <<-EOS
19
+ my-command -- test command has no description
20
+
21
+ USAGE:
22
+ my-command [actions] [options] [arguments]
23
+
24
+ Available options:
25
+
26
+ --help, -h : Get helpful information for action "my-command" along with its usage information.
27
+
28
+ Available actions:
29
+ [ Type "my-command help [action-name]" to get more information about that action ]
30
+
31
+ help -- The help action for command "my-command" which provides details and usage information on how to use the command.
32
+ EOS
33
+ end
34
+
35
+ it 'can execute help option to get help for base command' do
36
+ cli = Cliqr.interface do
37
+ name 'my-command'
38
+ description 'test command has no description'
39
+ handler TestCommand
40
+ end
41
+
42
+ result = cli.execute_internal ['--help'], output: :buffer
43
+ expect(result[:stdout]).to eq <<-EOS
44
+ my-command -- test command has no description
45
+
46
+ USAGE:
47
+ my-command [actions] [options] [arguments]
48
+
49
+ Available options:
50
+
51
+ --help, -h : Get helpful information for action "my-command" along with its usage information.
52
+
53
+ Available actions:
54
+ [ Type "my-command help [action-name]" to get more information about that action ]
55
+
56
+ help -- The help action for command "my-command" which provides details and usage information on how to use the command.
57
+ EOS
58
+ end
59
+
60
+ it 'can execute help action to get help for a action' do
61
+ cli = Cliqr.interface do
62
+ name :my_command
63
+ description 'test command has no description'
64
+ handler TestCommand
65
+
66
+ action :action_1 do
67
+ description 'test action'
68
+ handler TestCommand
69
+ shell :disable
70
+
71
+ action :sub_action do
72
+ description 'This is a sub action.'
73
+ handler TestCommand
74
+ end
75
+
76
+ option :temp do
77
+ description 'temporary option'
78
+ end
79
+ end
80
+
81
+ action :action_2 do
82
+ description 'another cool action for the base command'
83
+ handler TestCommand
84
+ end
85
+ end
86
+
87
+ result = cli.execute_internal %w(help action_1), output: :buffer
88
+ expect(result[:stdout]).to eq <<-EOS
89
+ my_command action_1 -- test action
90
+
91
+ USAGE:
92
+ my_command action_1 [actions] [options] [arguments]
93
+
94
+ Available options:
95
+
96
+ --temp : temporary option
97
+ --help, -h : Get helpful information for action "my_command action_1" along with its usage information.
98
+
99
+ Available actions:
100
+ [ Type "my_command action_1 help [action-name]" to get more information about that action ]
101
+
102
+ sub_action -- This is a sub action.
103
+
104
+ help -- The help action for command "my_command action_1" which provides details and usage information on how to use the command.
105
+ EOS
106
+ end
107
+
108
+ it 'can use help option to get help for a action' do
109
+ cli = Cliqr.interface do
110
+ name :my_command
111
+ description 'test command has no description'
112
+ handler TestCommand
113
+
114
+ action :action_1 do
115
+ description 'test action'
116
+ handler TestCommand
117
+
118
+ action :sub_action do
119
+ description 'This is a sub action.'
120
+ handler TestCommand
121
+ end
122
+
123
+ option :temp do
124
+ description 'temporary option'
125
+ end
126
+ end
127
+
128
+ action :action_2 do
129
+ description 'another cool action for the base command'
130
+ handler TestCommand
131
+ end
132
+ end
133
+
134
+ result = cli.execute_internal %w(action_2 --help), output: :buffer
135
+ expect(result[:stdout]).to eq <<-EOS
136
+ my_command action_2 -- another cool action for the base command
137
+
138
+ USAGE:
139
+ my_command action_2 [actions] [options] [arguments]
140
+
141
+ Available options:
142
+
143
+ --help, -h : Get helpful information for action "my_command action_2" along with its usage information.
144
+
145
+ Available actions:
146
+ [ Type "my_command action_2 help [action-name]" to get more information about that action ]
147
+
148
+ help -- The help action for command "my_command action_2" which provides details and usage information on how to use the command.
149
+ EOS
150
+ end
151
+
152
+ it 'can execute help action to get help for base command with multiple actions' do
153
+ cli = Cliqr.interface do
154
+ name :my_command
155
+ description 'test command has no description'
156
+ handler TestCommand
157
+ shell :disable
158
+
159
+ action :action_1 do
160
+ description 'test action'
161
+ handler TestCommand
162
+
163
+ action :sub_action do
164
+ description 'This is a sub action.'
165
+ handler TestCommand
166
+ end
167
+
168
+ option :temp do
169
+ description 'temporary option'
170
+ end
171
+ end
172
+
173
+ action :action_2 do
174
+ description 'another cool action for the base command'
175
+ handler TestCommand
176
+ end
177
+ end
178
+
179
+ result = cli.execute_internal %w(help), output: :buffer
180
+ expect(result[:stdout]).to eq <<-EOS
181
+ my_command -- test command has no description
182
+
183
+ USAGE:
184
+ my_command [actions] [options] [arguments]
185
+
186
+ Available options:
187
+
188
+ --help, -h : Get helpful information for action "my_command" along with its usage information.
189
+
190
+ Available actions:
191
+ [ Type "my_command help [action-name]" to get more information about that action ]
192
+
193
+ action_1 -- test action
194
+
195
+ action_2 -- another cool action for the base command
196
+
197
+ help -- The help action for command "my_command" which provides details and usage information on how to use the command.
198
+ EOS
199
+ end
200
+
201
+ it 'can execute help option to get help for base command' do
202
+ cli = Cliqr.interface do
203
+ name 'my-command'
204
+ description 'test command has no description'
205
+ handler TestCommand
206
+ end
207
+
208
+ result = cli.execute_internal ['--help'], output: :buffer
209
+ expect(result[:stdout]).to eq <<-EOS
210
+ my-command -- test command has no description
211
+
212
+ USAGE:
213
+ my-command [actions] [options] [arguments]
214
+
215
+ Available options:
216
+
217
+ --help, -h : Get helpful information for action "my-command" along with its usage information.
218
+
219
+ Available actions:
220
+ [ Type "my-command help [action-name]" to get more information about that action ]
221
+
222
+ help -- The help action for command "my-command" which provides details and usage information on how to use the command.
223
+ EOS
224
+ end
225
+
226
+ it 'can execute help option to get help for a action' do
227
+ cli = Cliqr.interface do
228
+ name :my_command
229
+ description 'test command has no description'
230
+ handler TestCommand
231
+
232
+ action :action_1 do
233
+ handler TestCommand
234
+ end
235
+ end
236
+
237
+ result = cli.execute_internal %w(--help action_1), output: :buffer
238
+ expect(result[:stdout]).to eq <<-EOS
239
+ my_command action_1
240
+
241
+ USAGE:
242
+ my_command action_1 [actions] [options] [arguments]
243
+
244
+ Available options:
245
+
246
+ --help, -h : Get helpful information for action "my_command action_1" along with its usage information.
247
+
248
+ Available actions:
249
+ [ Type "my_command action_1 help [action-name]" to get more information about that action ]
250
+
251
+ help -- The help action for command "my_command action_1" which provides details and usage information on how to use the command.
252
+ EOS
253
+ end
254
+
255
+ it 'can execute help action on the action itself to get help for a action' do
256
+ cli = Cliqr.interface do
257
+ name :my_command
258
+ description 'test command has no description'
259
+ handler TestCommand
260
+
261
+ action :action_1 do
262
+ description 'test action'
263
+ handler TestCommand
264
+ end
265
+ end
266
+
267
+ result = cli.execute_internal %w(action_1 help), output: :buffer
268
+ expect(result[:stdout]).to eq <<-EOS
269
+ my_command action_1 -- test action
270
+
271
+ USAGE:
272
+ my_command action_1 [actions] [options] [arguments]
273
+
274
+ Available options:
275
+
276
+ --help, -h : Get helpful information for action "my_command action_1" along with its usage information.
277
+
278
+ Available actions:
279
+ [ Type "my_command action_1 help [action-name]" to get more information about that action ]
280
+
281
+ help -- The help action for command "my_command action_1" which provides details and usage information on how to use the command.
282
+ EOS
283
+ end
284
+
285
+ it 'can execute help action on itself' do
286
+ cli = Cliqr.interface do
287
+ name :my_command
288
+ description 'test command has no description'
289
+ handler TestCommand
290
+
291
+ action :action_1 do
292
+ description 'test action'
293
+ handler TestCommand
294
+ end
295
+ end
296
+
297
+ result = cli.execute_internal %w(help help), output: :buffer
298
+ expect(result[:stdout]).to eq <<-EOS
299
+ my_command help -- The help action for command "my_command" which provides details and usage information on how to use the command.
300
+
301
+ USAGE:
302
+ my_command help [arguments]
303
+ EOS
304
+ end
305
+
306
+ it 'does not allow help action to take more than one argument' do
307
+ cli = Cliqr.interface do
308
+ name :my_command
309
+ description 'test command has no description'
310
+ handler TestCommand
311
+
312
+ action :action_1 do
313
+ description 'test action'
314
+ handler TestCommand
315
+ end
316
+ end
317
+
318
+ expect { cli.execute_internal %w(help action_1 arg2), output: :buffer }.to(
319
+ raise_error(Cliqr::Error::CommandRuntimeError,
320
+ "command 'my_command help' failed\n\n" \
321
+ "Cause: Cliqr::Error::IllegalArgumentError - too many arguments for \"my_command help\" command\n"))
322
+ end
323
+
324
+ it 'can forward command to another action' do
325
+ cli = Cliqr.interface do
326
+ name :my_command
327
+ description 'test command has no description'
328
+ handler TestCommand
329
+
330
+ action :action_1 do
331
+ description 'test action'
332
+ handler do
333
+ puts 'in action_1'
334
+ forward 'my_command action_2 sub-action' # starting with base command name
335
+ puts 'back in action_1'
336
+ end
337
+ end
338
+
339
+ action 'action_2' do
340
+ handler do
341
+ puts 'in action_2'
342
+ end
343
+
344
+ action 'sub-action' do
345
+ handler do
346
+ puts 'in sub-action'
347
+ forward 'action_2' # not starting with base command name
348
+ puts 'back in sub-action'
349
+ end
350
+ end
351
+ end
352
+ end
353
+
354
+ result = cli.execute_internal ['action_1'], output: :buffer
355
+ expect(result[:stdout]).to eq <<-EOS
356
+ in action_1
357
+ in sub-action
358
+ in action_2
359
+ back in sub-action
360
+ back in action_1
361
+ EOS
362
+ end
363
+
364
+ it 'executes help for action without handler' do
365
+ cli = Cliqr.interface do
366
+ name :my_command
367
+ description 'test command has no description'
368
+ shell :disable
369
+
370
+ action :action_1 do
371
+ description 'test action'
372
+ handler TestCommand
373
+ end
374
+
375
+ action 'action_2' do
376
+ shell :disable
377
+
378
+ action 'sub-action' do
379
+ handler TestCommand
380
+ end
381
+ end
382
+ end
383
+
384
+ result = cli.execute_internal %w(my_command), output: :buffer
385
+ expect(result[:stdout]).to eq <<-EOS
386
+ my_command -- test command has no description
387
+
388
+ USAGE:
389
+ my_command [actions] [options] [arguments]
390
+
391
+ Available options:
392
+
393
+ --help, -h : Get helpful information for action "my_command" along with its usage information.
394
+
395
+ Available actions:
396
+ [ Type "my_command help [action-name]" to get more information about that action ]
397
+
398
+ action_1 -- test action
399
+
400
+ action_2
401
+
402
+ help -- The help action for command "my_command" which provides details and usage information on how to use the command.
403
+ EOS
404
+
405
+ result = cli.execute_internal %w(my_command action_2), output: :buffer
406
+ expect(result[:stdout]).to eq <<-EOS
407
+ my_command action_2
408
+
409
+ USAGE:
410
+ my_command action_2 [actions] [options] [arguments]
411
+
412
+ Available options:
413
+
414
+ --help, -h : Get helpful information for action "my_command action_2" along with its usage information.
415
+
416
+ Available actions:
417
+ [ Type "my_command action_2 help [action-name]" to get more information about that action ]
418
+
419
+ sub-action
420
+
421
+ help -- The help action for command "my_command action_2" which provides details and usage information on how to use the command.
422
+ EOS
423
+ end
424
+ end
@@ -0,0 +1,233 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'fixtures/test_command'
6
+
7
+ describe Cliqr::CLI::Executor do
8
+ it 'can execute help in command shell' do
9
+ cli = Cliqr.interface do
10
+ name 'my-command'
11
+ handler TestCommand
12
+
13
+ action :bla
14
+ end
15
+
16
+ with_input(['help']) do
17
+ result = cli.execute_internal %w(my-command shell), output: :buffer
18
+ expect(result[:stdout]).to eq <<-EOS
19
+ Starting shell for command "my-command"
20
+ my-command > help.
21
+ my-command
22
+
23
+ USAGE:
24
+ my-command [actions] [options] [arguments]
25
+
26
+ Available options:
27
+
28
+ --help, -h : Get helpful information for action "my-command" along with its usage information.
29
+
30
+ Available actions:
31
+ [ Type "my-command help [action-name]" to get more information about that action ]
32
+
33
+ bla
34
+
35
+ shell -- Execute a shell in the context of "my-command" command.
36
+
37
+ help -- The help action for command "my-command" which provides details and usage information on how to use the command.
38
+ my-command > exit.
39
+ shell exited with code 0
40
+ EOS
41
+ end
42
+ end
43
+
44
+ it 'can execute a sub action from shell' do
45
+ cli = Cliqr.interface do
46
+ name 'my-command'
47
+ handler do
48
+ puts 'base command executed'
49
+ end
50
+
51
+ action :foo do
52
+ handler do
53
+ puts 'foo executed'
54
+ end
55
+
56
+ action :bar do
57
+ handler do
58
+ puts 'bar executed'
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ with_input(['', 'my-command', 'foo', 'foo bar', 'foo bar help']) do
65
+ result = cli.execute_internal %w(my-command shell), output: :buffer
66
+ expect(result[:stdout]).to eq <<-EOS
67
+ Starting shell for command "my-command"
68
+ my-command > .
69
+ base command executed
70
+ my-command > my-command.
71
+ base command executed
72
+ my-command > foo.
73
+ foo executed
74
+ my-command > foo bar.
75
+ bar executed
76
+ my-command > foo bar help.
77
+ my-command foo bar
78
+
79
+ USAGE:
80
+ my-command foo bar [actions] [options] [arguments]
81
+
82
+ Available options:
83
+
84
+ --help, -h : Get helpful information for action "my-command foo bar" along with its usage information.
85
+
86
+ Available actions:
87
+ [ Type "my-command foo bar help [action-name]" to get more information about that action ]
88
+
89
+ help -- The help action for command "my-command foo bar" which provides details and usage information on how to use the command.
90
+ my-command > exit.
91
+ shell exited with code 0
92
+ EOS
93
+ end
94
+ end
95
+
96
+ it 'does not allow shell action if shell config is disabled' do
97
+ cli = Cliqr.interface do
98
+ name 'my-command'
99
+ shell :disable
100
+ arguments :disable
101
+ end
102
+ expect { cli.execute_internal %w(my-command shell) }.to(
103
+ raise_error(Cliqr::Error::IllegalArgumentError, 'invalid command argument "shell"'))
104
+ end
105
+
106
+ it 'can handle errors in shell' do
107
+ cli = Cliqr.interface do
108
+ name 'my-command'
109
+ handler TestCommand
110
+ arguments :disable
111
+
112
+ action :foo do
113
+ handler do
114
+ fail StandardError, 'I failed!'
115
+ end
116
+
117
+ action :bar do
118
+ handler TestCommand
119
+ end
120
+ end
121
+ end
122
+
123
+ with_input(['unknown', '--opt-1 val', 'foo']) do
124
+ result = cli.execute_internal %w(my-command shell), output: :buffer
125
+ expect(result[:stdout]).to eq <<-EOS
126
+ Starting shell for command "my-command"
127
+ my-command > unknown.
128
+ invalid command argument "unknown"
129
+ my-command > --opt-1 val.
130
+ unknown option "--opt-1"
131
+ my-command > foo.
132
+ command 'my-command foo' failed
133
+
134
+ Cause: StandardError - I failed!
135
+ my-command > exit.
136
+ shell exited with code 0
137
+ EOS
138
+ end
139
+ end
140
+
141
+ describe 'illegal shell operations' do
142
+ it 'does not allow shell action if there are no sub-actions' do
143
+ cli = Cliqr.interface do
144
+ name 'my-command'
145
+ help :disable
146
+ handler TestCommand
147
+ arguments :disable
148
+ end
149
+ expect { cli.execute_internal %w(my-command shell) }.to(
150
+ raise_error(Cliqr::Error::IllegalArgumentError, 'invalid command argument "shell"'))
151
+ end
152
+
153
+ it 'does not allow shell in shell for base command' do
154
+ cli = Cliqr.interface do
155
+ name 'my-command'
156
+
157
+ action :foo do
158
+ action :bar
159
+ end
160
+ end
161
+
162
+ with_input(['shell']) do
163
+ result = cli.execute_internal %w(my-command shell), output: :buffer
164
+ expect(result[:stdout]).to eq <<-EOS
165
+ Starting shell for command "my-command"
166
+ my-command > shell.
167
+ command 'my-command shell' failed
168
+
169
+ Cause: Cliqr::Error::IllegalCommandError - Cannot run another shell within an already running shell
170
+ my-command > exit.
171
+ shell exited with code 0
172
+ EOS
173
+ end
174
+ end
175
+
176
+ it 'does not allow shell in shell for sub action' do
177
+ cli = Cliqr.interface do
178
+ name 'my-command'
179
+
180
+ action :foo do
181
+ action :bar
182
+ end
183
+ end
184
+
185
+ with_input(['shell']) do
186
+ result = cli.execute_internal %w(my-command foo shell), output: :buffer
187
+ expect(result[:stdout]).to eq <<-EOS
188
+ Starting shell for command "my-command foo"
189
+ my-command foo > shell.
190
+ command 'my-command foo shell' failed
191
+
192
+ Cause: Cliqr::Error::IllegalCommandError - Cannot run another shell within an already running shell
193
+ my-command foo > exit.
194
+ shell exited with code 0
195
+ EOS
196
+ end
197
+
198
+ with_input(['foo shell']) do
199
+ result = cli.execute_internal %w(my-command shell), output: :buffer
200
+ expect(result[:stdout]).to eq <<-EOS
201
+ Starting shell for command "my-command"
202
+ my-command > foo shell.
203
+ command 'my-command foo shell' failed
204
+
205
+ Cause: Cliqr::Error::IllegalCommandError - Cannot run another shell within an already running shell
206
+ my-command > exit.
207
+ shell exited with code 0
208
+ EOS
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ def with_input(lines, &block)
215
+ old_stdin = $stdin
216
+ $stdin = TestIO.new(lines)
217
+ block.call
218
+ ensure
219
+ $stdin = old_stdin
220
+ end
221
+
222
+ # A test class for wrapping stdin
223
+ class TestIO
224
+ def initialize(lines)
225
+ @lines = lines.reverse
226
+ end
227
+
228
+ def gets
229
+ input = "#{@lines.length > 0 ? @lines.pop : 'exit'}"
230
+ puts "#{input}."
231
+ "#{input}\n"
232
+ end
233
+ end