hammer_cli 0.19.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/bin/hammer-complete +28 -0
  3. data/config/cli_config.template.yml +2 -0
  4. data/config/hammer.completion +5 -0
  5. data/doc/commands_extension.md +12 -0
  6. data/doc/creating_commands.md +100 -0
  7. data/doc/installation.md +47 -4
  8. data/doc/release_notes.md +32 -6
  9. data/lib/hammer_cli.rb +1 -0
  10. data/lib/hammer_cli/abstract.rb +61 -4
  11. data/lib/hammer_cli/apipie/api_connection.rb +5 -1
  12. data/lib/hammer_cli/apipie/command.rb +3 -2
  13. data/lib/hammer_cli/apipie/option_builder.rb +15 -13
  14. data/lib/hammer_cli/apipie/option_definition.rb +9 -7
  15. data/lib/hammer_cli/bash.rb +2 -0
  16. data/lib/hammer_cli/bash/completion.rb +159 -0
  17. data/lib/hammer_cli/bash/prebuild_command.rb +21 -0
  18. data/lib/hammer_cli/command_extensions.rb +21 -1
  19. data/lib/hammer_cli/connection.rb +4 -0
  20. data/lib/hammer_cli/exception_handler.rb +11 -2
  21. data/lib/hammer_cli/full_help.rb +8 -1
  22. data/lib/hammer_cli/help/builder.rb +10 -3
  23. data/lib/hammer_cli/logger_watch.rb +1 -1
  24. data/lib/hammer_cli/main.rb +5 -3
  25. data/lib/hammer_cli/options/normalizers.rb +7 -3
  26. data/lib/hammer_cli/options/option_definition.rb +26 -6
  27. data/lib/hammer_cli/options/option_family.rb +114 -0
  28. data/lib/hammer_cli/options/predefined.rb +1 -1
  29. data/lib/hammer_cli/output/adapter/abstract.rb +1 -5
  30. data/lib/hammer_cli/output/adapter/base.rb +1 -1
  31. data/lib/hammer_cli/output/adapter/csv.rb +3 -2
  32. data/lib/hammer_cli/output/adapter/json.rb +14 -3
  33. data/lib/hammer_cli/output/adapter/silent.rb +1 -1
  34. data/lib/hammer_cli/output/adapter/table.rb +27 -8
  35. data/lib/hammer_cli/output/adapter/yaml.rb +6 -3
  36. data/lib/hammer_cli/output/output.rb +2 -4
  37. data/lib/hammer_cli/settings.rb +2 -1
  38. data/lib/hammer_cli/subcommand.rb +25 -1
  39. data/lib/hammer_cli/testing/command_assertions.rb +2 -2
  40. data/lib/hammer_cli/utils.rb +22 -0
  41. data/lib/hammer_cli/version.rb +1 -1
  42. data/locale/ca/LC_MESSAGES/hammer-cli.mo +0 -0
  43. data/locale/de/LC_MESSAGES/hammer-cli.mo +0 -0
  44. data/locale/en/LC_MESSAGES/hammer-cli.mo +0 -0
  45. data/locale/en_GB/LC_MESSAGES/hammer-cli.mo +0 -0
  46. data/locale/es/LC_MESSAGES/hammer-cli.mo +0 -0
  47. data/locale/fr/LC_MESSAGES/hammer-cli.mo +0 -0
  48. data/locale/it/LC_MESSAGES/hammer-cli.mo +0 -0
  49. data/locale/ja/LC_MESSAGES/hammer-cli.mo +0 -0
  50. data/locale/ko/LC_MESSAGES/hammer-cli.mo +0 -0
  51. data/locale/pt_BR/LC_MESSAGES/hammer-cli.mo +0 -0
  52. data/locale/ru/LC_MESSAGES/hammer-cli.mo +0 -0
  53. data/locale/zh_CN/LC_MESSAGES/hammer-cli.mo +0 -0
  54. data/locale/zh_TW/LC_MESSAGES/hammer-cli.mo +0 -0
  55. data/man/hammer.1.gz +0 -0
  56. data/test/unit/abstract_test.rb +23 -2
  57. data/test/unit/apipie/api_connection_test.rb +1 -0
  58. data/test/unit/apipie/option_builder_test.rb +8 -0
  59. data/test/unit/bash_test.rb +138 -0
  60. data/test/unit/command_extensions_test.rb +67 -49
  61. data/test/unit/exception_handler_test.rb +44 -0
  62. data/test/unit/help/builder_test.rb +22 -0
  63. data/test/unit/options/option_family_test.rb +48 -0
  64. data/test/unit/output/adapter/base_test.rb +58 -0
  65. data/test/unit/output/adapter/csv_test.rb +63 -1
  66. data/test/unit/output/adapter/json_test.rb +61 -0
  67. data/test/unit/output/adapter/table_test.rb +70 -1
  68. data/test/unit/output/adapter/yaml_test.rb +59 -0
  69. data/test/unit/output/output_test.rb +3 -3
  70. metadata +82 -71
  71. data/hammer_cli_complete +0 -13
