convoy 1.0.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 (109) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.irbrc +3 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +8 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +22 -0
  9. data/README.md +705 -0
  10. data/Rakefile +1 -0
  11. data/convoy.gemspec +24 -0
  12. data/examples/.my_apprc +24 -0
  13. data/examples/basic +10 -0
  14. data/examples/basic_config_file +16 -0
  15. data/examples/basic_conflicts +17 -0
  16. data/examples/basic_depends_on +25 -0
  17. data/examples/basic_flags +15 -0
  18. data/examples/basic_options +14 -0
  19. data/examples/basic_options_multi +15 -0
  20. data/examples/basic_require_arguments +17 -0
  21. data/examples/basic_texts +21 -0
  22. data/examples/basic_validations +21 -0
  23. data/examples/basic_with_everything +30 -0
  24. data/examples/commands/example_command.rb +13 -0
  25. data/examples/suite_complex +65 -0
  26. data/examples/suite_simple +19 -0
  27. data/examples/suite_with_sub_commands +94 -0
  28. data/lib/convoy.rb +83 -0
  29. data/lib/convoy/action_command/base.rb +85 -0
  30. data/lib/convoy/action_command/escort_utility_command.rb +53 -0
  31. data/lib/convoy/app.rb +127 -0
  32. data/lib/convoy/arguments.rb +20 -0
  33. data/lib/convoy/auto_options.rb +71 -0
  34. data/lib/convoy/error/error.rb +33 -0
  35. data/lib/convoy/formatter/command.rb +87 -0
  36. data/lib/convoy/formatter/commands.rb +37 -0
  37. data/lib/convoy/formatter/cursor_position.rb +29 -0
  38. data/lib/convoy/formatter/default_help_formatter.rb +117 -0
  39. data/lib/convoy/formatter/global_command.rb +17 -0
  40. data/lib/convoy/formatter/option.rb +152 -0
  41. data/lib/convoy/formatter/options.rb +28 -0
  42. data/lib/convoy/formatter/shell_command_executor.rb +49 -0
  43. data/lib/convoy/formatter/stream_output_formatter.rb +88 -0
  44. data/lib/convoy/formatter/string_grid.rb +108 -0
  45. data/lib/convoy/formatter/string_splitter.rb +50 -0
  46. data/lib/convoy/formatter/terminal.rb +30 -0
  47. data/lib/convoy/global_pre_parser.rb +43 -0
  48. data/lib/convoy/logger.rb +75 -0
  49. data/lib/convoy/option_dependency_validator.rb +82 -0
  50. data/lib/convoy/option_parser.rb +155 -0
  51. data/lib/convoy/setup/configuration/generator.rb +75 -0
  52. data/lib/convoy/setup/configuration/instance.rb +34 -0
  53. data/lib/convoy/setup/configuration/loader.rb +43 -0
  54. data/lib/convoy/setup/configuration/locator/base.rb +19 -0
  55. data/lib/convoy/setup/configuration/locator/chaining.rb +29 -0
  56. data/lib/convoy/setup/configuration/locator/descending_to_home.rb +23 -0
  57. data/lib/convoy/setup/configuration/locator/executing_script_directory.rb +15 -0
  58. data/lib/convoy/setup/configuration/locator/specified_directory.rb +21 -0
  59. data/lib/convoy/setup/configuration/merge_tool.rb +38 -0
  60. data/lib/convoy/setup/configuration/reader.rb +36 -0
  61. data/lib/convoy/setup/configuration/writer.rb +46 -0
  62. data/lib/convoy/setup/dsl/action.rb +17 -0
  63. data/lib/convoy/setup/dsl/command.rb +67 -0
  64. data/lib/convoy/setup/dsl/config_file.rb +13 -0
  65. data/lib/convoy/setup/dsl/global.rb +29 -0
  66. data/lib/convoy/setup/dsl/options.rb +81 -0
  67. data/lib/convoy/setup_accessor.rb +206 -0
  68. data/lib/convoy/trollop.rb +861 -0
  69. data/lib/convoy/utils.rb +21 -0
  70. data/lib/convoy/validator.rb +45 -0
  71. data/spec/integration/basic_config_file_spec.rb +126 -0
  72. data/spec/integration/basic_conflicts_spec.rb +47 -0
  73. data/spec/integration/basic_depends_on_spec.rb +275 -0
  74. data/spec/integration/basic_options_spec.rb +41 -0
  75. data/spec/integration/basic_options_with_multi_spec.rb +30 -0
  76. data/spec/integration/basic_spec.rb +38 -0
  77. data/spec/integration/basic_validations_spec.rb +77 -0
  78. data/spec/integration/basic_with_arguments_spec.rb +35 -0
  79. data/spec/integration/basic_with_text_fields_spec.rb +21 -0
  80. data/spec/integration/suite_simple_spec.rb +45 -0
  81. data/spec/integration/suite_sub_command_spec.rb +51 -0
  82. data/spec/lib/convoy/action_command/base_spec.rb +200 -0
  83. data/spec/lib/convoy/formatter/command_spec.rb +238 -0
  84. data/spec/lib/convoy/formatter/global_command_spec.rb +50 -0
  85. data/spec/lib/convoy/formatter/option_spec.rb +300 -0
  86. data/spec/lib/convoy/formatter/shell_command_executor_spec.rb +59 -0
  87. data/spec/lib/convoy/formatter/stream_output_formatter_spec.rb +214 -0
  88. data/spec/lib/convoy/formatter/string_grid_spec.rb +59 -0
  89. data/spec/lib/convoy/formatter/string_splitter_spec.rb +50 -0
  90. data/spec/lib/convoy/formatter/terminal_spec.rb +19 -0
  91. data/spec/lib/convoy/setup/configuration/generator_spec.rb +101 -0
  92. data/spec/lib/convoy/setup/configuration/loader_spec.rb +79 -0
  93. data/spec/lib/convoy/setup/configuration/locator/chaining_spec.rb +81 -0
  94. data/spec/lib/convoy/setup/configuration/locator/descending_to_home_spec.rb +57 -0
  95. data/spec/lib/convoy/setup/configuration/locator/executing_script_directory_spec.rb +29 -0
  96. data/spec/lib/convoy/setup/configuration/locator/specified_directory_spec.rb +33 -0
  97. data/spec/lib/convoy/setup/configuration/merge_tool_spec.rb +41 -0
  98. data/spec/lib/convoy/setup/configuration/reader_spec.rb +41 -0
  99. data/spec/lib/convoy/setup/configuration/writer_spec.rb +75 -0
  100. data/spec/lib/convoy/setup_accessor_spec.rb +226 -0
  101. data/spec/lib/convoy/utils_spec.rb +30 -0
  102. data/spec/spec_helper.rb +29 -0
  103. data/spec/support/integration_helpers.rb +2 -0
  104. data/spec/support/matchers/execute_action_for_command_matcher.rb +21 -0
  105. data/spec/support/matchers/execute_action_with_arguments_matcher.rb +25 -0
  106. data/spec/support/matchers/execute_action_with_options_matcher.rb +29 -0
  107. data/spec/support/matchers/exit_with_code_matcher.rb +29 -0
  108. data/spec/support/shared_contexts/integration_setup.rb +34 -0
  109. metadata +292 -0
