hammer_cli 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
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
+