@@ -55,4 +55,48 @@ describe HammerCLI::ExceptionHandler do
55
55
  assert_match /ERROR Exception : (Resource )?Not Found/, @log_output.readline.strip
56
56
  end
57
57
 
58
+ it "should print default prompts for standard missing arguments" do
59
+ params = %w[login mail]
60
+ heading = 'Could not create user:'
61
+ body = "Missing arguments for '--login', '--mail'."
62
+ ex = ApipieBindings::MissingArgumentsError.new(params)
63
+ output.expects(:print_error).with(heading, body)
64
+ handler.handle_exception(ex, heading: heading)
65
+ end
66
+
67
+ it "should print right prompts for nested missing arguments" do
68
+ params = %w[user[login] user[mail]]
69
+ heading = 'Could not create user:'
70
+ body = "Missing arguments for '--login', '--mail'."
71
+ ex = ApipieBindings::MissingArgumentsError.new(params)
72
+ output.expects(:print_error).with(heading, body)
73
+ handler.handle_exception(ex, heading: heading)
74
+ end
75
+
76
+ it "should print simple prompts for even more nested arguments" do
77
+ params = %w[user[address][city] user[address][street]]
78
+ heading = 'Could not create user:'
79
+ body = "Missing arguments for '--address'."
80
+ ex = ApipieBindings::MissingArgumentsError.new(params)
81
+ output.expects(:print_error).with(heading, body)
82
+ handler.handle_exception(ex, heading: heading)
83
+ end
84
+
85
+ it "should print simple prompts for even more different nested arguments" do
86
+ params = %w[user[address][city] user[address][street] user[nested][par1]]
87
+ heading = 'Could not create user:'
88
+ body = "Missing arguments for '--address', '--nested'."
89
+ ex = ApipieBindings::MissingArgumentsError.new(params)
90
+ output.expects(:print_error).with(heading, body)
91
+ handler.handle_exception(ex, heading: heading)
92
+ end
93
+
94
+ it "should print default prompts for standard missing arguments" do
95
+ params = %w[opt_abc opt_a_b-c]
96
+ heading = 'Could not create user:'
97
+ body = "Missing arguments for '--opt-abc', '--opt-a-b-c'."
98
+ ex = ApipieBindings::MissingArgumentsError.new(params)
99
+ output.expects(:print_error).with(heading, body)
100
+ handler.handle_exception(ex, heading: heading)
101
+ end
58
102
  end
@@ -69,4 +69,26 @@ describe HammerCLI::Help::Builder do
69
69
  help.string.strip.must_equal expected_output
70
70
  end
71
71
  end
72
+
73
+ describe 'option family' do
74
+ let(:family) { Class.new(HammerCLI::Options::OptionFamily) }
75
+
76
+ it 'prints option families' do
77
+ fm1 = family.new
78
+ fm1.parent(['--option-zzz'], 'OPT', 'Some description')
79
+ fm1.child(['--option-aaa'], 'OPT', 'Some description')
80
+ fm2 = family.new
81
+ fm2.parent(['--option-bbb'], 'OPT', 'Some description')
82
+ fm2.child(['--option-yyy'], 'OPT', 'Some description')
83
+
84
+ options = fm1.all + fm2.all
85
+ help.add_list('Options', options)
86
+
87
+ help.string.strip.must_equal [
88
+ 'Options:',
89
+ ' --option[-yyy|-bbb] Some description',
90
+ ' --option[-aaa|-zzz] Some description',
91
+ ].join("\n")
92
+ end
93
+ end
72
94
  end