@@ -0,0 +1,79 @@
1
+ describe Convoy::Setup::Configuration::Loader do
2
+ include FakeFS::SpecHelpers
3
+
4
+ let(:loader) { Convoy::Setup::Configuration::Loader.new(setup, auto_options) }
5
+ let(:setup) { Convoy::SetupAccessor.new(app_configuration) }
6
+ let(:auto_options) { Convoy::AutoOptions.new({}) }
7
+
8
+ describe "#default_config_path" do
9
+ subject { loader.default_config_path }
10
+
11
+ context "when setup has config file" do
12
+ let(:app_configuration) do
13
+ Convoy::Setup::Dsl::Global.new do |app|
14
+ app.config_file '.test1rc'
15
+ app.options do |opts|
16
+ opts.opt :option1, "option1", :short => :none, :long => '--option1', :type => :string
17
+ end
18
+ end
19
+ end
20
+ it { subject.should == File.join(File.expand_path('~'), '.test1rc') }
21
+ end
22
+
23
+ context "when setup has no config file" do
24
+ let(:app_configuration) do
25
+ Convoy::Setup::Dsl::Global.new do |app|
26
+ app.options do |opts|
27
+ opts.opt :option1, "option1", :short => :none, :long => '--option1', :type => :string
28
+ end
29
+ end
30
+ end
31
+ it { subject.should be_nil }
32
+ end
33
+ end
34
+
35
+ describe "#configuration" do
36
+ subject { loader.configuration }
37
+
38
+
39
+ context "when setup has config file" do
40
+ context "and config file is autocreatable" do
41
+ let(:app_configuration) do
42
+ Convoy::Setup::Dsl::Global.new do |app|
43
+ app.config_file '.test1rc', :autocreate => true
44
+ app.options do |opts|
45
+ opts.opt :option1, "option1", :short => :none, :long => '--option1', :type => :string
46
+ end
47
+ end
48
+ end
49
+ it { subject.should_not be_nil }
50
+ it { subject.should_not be_empty }
51
+ it { subject; File.should exist File.join(File.expand_path('~'), '.test1rc') }
52
+ end
53
+
54
+ context "and config file is not autocreatable" do
55
+ let(:app_configuration) do
56
+ Convoy::Setup::Dsl::Global.new do |app|
57
+ app.config_file '.test1rc'
58
+ app.options do |opts|
59
+ opts.opt :option1, "option1", :short => :none, :long => '--option1', :type => :string
60
+ end
61
+ end
62
+ end
63
+ it { subject.should_not be_nil }
64
+ it { subject.should be_empty }
65
+ end
66
+ end
67
+
68
+ context "when setup has no config file" do
69
+ let(:app_configuration) do
70
+ Convoy::Setup::Dsl::Global.new do |app|
71
+ app.options do |opts|
72
+ opts.opt :option1, "option1", :short => :none, :long => '--option1', :type => :string
73
+ end
74
+ end
75
+ end
76
+ it { subject.should be_empty }
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,81 @@
1
+ describe Convoy::Setup::Configuration::Locator::Chaining do
2
+ include FakeFS::SpecHelpers
3
+
4
+ let(:locator) { Convoy::Setup::Configuration::Locator::Chaining.new(filename) }
5
+ let(:filename) { '.blahrc' }
6
+ let(:home) { File.expand_path('~') }
7
+ let(:pwd) { File.expand_path(File.join(home, 'blah', 'yadda', 'foo')) }
8
+ let(:executing_directory) { File.expand_path(File.dirname($0)) }
9
+
10
+ let(:descending_locator) { Convoy::Setup::Configuration::Locator::DescendingToHome.new(filename) }
11
+ let(:executing_script_locator) { Convoy::Setup::Configuration::Locator::ExecutingScriptDirectory.new(filename) }
12
+
13
+ before do
14
+ FileUtils.mkdir_p(executing_directory)
15
+ FileUtils.mkdir_p(home)
16
+ FileUtils.mkdir_p(pwd)
17
+ Dir.chdir(pwd)
18
+ end
19
+
20
+ describe "#add_locator" do
21
+ subject { locator.add_locator("blah") }
22
+ it { subject.should == locator }
23
+ it { subject.locators.size.should == 1 }
24
+ end
25
+
26
+ describe "#locate" do
27
+ subject { locator.locate }
28
+
29
+ context "when no sub-locators specified" do
30
+ it { subject.should be_nil }
31
+ end
32
+
33
+ context "when executing directory locator specified" do
34
+ before do
35
+ locator.add_locator(executing_script_locator)
36
+ end
37
+
38
+ let(:path) { File.join(executing_directory, filename) }
39
+
40
+ context "and file should be found by it" do
41
+ before do
42
+ File.open(path, 'w') { |f| f.write("hello") }
43
+ end
44
+
45
+ it { subject.should == path }
46
+ end
47
+
48
+ context "and file should not be found by it" do
49
+ it { subject.should be_nil }
50
+ end
51
+ end
52
+
53
+ context "when descending and current script locators are specified" do
54
+ before do
55
+ locator.
56
+ add_locator(descending_locator).
57
+ add_locator(executing_script_locator)
58
+ end
59
+
60
+ context "and file should not be found" do
61
+ it { subject.should be_nil }
62
+ end
63
+
64
+ context "and file should be found by current script locator" do
65
+ let(:path) { File.join(executing_directory, filename) }
66
+ before do
67
+ File.open(path, 'w') { |f| f.write("hello") }
68
+ end
69
+ it { subject.should == path }
70
+ end
71
+
72
+ context "and file should be found by descending locator" do
73
+ let(:path) { File.join(home, filename) }
74
+ before do
75
+ File.open(path, 'w') { |f| f.write("hello") }
76
+ end
77
+ it { subject.should == path }
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,57 @@
1
+ describe Convoy::Setup::Configuration::Locator::DescendingToHome do
2
+ include FakeFS::SpecHelpers
3
+
4
+ let(:locator) { Convoy::Setup::Configuration::Locator::DescendingToHome.new(filename) }
5
+ let(:filename) { '.blahrc' }
6
+ let(:home) { File.expand_path('~') }
7
+ let(:pwd) { File.expand_path(File.join(home, 'blah', 'yadda', 'foo')) }
8
+
9
+ before do
10
+ FileUtils.mkdir_p(home)
11
+ FileUtils.mkdir_p(pwd)
12
+ Dir.chdir(pwd)
13
+ end
14
+
15
+ describe "#locate" do
16
+ subject { locator.locate }
17
+
18
+ context "when file does not exist anywhere in the relevant directories" do
19
+ it { subject.should be_nil }
20
+ end
21
+
22
+ context "when file exists" do
23
+ context "in the top directory of search" do
24
+ let(:file_location) { File.expand_path(File.join(home, 'blah', 'yadda', 'foo')) }
25
+ let(:path) { File.join(file_location, filename) }
26
+
27
+ before do
28
+ File.open(path, 'w') { |f| f.write("hello") }
29
+ end
30
+
31
+ it { subject.should == path }
32
+ end
33
+
34
+ context "in one of the middle directories of search" do
35
+ let(:file_location) { File.expand_path(File.join(home, 'blah', 'yadda')) }
36
+ let(:path) { File.join(file_location, filename) }
37
+
38
+ before do
39
+ File.open(path, 'w') { |f| f.write("hello") }
40
+ end
41
+
42
+ it { subject.should == path }
43
+ end
44
+
45
+ context "in the home directory" do
46
+ let(:file_location) { File.expand_path(home) }
47
+ let(:path) { File.join(file_location, filename) }
48
+
49
+ before do
50
+ File.open(path, 'w') { |f| f.write("hello") }
51
+ end
52
+
53
+ it { subject.should == path }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,29 @@
1
+ describe Convoy::Setup::Configuration::Locator::ExecutingScriptDirectory do
2
+ include FakeFS::SpecHelpers
3
+
4
+ let(:locator) { Convoy::Setup::Configuration::Locator::ExecutingScriptDirectory.new(filename) }
5
+ let(:filename) { '.blahrc' }
6
+ let(:directory) { File.expand_path(File.dirname($0)) }
7
+ let(:path) { File.join(directory, filename) }
8
+
9
+ describe "#locate" do
10
+ subject { locator.locate }
11
+
12
+ context "when file does not exist in directory" do
13
+ before do
14
+ FileUtils.mkdir_p(directory)
15
+ end
16
+
17
+ it { subject.should be_nil }
18
+ end
19
+
20
+ context "when file exists in directory" do
21
+ before do
22
+ FileUtils.mkdir_p(directory)
23
+ File.open(path, 'w') { |f| f.write("hello") }
24
+ end
25
+
26
+ it { subject.should == path }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ describe Convoy::Setup::Configuration::Locator::SpecifiedDirectory do
2
+ include FakeFS::SpecHelpers
3
+
4
+ let(:locator) { Convoy::Setup::Configuration::Locator::SpecifiedDirectory.new(filename, directory) }
5
+ let(:filename) { '.blahrc' }
6
+ let(:directory) { '/usr/alan/yadda' }
7
+ let(:path) { File.join(directory, filename) }
8
+
9
+ describe "#locate" do
10
+ subject { locator.locate }
11
+
12
+ context "when directory does not exist" do
13
+ it { subject.should be_nil }
14
+ end
15
+
16
+ context "when file does not exist in directory" do
17
+ before do
18
+ FileUtils.mkdir_p(directory)
19
+ end
20
+
21
+ it { subject.should be_nil }
22
+ end
23
+
24
+ context "when file exists in directory" do
25
+ before do
26
+ FileUtils.mkdir_p(directory)
27
+ File.open(path, 'w') { |f| f.write("hello") }
28
+ end
29
+
30
+ it { subject.should == path }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ describe Convoy::Setup::Configuration::MergeTool do
2
+ let(:merge_tool) { Convoy::Setup::Configuration::MergeTool.new(new_config_hash, old_config_hash) }
3
+
4
+ describe "#config_hash" do
5
+ subject { merge_tool.config_hash }
6
+
7
+ context "when there is a new command" do
8
+ let(:old_config_hash) { {:global => {:commands => {}, :options => {:option1 => nil}}, :user => {:hello => :world}} }
9
+ let(:new_config_hash) { {:global => {:commands => {:command1 => {:commands => {}, :options => {}}}, :options => {}}, :user => {}} }
10
+
11
+ it("should be part of the config") { subject[:global][:commands][:command1].should_not be_nil }
12
+ end
13
+
14
+ context "when there is a new option for an old command" do
15
+ let(:old_config_hash) { {:global => {:commands => {}, :options => {:option1 => nil}}, :user => {:hello => :world}} }
16
+ let(:new_config_hash) { {:global => {:commands => {}, :options => {:option1 => nil, :option2 => nil}}, :user => {}} }
17
+
18
+ it("should be part of the config") { subject[:global][:options].keys.should include(:option2) }
19
+ end
20
+
21
+ context "when an old option for a command is no longer there" do
22
+ let(:old_config_hash) { {:global => {:commands => {}, :options => {:option1 => nil}}, :user => {:hello => :world}} }
23
+ let(:new_config_hash) { {:global => {:commands => {}, :options => {:option2 => nil}}, :user => {}} }
24
+
25
+ it("should not be part of the config") { subject[:global][:options].keys.should_not include(:option1) }
26
+ end
27
+
28
+ context "when old config hash has user supplied value for option" do
29
+ let(:old_config_hash) { {:global => {:commands => {}, :options => {:option1 => :hello}}, :user => {:hello => :world}} }
30
+ let(:new_config_hash) { {:global => {:commands => {}, :options => {:option1 => nil}}, :user => {}} }
31
+
32
+ it("should retain the user supplied value for option") { subject[:global][:options][:option1].should == :hello }
33
+ end
34
+
35
+ context "when old user hash has values" do
36
+ let(:old_config_hash) { {:global => {:commands => {}, :options => {}}, :user => {:hello => :world}} }
37
+ let(:new_config_hash) { {:global => {:commands => {}, :options => {}}, :user => {}} }
38
+ it("should retain the user config values") { subject[:user][:hello].should == :world }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ describe Convoy::Setup::Configuration::Reader do
2
+ include FakeFS::SpecHelpers
3
+
4
+ let(:reader) { Convoy::Setup::Configuration::Reader.new(path) }
5
+ let(:path) { '/usr/alan/blah.json' }
6
+ let(:data) { {:hello => :world} }
7
+
8
+ describe "#read" do
9
+ subject { reader.read }
10
+
11
+ context "when path not given" do
12
+ let(:path) { nil }
13
+ it { subject.should be_empty }
14
+ end
15
+
16
+ context "when configuration file not present" do
17
+ before do
18
+ FileUtils.mkdir_p(File.dirname path)
19
+ end
20
+ it { subject.should be_empty }
21
+ end
22
+
23
+ context "when configuration file not JSON" do
24
+ before do
25
+ FileUtils.mkdir_p(File.dirname path)
26
+ File.open(path, 'w') { |f| f.write("hello") }
27
+ end
28
+ it { subject.should be_empty }
29
+ end
30
+
31
+ context "when configuration file present" do
32
+ let(:config_data) { {:hello => :blah} }
33
+
34
+ before do
35
+ FileUtils.mkdir_p(File.dirname path)
36
+ File.open(path, 'w') { |f| f.write(JSON.pretty_generate(config_data)) }
37
+ end
38
+ it { subject.data.should == {:hello => "blah"} }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,75 @@
1
+ describe Convoy::Setup::Configuration::Writer do
2
+ include FakeFS::SpecHelpers
3
+ let(:writer) { Convoy::Setup::Configuration::Writer.new(path, data) }
4
+ let(:path) { '/usr/alan/blah.json' }
5
+ let(:data) { {:hello => :world} }
6
+
7
+ describe "#write" do
8
+ subject { writer.write }
9
+
10
+ context "when path is nil" do
11
+ let(:path) { nil }
12
+ it { subject.should be_empty }
13
+ end
14
+
15
+ context "when path is not nil" do
16
+ context "and the file does not exist" do
17
+ it { subject.should_not be_empty }
18
+ it { subject.path.should == path }
19
+ it { subject.data.should == data }
20
+ it("file should have the right contents") do
21
+ subject
22
+ json = File.read(path)
23
+ hash = ::JSON.parse(json)
24
+ actual_data = Convoy::Utils.symbolize_keys(hash)
25
+ actual_data.should == {:hello => "world"}
26
+ end
27
+ end
28
+
29
+ context "and the file already exists" do
30
+ before do
31
+ FileUtils.mkdir_p(File.dirname path)
32
+ FileUtils.touch(path)
33
+ end
34
+ it { subject.should be_empty }
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "#update" do
40
+ subject { writer.update }
41
+
42
+ context "when file does not exist" do
43
+ it { subject.should_not be_empty }
44
+ it { subject.path.should == path }
45
+ it { subject.data.should == data }
46
+ it("file should have the right contents") do
47
+ subject
48
+ json = File.read(path)
49
+ hash = ::JSON.parse(json)
50
+ actual_data = Convoy::Utils.symbolize_keys(hash)
51
+ actual_data.should == {:hello => "world"}
52
+ end
53
+ end
54
+
55
+ context "when file does exit" do
56
+ let(:previous_data) { {:hello => :blah} }
57
+ let(:data) { {:hello => :world, :foo => :bar} }
58
+ before do
59
+ FileUtils.mkdir_p(File.dirname path)
60
+ File.open(path, 'w') { |f| f.write(JSON.pretty_generate(previous_data)) }
61
+ end
62
+
63
+ it { subject.should_not be_empty }
64
+ it { subject.path.should == path }
65
+ it { subject.data.should == data }
66
+ it("file should have the right contents") do
67
+ subject
68
+ json = File.read(path)
69
+ hash = ::JSON.parse(json)
70
+ actual_data = Convoy::Utils.symbolize_keys(hash)
71
+ actual_data.should == {:hello => "blah", :foo => "bar"}
72
+ end
73
+ end
74
+ end
75
+ end