hammer_cli 0.0.9 → 0.0.10

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/doc/creating_apipie_commands.md +296 -0
  4. data/doc/creating_commands.md +547 -0
  5. data/doc/developer_docs.md +5 -926
  6. data/doc/development_tips.md +30 -0
  7. data/doc/writing_a_plugin.md +90 -0
  8. data/lib/hammer_cli/abstract.rb +31 -11
  9. data/lib/hammer_cli/apipie/resource.rb +14 -6
  10. data/lib/hammer_cli/apipie/write_command.rb +14 -5
  11. data/lib/hammer_cli/exception_handler.rb +7 -4
  12. data/lib/hammer_cli/options/normalizers.rb +27 -0
  13. data/lib/hammer_cli/output/adapter/abstract.rb +8 -8
  14. data/lib/hammer_cli/output/adapter/csv.rb +37 -4
  15. data/lib/hammer_cli/output/adapter/silent.rb +2 -2
  16. data/lib/hammer_cli/output/dsl.rb +3 -1
  17. data/lib/hammer_cli/output/output.rb +24 -19
  18. data/lib/hammer_cli/utils.rb +18 -0
  19. data/lib/hammer_cli/version.rb +1 -1
  20. data/lib/hammer_cli.rb +1 -0
  21. data/test/unit/abstract_test.rb +296 -0
  22. data/test/unit/apipie/command_test.rb +270 -0
  23. data/test/unit/apipie/fake_api.rb +101 -0
  24. data/test/unit/apipie/read_command_test.rb +34 -0
  25. data/test/unit/apipie/write_command_test.rb +38 -0
  26. data/test/unit/exception_handler_test.rb +45 -0
  27. data/test/unit/main_test.rb +47 -0
  28. data/test/unit/options/normalizers_test.rb +148 -0
  29. data/test/unit/options/option_definition_test.rb +43 -0
  30. data/test/unit/output/adapter/abstract_test.rb +96 -0
  31. data/test/unit/output/adapter/base_test.rb +27 -0
  32. data/test/unit/output/adapter/csv_test.rb +75 -0
  33. data/test/unit/output/adapter/table_test.rb +58 -0
  34. data/test/unit/output/definition_test.rb +27 -0
  35. data/test/unit/output/dsl_test.rb +119 -0
  36. data/test/unit/output/fields_test.rb +97 -0
  37. data/test/unit/output/formatters_test.rb +83 -0
  38. data/test/unit/output/output_test.rb +104 -0
  39. data/test/unit/settings_test.rb +106 -0
  40. data/test/unit/test_helper.rb +20 -0
  41. data/test/unit/utils_test.rb +35 -0
  42. data/test/unit/validator_test.rb +142 -0
  43. metadata +112 -35
  44. data/LICENSE +0 -5
  45. data/hammer_cli_complete +0 -13
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+ require File.join(File.dirname(__FILE__), 'fake_api')
3
+
4
+ describe HammerCLI::Apipie::WriteCommand do
5
+
6
+ let(:cmd) { HammerCLI::Apipie::WriteCommand.new("") }
7
+ let(:cmd_run) { cmd.run([]) }
8
+
9
+ it "should raise exception when no action is defined" do
10
+ cmd.stubs(:handle_exception).returns(HammerCLI::EX_SOFTWARE)
11
+ cmd_run.must_equal HammerCLI::EX_SOFTWARE
12
+ end
13
+
14
+ context "resource defined" do
15
+
16
+ before :each do
17
+ cmd.class.resource FakeApi::Resources::Architecture, "some_action"
18
+
19
+ arch = FakeApi::Resources::Architecture.new
20
+ arch.expects(:some_action).returns([])
21
+ FakeApi::Resources::Architecture.stubs(:new).returns(arch)
22
+ end
23
+
24
+ it "should perform a call to api when resource is defined" do
25
+ cmd_run.must_equal 0
26
+ end
27
+
28
+ context "output" do
29
+ it "should print success message" do
30
+ cmd.class.success_message "XXX"
31
+ proc { cmd_run }.must_output /.*XXX.*/
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
@@ -0,0 +1,45 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+
4
+ describe HammerCLI::ExceptionHandler do
5
+
6
+ before(:each) do
7
+ @log_output = Logging::Appenders['__test__']
8
+ @log_output.reset
9
+ end
10
+
11
+ let(:output) { HammerCLI::Output::Output.new }
12
+ let(:handler) { HammerCLI::ExceptionHandler.new(:output => output)}
13
+ let(:heading) { "Something went wrong" }
14
+
15
+ it "should handle unauthorized" do
16
+ output.expects(:print_error).with(heading, "Invalid username or password")
17
+ handler.handle_exception(RestClient::Unauthorized.new, :heading => heading)
18
+ end
19
+
20
+ it "should handle general exception" do
21
+ output.expects(:print_error).with(heading, "Error: message")
22
+ handler.handle_exception(Exception.new('message'), :heading => heading)
23
+ end
24
+
25
+ it "should handle unknown exception" do
26
+ output.expects(:print_error).with(heading, "Error: message")
27
+ MyException = Class.new(Exception)
28
+ handler.handle_exception(MyException.new('message'), :heading => heading)
29
+ end
30
+
31
+ it "should handle resource not found" do
32
+ ex = RestClient::ResourceNotFound.new
33
+ output.expects(:print_error).with(heading, ex.message)
34
+ handler.handle_exception(ex, :heading => heading)
35
+ end
36
+
37
+ it "should log the error" do
38
+ ex = RestClient::ResourceNotFound.new
39
+ output.default_adapter = :silent
40
+ handler.handle_exception(ex)
41
+ @log_output.readline.strip.must_equal 'ERROR Exception : Resource Not Found'
42
+ end
43
+
44
+ end
45
+
@@ -0,0 +1,47 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe HammerCLI::MainCommand do
4
+
5
+ describe "loading context" do
6
+
7
+ let(:context) { {} }
8
+ let(:cmd) { HammerCLI::MainCommand.new("", context) }
9
+
10
+ before :each do
11
+ cmd.stubs(:execute).returns(1)
12
+ end
13
+
14
+ describe "username" do
15
+
16
+ it "should prioritize parameter" do
17
+ cmd.run(["-uuser"])
18
+ context[:username].must_equal "user"
19
+ end
20
+
21
+ it "should prioritize parameter 2" do
22
+ cmd.run([])
23
+ context[:username].must_equal nil
24
+ end
25
+
26
+ end
27
+
28
+
29
+ describe "password" do
30
+
31
+ it "should prioritize parameter" do
32
+ cmd.run(["-ppassword"])
33
+ context[:password].must_equal "password"
34
+ end
35
+
36
+ it "should prioritize parameter" do
37
+ cmd.run([])
38
+ context[:password].must_equal nil
39
+ end
40
+
41
+ end
42
+
43
+
44
+ end
45
+
46
+ end
47
+
@@ -0,0 +1,148 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+
3
+
4
+ describe HammerCLI::Options::Normalizers do
5
+
6
+
7
+ describe 'abstract' do
8
+
9
+ let(:formatter) { HammerCLI::Options::Normalizers::AbstractNormalizer.new }
10
+
11
+ it "should raise exception on format" do
12
+ proc { formatter.format(nil) }.must_raise NotImplementedError
13
+ end
14
+
15
+ end
16
+
17
+ describe 'list' do
18
+
19
+ let(:formatter) { HammerCLI::Options::Normalizers::List.new }
20
+
21
+ it "should return empty array on nil" do
22
+ formatter.format(nil).must_equal []
23
+ end
24
+
25
+ it "should return empty array on empty string" do
26
+ formatter.format("").must_equal []
27
+ end
28
+
29
+ it "should parse a string" do
30
+ formatter.format("a").must_equal ['a']
31
+ end
32
+
33
+ it "should parse a comma separated string" do
34
+ formatter.format("a,b,c").must_equal ['a', 'b', 'c']
35
+ end
36
+ end
37
+
38
+
39
+ describe 'key_value_list' do
40
+
41
+ let(:formatter) { HammerCLI::Options::Normalizers::KeyValueList.new }
42
+
43
+ it "should return empty array on nil" do
44
+ formatter.format(nil).must_equal({})
45
+ end
46
+
47
+ it "should return empty array on empty string" do
48
+ formatter.format("").must_equal({})
49
+ end
50
+
51
+ it "should parse a string" do
52
+ proc { formatter.format("a") }.must_raise ArgumentError
53
+ end
54
+
55
+ it "should parse a comma separated string" do
56
+ formatter.format("a=1,b=2,c=3").must_equal({'a' => '1', 'b' => '2', 'c' => '3'})
57
+ end
58
+
59
+ it "should parse a comma separated string 2" do
60
+ proc { formatter.format("a=1,b,c=3") }.must_raise ArgumentError
61
+ end
62
+ end
63
+
64
+ describe 'bool' do
65
+
66
+ let(:formatter) { HammerCLI::Options::Normalizers::Bool.new }
67
+
68
+ it "should return true on true" do
69
+ formatter.format("true").must_equal(true)
70
+ formatter.format("TRUE").must_equal(true)
71
+ end
72
+
73
+ it "should return true on t" do
74
+ formatter.format("t").must_equal(true)
75
+ formatter.format("T").must_equal(true)
76
+ end
77
+
78
+ it "should return true on yes" do
79
+ formatter.format("yes").must_equal(true)
80
+ formatter.format("YES").must_equal(true)
81
+ end
82
+
83
+ it "should return true on y" do
84
+ formatter.format("y").must_equal(true)
85
+ formatter.format("Y").must_equal(true)
86
+ end
87
+
88
+ it "should return true on 1" do
89
+ formatter.format("1").must_equal(true)
90
+ end
91
+
92
+ it "should return false on false" do
93
+ formatter.format("false").must_equal(false)
94
+ formatter.format("FALSE").must_equal(false)
95
+ end
96
+
97
+ it "should return false on f" do
98
+ formatter.format("f").must_equal(false)
99
+ formatter.format("F").must_equal(false)
100
+ end
101
+
102
+ it "should return false on no" do
103
+ formatter.format("no").must_equal(false)
104
+ formatter.format("NO").must_equal(false)
105
+ end
106
+
107
+ it "should return false on n" do
108
+ formatter.format("n").must_equal(false)
109
+ formatter.format("N").must_equal(false)
110
+ end
111
+
112
+ it "should return false on 0" do
113
+ formatter.format("0").must_equal(false)
114
+ end
115
+
116
+ it "should raise exception on nil" do
117
+ proc { formatter.format(nil) }.must_raise ArgumentError
118
+ end
119
+
120
+ it "should raise exception on other values" do
121
+ proc { formatter.format('unknown') }.must_raise ArgumentError
122
+ end
123
+ end
124
+
125
+ describe 'enum' do
126
+
127
+ let(:formatter) { HammerCLI::Options::Normalizers::Enum.new ['a', 'b'] }
128
+
129
+ it "should return return value when in the list" do
130
+ formatter.format("a").must_equal("a")
131
+ end
132
+
133
+ it "should rise argument error when the value is nil" do
134
+ proc { formatter.format(nil) }.must_raise ArgumentError
135
+ end
136
+
137
+ it "should rise argument error when the value is not in the list" do
138
+ proc { formatter.format("c") }.must_raise ArgumentError
139
+ end
140
+
141
+ it "should list allowed values in description" do
142
+ formatter.description.must_equal("One of 'a', 'b'")
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
@@ -0,0 +1,43 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+
3
+ # require 'hammer_cli/options/option_definition'
4
+
5
+ describe HammerCLI::Options::OptionDefinition do
6
+
7
+ class FakeFormatter < HammerCLI::Options::Normalizers::AbstractNormalizer
8
+ def format(val)
9
+ ">>>#{val}<<<"
10
+ end
11
+ end
12
+
13
+ class TestOptionFormattersCmd < HammerCLI::AbstractCommand
14
+ option "--test-format", "TEST_FORMAT", "Test option with a formatter",
15
+ :format => FakeFormatter.new,
16
+ :default => "A"
17
+ end
18
+
19
+ describe "formatters" do
20
+
21
+ it "should use formatter to format a default value" do
22
+ opt = TestOptionFormattersCmd.find_option("--test-format")
23
+
24
+ opt_instance = opt.of(TestOptionFormattersCmd.new([]))
25
+ opt_instance.read.must_equal '>>>A<<<'
26
+ end
27
+
28
+ it "should use formatter as a conversion block" do
29
+ opt = TestOptionFormattersCmd.find_option("--test-format")
30
+
31
+ opt_instance = opt.of(TestOptionFormattersCmd.new([]))
32
+ # clamp api changed in 0.6.2
33
+ if opt_instance.respond_to? :write
34
+ opt_instance.write('B')
35
+ else
36
+ opt_instance.take('B')
37
+ end
38
+ opt_instance.read.must_equal '>>>B<<<'
39
+ end
40
+ end
41
+
42
+ end
43
+
@@ -0,0 +1,96 @@
1
+ require File.join(File.dirname(__FILE__), '../../test_helper')
2
+
3
+ describe HammerCLI::Output::Adapter::Abstract do
4
+
5
+ let(:adapter_class) { HammerCLI::Output::Adapter::Abstract }
6
+ let(:adapter) { HammerCLI::Output::Adapter::Abstract.new }
7
+
8
+
9
+ it "should have tags" do
10
+ adapter.tags.must_be_kind_of Array
11
+ end
12
+
13
+ class UnknownTestFormatter < HammerCLI::Output::Formatters::FieldFormatter
14
+ def format(data)
15
+ data+'.'
16
+ end
17
+
18
+ def tags
19
+ [:unknown]
20
+ end
21
+ end
22
+
23
+ it "should filter formatters with incompatible tags" do
24
+
25
+ HammerCLI::Output::Formatters::FormatterLibrary.expects(:new).with({ :type => [] })
26
+ adapter = adapter_class.new({}, {:type => [UnknownTestFormatter.new]})
27
+ end
28
+
29
+ it "should keep compatible formatters" do
30
+ formatter = UnknownTestFormatter.new
31
+ HammerCLI::Output::Formatters::FormatterLibrary.expects(:new).with({ :type => [formatter] })
32
+ # set :unknown tag to abstract
33
+ adapter_class.any_instance.stubs(:tags).returns([:unknown])
34
+ adapter = adapter_class.new({}, {:type => [formatter]})
35
+ end
36
+
37
+ it "should put serializers first" do
38
+ formatter1 = UnknownTestFormatter.new
39
+ formatter1.stubs(:tags).returns([])
40
+ formatter2 = UnknownTestFormatter.new
41
+ formatter2.stubs(:tags).returns([:flat])
42
+ HammerCLI::Output::Formatters::FormatterLibrary.expects(:new).with({ :type => [formatter2, formatter1] })
43
+ # set :unknown tag to abstract
44
+ adapter_class.any_instance.stubs(:tags).returns([:flat])
45
+ adapter = adapter_class.new({}, {:type => [formatter1, formatter2]})
46
+ end
47
+
48
+
49
+ context "messages" do
50
+ it "should print message to stdout" do
51
+ proc { adapter.print_message("MESSAGE") }.must_output(/.*MESSAGE.*/, "")
52
+ end
53
+
54
+ it "should print formatted message with parameters" do
55
+ proc { adapter.print_message("MESSAGE %{a}s, %{b}s", :a => 'A', :b => 'B') }.must_output(/.*MESSAGE A, B.*/, "")
56
+ end
57
+
58
+ end
59
+
60
+ it "should raise not implemented on print_records" do
61
+ proc { adapter.print_records([], []) }.must_raise NotImplementedError
62
+ end
63
+
64
+ context "error messages" do
65
+ it "should print error message to stderr" do
66
+ proc { adapter.print_error("MESSAGE") }.must_output("", /.*MESSAGE.*/)
67
+ end
68
+
69
+ let(:expected_output) { "MESSAGE:\n"+
70
+ " error\n"+
71
+ " message\n"+
72
+ " details\n"
73
+ }
74
+
75
+ let(:expected_formatted_output) { "MESSAGE A, B:\n"+
76
+ " error A\n"+
77
+ " error B\n"
78
+ }
79
+
80
+ it "should print list details of error to stderr" do
81
+ proc { adapter.print_error("MESSAGE", ["error", "message", "details"]) }.must_output("", expected_output)
82
+ end
83
+
84
+ it "should print string details of error to stderr" do
85
+ proc { adapter.print_error("MESSAGE", "error\nmessage\ndetails") }.must_output("", expected_output)
86
+ end
87
+
88
+ it "should print formatted message with parameters" do
89
+ proc {
90
+ adapter.print_error("MESSAGE %{a}s, %{b}s", ["error %{a}s", "error %{b}s"], :a => 'A', :b => 'B')
91
+ }.must_output("", expected_formatted_output)
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__), '../../test_helper')
2
+
3
+ describe HammerCLI::Output::Adapter::Base do
4
+
5
+ let(:adapter) { HammerCLI::Output::Adapter::Base.new }
6
+
7
+ context "print_records" do
8
+
9
+ let(:field_name) { Fields::DataField.new(:path => [:name], :label => "Name") }
10
+ let(:fields) {
11
+ [field_name]
12
+ }
13
+ let(:data) {[{
14
+ :name => "John Doe"
15
+ }]}
16
+
17
+ it "should print field name" do
18
+ proc { adapter.print_records(fields, data) }.must_output(/.*Name[ ]*:.*/, "")
19
+ end
20
+
21
+ it "should print field value" do
22
+ proc { adapter.print_records(fields, data) }.must_output(/.*John Doe.*/, "")
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,75 @@
1
+ require File.join(File.dirname(__FILE__), '../../test_helper')
2
+
3
+ describe HammerCLI::Output::Adapter::CSValues do
4
+
5
+ let(:adapter) { HammerCLI::Output::Adapter::CSValues.new }
6
+
7
+ context "print_records" do
8
+
9
+ let(:field_name) { Fields::DataField.new(:path => [:name], :label => "Name") }
10
+ let(:field_started_at) { Fields::DataField.new(:path => [:started_at], :label => "Started At") }
11
+ let(:fields) {
12
+ [field_name, field_started_at]
13
+ }
14
+ let(:data) {[{
15
+ :name => "John Doe",
16
+ :started_at => "2000"
17
+ }]}
18
+
19
+ it "should print column name" do
20
+ proc { adapter.print_records(fields, data) }.must_output(/.*Name,Started At.*/, "")
21
+ end
22
+
23
+ it "should print field value" do
24
+ proc { adapter.print_records(fields, data) }.must_output(/.*John Doe.*/, "")
25
+ end
26
+
27
+ context "handle ids" do
28
+ let(:field_id) { Fields::Id.new(:path => [:some_id], :label => "Id") }
29
+ let(:fields) {
30
+ [field_name, field_id]
31
+ }
32
+
33
+ it "should ommit column of type Id by default" do
34
+ out, err = capture_io { adapter.print_records(fields, data) }
35
+ out.wont_match(/.*Id.*/)
36
+ out.wont_match(/.*John Doe,.*/)
37
+ end
38
+
39
+ it "should print column of type Id when --show-ids is set" do
40
+ adapter = HammerCLI::Output::Adapter::CSValues.new( { :show_ids => true } )
41
+ out, err = capture_io { adapter.print_records(fields, data) }
42
+ out.must_match(/.*Id.*/)
43
+ end
44
+ end
45
+
46
+ context "formatters" do
47
+ it "should apply formatters" do
48
+ class DotFormatter < HammerCLI::Output::Formatters::FieldFormatter
49
+ def format(data)
50
+ '-DOT-'
51
+ end
52
+ end
53
+
54
+ adapter = HammerCLI::Output::Adapter::CSValues.new({}, { :DataField => [ DotFormatter.new ]})
55
+ out, err = capture_io { adapter.print_records(fields, data) }
56
+ out.must_match(/.*-DOT-.*/)
57
+ end
58
+ end
59
+ end
60
+
61
+ context "print message" do
62
+
63
+ it "shoud print a message" do
64
+ proc { adapter.print_message("SOME MESSAGE") }.must_output("Message\nSOME MESSAGE\n", "")
65
+ end
66
+
67
+ it "should print message, id and name of created/updated record" do
68
+ proc {
69
+ adapter.print_message("SOME MESSAGE", "id" => 83, "name" => "new_record")
70
+ }.must_output("Message,Id,Name\nSOME MESSAGE,83,new_record\n", "")
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,58 @@
1
+ require File.join(File.dirname(__FILE__), '../../test_helper')
2
+
3
+ describe HammerCLI::Output::Adapter::Table do
4
+
5
+ let(:adapter) { HammerCLI::Output::Adapter::Table.new }
6
+
7
+ context "print_records" do
8
+
9
+ let(:field_name) { Fields::DataField.new(:path => [:name], :label => "Name") }
10
+ let(:fields) {
11
+ [field_name]
12
+ }
13
+ let(:data) {[{
14
+ :name => "John Doe"
15
+ }]}
16
+
17
+ it "should print column name" do
18
+ proc { adapter.print_records(fields, data) }.must_output(/.*NAME.*/, "")
19
+ end
20
+
21
+ it "should print field value" do
22
+ proc { adapter.print_records(fields, data) }.must_output(/.*John Doe.*/, "")
23
+ end
24
+
25
+ context "handle ids" do
26
+ let(:field_id) { Fields::Id.new(:path => [:some_id], :label => "Id") }
27
+ let(:fields) {
28
+ [field_name, field_id]
29
+ }
30
+
31
+ it "should ommit column of type Id by default" do
32
+ out, err = capture_io { adapter.print_records(fields, data) }
33
+ out.wont_match(/.*ID.*/)
34
+ end
35
+
36
+ it "should print column of type Id when --show-ids is set" do
37
+ adapter = HammerCLI::Output::Adapter::Table.new( { :show_ids => true } )
38
+ out, err = capture_io { adapter.print_records(fields, data) }
39
+ out.must_match(/.*ID.*/)
40
+ end
41
+ end
42
+
43
+ context "formatters" do
44
+ it "should apply formatters" do
45
+ class DotFormatter < HammerCLI::Output::Formatters::FieldFormatter
46
+ def format(data)
47
+ '-DOT-'
48
+ end
49
+ end
50
+
51
+ adapter = HammerCLI::Output::Adapter::Table.new({}, { :DataField => [ DotFormatter.new ]})
52
+ out, err = capture_io { adapter.print_records(fields, data) }
53
+ out.must_match(/.*-DOT-.*/)
54
+ end
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+
3
+
4
+
5
+ describe HammerCLI::Output::Definition do
6
+
7
+ let(:definition) { HammerCLI::Output::Definition.new }
8
+ let(:last_field) { definition.fields[-1] }
9
+ let(:field_count) { definition.fields.length }
10
+
11
+ it "should be able to add field" do
12
+ definition.fields << Fields::Field.new
13
+ field_count.must_equal 1
14
+ end
15
+
16
+ it "append should allow to add data from another definition" do
17
+ another_def = HammerCLI::Output::Definition.new
18
+ another_def.fields << Fields::Field.new
19
+ another_def.fields << Fields::Field.new
20
+
21
+ definition.append another_def.fields
22
+ field_count.must_equal another_def.fields.length
23
+ definition.fields.must_equal another_def.fields
24
+ end
25
+
26
+ end
27
+