@@ -0,0 +1,48 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe HammerCLI::Options::OptionFamily do
4
+ let(:family) do
5
+ HammerCLI::Options::OptionFamily.new(
6
+ deprecated: { '--test-one' => 'Use --test-two instead' }
7
+ )
8
+ end
9
+ let(:first_option) { HammerCLI::Apipie::OptionDefinition.new("--test-one", '', '') }
10
+ let(:second_option) { HammerCLI::Apipie::OptionDefinition.new("--test-two", '', '') }
11
+ let(:third_option) { HammerCLI::Apipie::OptionDefinition.new("--test-three", '', '') }
12
+ let(:full_family) do
13
+ family.parent('--test-one', '', 'Test').family.child('--test-two', '', '').family
14
+ end
15
+
16
+ describe 'switch' do
17
+ it 'returns nil if family is empty' do
18
+ family.switch.must_be_nil
19
+ end
20
+
21
+ it 'returns parent switch if family has no children' do
22
+ family.parent('--test-one', '', '')
23
+ family.switch.must_equal '--test-one'
24
+ end
25
+
26
+ it 'returns switch based on members' do
27
+ full_family.switch.must_equal '--test[-two|-one]'
28
+ end
29
+ end
30
+
31
+ describe 'description' do
32
+ it 'returns parent description if nothing passed to initializer' do
33
+ full_family.description.must_equal full_family.head.help[1]
34
+ end
35
+
36
+ it 'returns description with deprecation message' do
37
+ full_family.description.must_equal 'Test (--test-one is deprecated: Use --test-two instead)'
38
+ end
39
+ end
40
+
41
+ describe 'adopt' do
42
+ it 'appends an option to children' do
43
+ full_family.adopt(third_option)
44
+ full_family.children.size.must_equal 2
45
+ third_option.family.must_equal full_family
46
+ end
47
+ end
48
+ end
@@ -199,6 +199,64 @@ describe HammerCLI::Output::Adapter::Base do
199
199
  proc { adapter.print_collection(fields, data) }.must_output(expected_output)
200
200
  end
201
201
 
202
+ context 'printing by chunks' do
203
+ let(:context) { { show_ids: true } }
204
+ let(:collection_count) { 30 }
205
+ let(:collection_data) do
206
+ collection = collection_count.times.each_with_object([]) do |t, r|
207
+ r << { id: t, name: "John #{t}"}
208
+ end
209
+ HammerCLI::Output::RecordCollection.new(collection)
210
+ end
211
+ let(:fields) { [id, name] }
212
+
213
+ it 'prints single chunk' do
214
+ expected_output = collection_count.times.each_with_object([]) do |t, r|
215
+ r << ["Id: #{t}", "Name: John #{t}", "\n"].join("\n")
216
+ end.flatten(1).join
217
+
218
+ proc do
219
+ adapter.print_collection(fields, collection_data)
220
+ end.must_output(expected_output)
221
+ end
222
+
223
+ it 'prints first chunk' do
224
+ expected_output = 10.times.each_with_object([]) do |t, r|
225
+ r << ["Id: #{t}", "Name: John #{t}", "\n"].join("\n")
226
+ end.flatten(1).join
227
+
228
+ proc do
229
+ adapter.print_collection(
230
+ fields, collection_data[0...10], current_chunk: :first
231
+ )
232
+ end.must_output(expected_output)
233
+ end
234
+
235
+ it 'prints another chunk' do
236
+ expected_output = (10...20).each_with_object([]) do |t, r|
237
+ r << ["Id: #{t}", "Name: John #{t}", "\n"].join("\n")
238
+ end.flatten(1).join
239
+
240
+ proc do
241
+ adapter.print_collection(
242
+ fields, collection_data[10...20], current_chunk: :another
243
+ )
244
+ end.must_output(expected_output)
245
+ end
246
+
247
+ it 'prints last chunk' do
248
+ expected_output = (20...30).each_with_object([]) do |t, r|
249
+ r << ["Id: #{t}", "Name: John #{t}", "\n"].join("\n")
250
+ end.flatten(1).join
251
+
252
+ proc do
253
+ adapter.print_collection(
254
+ fields, collection_data[20...30], current_chunk: :last
255
+ )
256
+ end.must_output(expected_output)
257
+ end
258
+ end
259
+
202
260
  context "show ids" do
203
261
 
204
262
  let(:context) { {:show_ids => true} }
@@ -9,7 +9,7 @@ describe HammerCLI::Output::Adapter::CSValues do
9
9
  end
10
10
 
11
11
  context "print_collection" do
12
-
12
+ let(:field_id) { Fields::Id.new(:path => [:id], :label => "Id") }
13
13
  let(:field_name) { Fields::Field.new(:path => [:name], :label => "Name") }
14
14
  let(:field_started_at) { Fields::Field.new(:path => [:started_at], :label => "Started At") }
15
15
  let(:field_login) { Fields::Field.new(:path => [:login], :label => "Login") }
@@ -220,6 +220,68 @@ describe HammerCLI::Output::Adapter::CSValues do
220
220
  end
221
221
  end
222
222
 
223
+ context 'printing by chunks' do
224
+ let(:adapter) { HammerCLI::Output::Adapter::CSValues.new(show_ids: true) }
225
+ let(:collection_count) { 30 }
226
+ let(:collection_data) do
227
+ collection = collection_count.times.each_with_object([]) do |t, r|
228
+ r << { id: t, name: "John #{t}"}
229
+ end
230
+ HammerCLI::Output::RecordCollection.new(collection)
231
+ end
232
+ let(:fields) { [field_id, field_name] }
233
+
234
+ it 'prints single chunk' do
235
+ expected_output = collection_count.times.each_with_object([]) do |t, r|
236
+ r << ["#{t}", "John #{t}"].join(',')
237
+ end.flatten(1).unshift('Id,Name').join("\n") + "\n"
238
+
239
+ out, _err = capture_io do
240
+ adapter.print_collection(fields, collection_data)
241
+ end
242
+ out.must_equal(expected_output)
243
+ end
244
+
245
+ it 'prints first chunk' do
246
+ expected_output = 10.times.each_with_object([]) do |t, r|
247
+ r << ["#{t}", "John #{t}"].join(',')
248
+ end.flatten(1).unshift('Id,Name').join("\n") + "\n"
249
+
250
+ out, _err = capture_io do
251
+ adapter.print_collection(
252
+ fields, collection_data[0...10], current_chunk: :first
253
+ )
254
+ end
255
+ out.must_equal(expected_output)
256
+ end
257
+
258
+ it 'prints another chunk' do
259
+ expected_output = (10...20).each_with_object([]) do |t, r|
260
+ r << ["#{t}", "John #{t}"].join(',')
261
+ end.flatten(1).join("\n") + "\n"
262
+
263
+ out, _err = capture_io do
264
+ adapter.print_collection(
265
+ fields, collection_data[10...20], current_chunk: :another
266
+ )
267
+ end
268
+ out.must_equal(expected_output)
269
+ end
270
+
271
+ it 'prints last chunk' do
272
+ expected_output = (20...30).each_with_object([]) do |t, r|
273
+ r << ["#{t}", "John #{t}"].join(',')
274
+ end.flatten(1).join("\n") + "\n"
275
+
276
+ out, _err = capture_io do
277
+ adapter.print_collection(
278
+ fields, collection_data[20...30], current_chunk: :last
279
+ )
280
+ end
281
+ out.must_equal(expected_output)
282
+ end
283
+ end
284
+
223
285
  context "output_stream" do
224
286
 
225
287
  let(:tempfile) { Tempfile.new("output_stream_csv_test_temp") }
@@ -316,6 +316,67 @@ describe HammerCLI::Output::Adapter::Json do
316
316
  end
317
317
  end
318
318
 
319
+ context 'printing by chunks' do
320
+ let(:settings) { HammerCLI::Settings }
321
+ let(:context) { { show_ids: true, capitalization: HammerCLI.capitalization } }
322
+ let(:collection_count) { 30 }
323
+ let(:collection) do
324
+ collection_count.times.each_with_object([]) do |t, r|
325
+ r << { id: t, name: "John #{t}"}
326
+ end
327
+ end
328
+ let(:collection_data) do
329
+ HammerCLI::Output::RecordCollection.new(collection)
330
+ end
331
+ let(:fields) { [id, name] }
332
+
333
+ before do
334
+ settings.load(ui: { capitalization: :downcase })
335
+ end
336
+
337
+ it 'prints single chunk' do
338
+ expected_output = JSON.pretty_generate(collection) + "\n"
339
+
340
+ out, _err = capture_io do
341
+ adapter.print_collection(fields, collection_data)
342
+ end
343
+ out.must_equal(expected_output)
344
+ end
345
+
346
+ it 'prints first chunk' do
347
+ expected_output = JSON.pretty_generate(collection[0...10])[0...-2] + ",\n"
348
+
349
+ out, _err = capture_io do
350
+ adapter.print_collection(
351
+ fields, collection_data[0...10], current_chunk: :first
352
+ )
353
+ end
354
+ out.must_equal(expected_output)
355
+ end
356
+
357
+ it 'prints another chunk' do
358
+ expected_output = JSON.pretty_generate(collection[10...20])[2...-2] + ",\n"
359
+
360
+ out, _err = capture_io do
361
+ adapter.print_collection(
362
+ fields, collection_data[10...20], current_chunk: :another
363
+ )
364
+ end
365
+ out.must_equal(expected_output)
366
+ end
367
+
368
+ it 'prints last chunk' do
369
+ expected_output = JSON.pretty_generate(collection[20...30])[2..-1] + "\n"
370
+
371
+ out, _err = capture_io do
372
+ adapter.print_collection(
373
+ fields, collection_data[20...30], current_chunk: :last
374
+ )
375
+ end
376
+ out.must_equal(expected_output)
377
+ end
378
+ end
379
+
319
380
  context "show ids" do
320
381
 
321
382
  let(:context) { {:show_ids => true} }
@@ -9,7 +9,7 @@ describe HammerCLI::Output::Adapter::Table do
9
9
  end
10
10
 
11
11
  context "print_collection" do
12
-
12
+ let(:field_id) { Fields::Id.new(:path => [:id], :label => "Id") }
13
13
  let(:field_name) { Fields::Field.new(:path => [:fullname], :label => "Name") }
14
14
  let(:field_firstname) { Fields::Field.new(:path => [:firstname], :label => "Firstname") }
15
15
  let(:field_lastname) { Fields::Field.new(:path => [:lastname], :label => "Lastname") }
@@ -380,6 +380,75 @@ describe HammerCLI::Output::Adapter::Table do
380
380
 
381
381
  end
382
382
 
383
+ context 'printing by chunks' do
384
+ let(:adapter) { HammerCLI::Output::Adapter::Table.new(show_ids: true) }
385
+ let(:collection_count) { 30 }
386
+ let(:collection_data) do
387
+ collection = collection_count.times.each_with_object([]) do |t, r|
388
+ r << { id: t, fullname: "John Doe #{t}"}
389
+ end
390
+ HammerCLI::Output::RecordCollection.new(collection)
391
+ end
392
+ let(:fields) { [field_id, field_name] }
393
+
394
+ it 'prints single chunk' do
395
+ expected_output = collection_count.times.each_with_object([]) do |t, r|
396
+ sp = t < 10 ? ' ' : ''
397
+ r << ["#{t} #{sp}| John Doe #{t}#{sp}"]
398
+ end.flatten(1).unshift(
399
+ '---|------------',
400
+ 'ID | NAME ',
401
+ "---|------------",
402
+ ).join("\n") + "\n---|------------\n"
403
+
404
+ proc do
405
+ adapter.print_collection(fields, collection_data)
406
+ end.must_output(expected_output)
407
+ end
408
+
409
+ it 'prints first chunk' do
410
+ expected_output = (0...10).each_with_object([]) do |t, r|
411
+ r << [
412
+ "#{t} | John Doe #{t}"
413
+ ]
414
+ end.flatten(1).unshift(
415
+ '---|-----------',
416
+ 'ID | NAME ',
417
+ "---|-----------",
418
+ ).join("\n") + "\n"
419
+
420
+ proc do
421
+ adapter.print_collection(
422
+ fields, collection_data[0...10], current_chunk: :first
423
+ )
424
+ end.must_output(expected_output)
425
+ end
426
+
427
+ it 'prints another chunk' do
428
+ expected_output = (10...20).each_with_object([]) do |t, r|
429
+ r << ["#{t} | John Doe #{t}"]
430
+ end.flatten(1).join("\n") + "\n"
431
+
432
+ proc do
433
+ adapter.print_collection(
434
+ fields, collection_data[10...20], current_chunk: :another
435
+ )
436
+ end.must_output(expected_output)
437
+ end
438
+ #
439
+ it 'prints last chunk' do
440
+ expected_output = (20...30).each_with_object([]) do |t, r|
441
+ r << ["#{t} | John Doe #{t}"]
442
+ end.flatten(1).join("\n") + "\n---|------------\n"
443
+
444
+ proc do
445
+ adapter.print_collection(
446
+ fields, collection_data[20...30], current_chunk: :last
447
+ )
448
+ end.must_output(expected_output)
449
+ end
450
+ end
451
+
383
452
  context "output_stream" do
384
453
 
385
454
  let(:tempfile) { Tempfile.new("output_stream_table_test_temp") }
@@ -312,6 +312,65 @@ describe HammerCLI::Output::Adapter::Yaml do
312
312
  end
313
313
  end
314
314
 
315
+ context 'printing by chunks' do
316
+ let(:context) { { show_ids: true } }
317
+ let(:collection_count) { 30 }
318
+ let(:collection) do
319
+ collection_count.times.each_with_object([]) do |t, r|
320
+ r << { id: t, name: "John #{t}"}
321
+ end
322
+ end
323
+ let(:prepared_collection) do
324
+ collection.map { |i| i.transform_keys { |k| k.to_s.capitalize } }
325
+ end
326
+ let(:collection_data) do
327
+ HammerCLI::Output::RecordCollection.new(collection)
328
+ end
329
+ let(:fields) { [id, name] }
330
+
331
+ it 'prints single chunk' do
332
+ expected_output = YAML.dump(prepared_collection)
333
+
334
+ out, _err = capture_io do
335
+ adapter.print_collection(fields, collection_data)
336
+ end
337
+ out.must_equal(expected_output)
338
+ end
339
+
340
+ it 'prints first chunk' do
341
+ expected_output = YAML.dump(prepared_collection[0...10])
342
+
343
+ out, _err = capture_io do
344
+ adapter.print_collection(
345
+ fields, collection_data[0...10], current_chunk: :first
346
+ )
347
+ end
348
+ out.must_equal(expected_output)
349
+ end
350
+
351
+ it 'prints another chunk' do
352
+ expected_output = YAML.dump(prepared_collection[10...20])[4..-1]
353
+
354
+ out, _err = capture_io do
355
+ adapter.print_collection(
356
+ fields, collection_data[10...20], current_chunk: :another
357
+ )
358
+ end
359
+ out.must_equal(expected_output)
360
+ end
361
+
362
+ it 'prints last chunk' do
363
+ expected_output = YAML.dump(prepared_collection[20...30])[4..-1]
364
+
365
+ out, _err = capture_io do
366
+ adapter.print_collection(
367
+ fields, collection_data[20...30], current_chunk: :last
368
+ )
369
+ end
370
+ out.must_equal(expected_output)
371
+ end
372
+ end
373
+
315
374
  context "show ids" do
316
375
 
317
376
  let(:context) { {:show_ids => true} }