taketo 0.0.6 → 0.0.7

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 (51) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.lock +3 -3
  3. data/README.md +20 -7
  4. data/VERSION +1 -1
  5. data/bin/taketo +30 -31
  6. data/features/config.feature +33 -3
  7. data/features/config_validation.feature +29 -4
  8. data/features/connect_to_server.feature +2 -1
  9. data/features/error_handling.feature +1 -0
  10. data/features/help.feature +5 -3
  11. data/features/support/env.rb +2 -0
  12. data/lib/taketo/associated_nodes.rb +90 -0
  13. data/lib/taketo/commands/ssh_command.rb +0 -4
  14. data/lib/taketo/config_printer_visitor.rb +68 -0
  15. data/lib/taketo/config_traverser.rb +55 -0
  16. data/lib/taketo/config_validator.rb +33 -34
  17. data/lib/taketo/config_visitor.rb +29 -0
  18. data/lib/taketo/constructs/base_construct.rb +9 -54
  19. data/lib/taketo/constructs/command.rb +9 -1
  20. data/lib/taketo/constructs/environment.rb +11 -1
  21. data/lib/taketo/constructs/project.rb +5 -0
  22. data/lib/taketo/constructs/server.rb +7 -4
  23. data/lib/taketo/dsl.rb +13 -2
  24. data/lib/taketo/support/named_nodes_collection.rb +10 -0
  25. data/lib/taketo/support.rb +0 -1
  26. data/lib/taketo.rb +2 -1
  27. data/spec/integration/dsl_integration_spec.rb +1 -1
  28. data/spec/lib/taketo/associated_nodes_spec.rb +101 -0
  29. data/spec/lib/taketo/commands/ssh_command_spec.rb +5 -15
  30. data/spec/lib/taketo/config_printer_visitor_spec.rb +112 -0
  31. data/spec/lib/taketo/config_traverser_spec.rb +63 -0
  32. data/spec/lib/taketo/config_validator_spec.rb +53 -39
  33. data/spec/lib/taketo/config_visitor_spec.rb +51 -0
  34. data/spec/lib/taketo/constructs/base_construct_spec.rb +7 -70
  35. data/spec/lib/taketo/constructs/command_spec.rb +20 -8
  36. data/spec/lib/taketo/constructs/config_spec.rb +2 -6
  37. data/spec/lib/taketo/constructs/environment_spec.rb +12 -7
  38. data/spec/lib/taketo/constructs/project_spec.rb +10 -4
  39. data/spec/lib/taketo/constructs/server_spec.rb +25 -16
  40. data/spec/lib/taketo/constructs_factory_spec.rb +12 -12
  41. data/spec/lib/taketo/destination_resolver_spec.rb +32 -32
  42. data/spec/lib/taketo/dsl_spec.rb +115 -98
  43. data/spec/lib/taketo/support/named_nodes_collection_spec.rb +49 -21
  44. data/spec/support/helpers/construct_spec_helper.rb +5 -5
  45. data/spec/support/matchers/be_appropriate_construct_matcher.rb +9 -1
  46. data/spec/support/matchers/have_accessor_matcher.rb +6 -3
  47. metadata +18 -7
  48. data/lib/taketo/config_printer.rb +0 -84
  49. data/lib/taketo/support/eval_delegator.rb +0 -25
  50. data/spec/lib/taketo/config_printer_spec.rb +0 -116
  51. data/spec/lib/taketo/support/eval_delegator_spec.rb +0 -43
@@ -1,60 +1,74 @@
1
- require File.expand_path('../../../spec_helper', __FILE__)
1
+ require 'spec_helper'
2
2
  require 'taketo/config_validator'
3
3
 
4
4
  include Taketo
5
5
 
6
6
  describe "ConfigValidator" do
7
- it "should require at least one project" do
8
- config = stub(:projects => [])
9
- expect { validator(config).validate! }.to raise_error ConfigError, /projects/i
7
+ let(:traverser) { stub(:ConfigTraverser) }
8
+
9
+ describe "#validate!" do
10
+ it "should visit all nodes with an instance of ConfigValidator::ConfigValidatorVisitor" do
11
+ validator = ConfigValidator.new(traverser)
12
+ traverser.should_receive(:visit_depth_first).with(an_instance_of(ConfigValidator::ConfigValidatorVisitor))
13
+ validator.validate!
14
+ end
10
15
  end
16
+ end
11
17
 
12
- it "should require every project to have at least one environment" do
13
- project_1 = stub(:Project, :name => :project_1, :environments => [])
14
- project_2 = stub(:Project, :name => :project_2, :environments => [stub])
18
+ describe "ConfigValidator::ConfigValidatorVisitor" do
19
+ subject(:visitor) { ConfigValidator::ConfigValidatorVisitor.new }
15
20
 
16
- config = stub(:projects => [project_1, project_2])
17
- expect { validator(config).validate! }.to raise_error ConfigError, /project_1/i
21
+ it "should require config to have projects" do
22
+ config = stub(:Config, :has_projects? => false)
23
+ error_message = /no projects/
24
+ expect { visitor.visit_config(config) }.to raise_error ConfigError, error_message
18
25
 
19
- project_1.stub(:environments => { :e => stub })
20
- expect { validator(config).validate! }.not_to raise_error ConfigError, /project_1/i
26
+ config.stub(:has_projects? => true)
27
+ expect { visitor.visit_config(config) }.not_to raise_error ConfigError, error_message
21
28
  end
22
29
 
23
- it "should require every environment to have at least one server" do
24
- environment_1 = stub(:Environment, :name => :environment_1, :servers => [])
25
- environment_2 = stub(:Environment, :name => :environment_2, :servers => [stub])
26
- project_1 = stub(:Project, :name => :project_1, :environments => [environment_1])
27
- project_2 = stub(:Project, :name => :project_2, :environments => [environment_2])
30
+ it "should require projects to have environments" do
31
+ project = stub(:Project, :has_environments? => false, :path => "my_project")
32
+ error_message = /my_project: no environments/
33
+ expect { visitor.visit_project(project) }.to raise_error ConfigError, error_message
34
+
35
+ project.stub(:has_environments? => true)
36
+ expect { visitor.visit_project(project) }.not_to raise_error ConfigError, error_message
37
+ end
28
38
 
29
- config = stub(:projects => [project_1, project_2])
30
- expect { validator(config).validate! }.to raise_error ConfigError, /environment_1/i
39
+ it "should require environments to have servers" do
40
+ environment = stub(:Environment, :has_servers? => false, :path => "my_project:my_environment")
41
+ error_message = /my_project:my_environment: no servers/
42
+ expect { visitor.visit_environment(environment) }.to raise_error ConfigError, error_message
31
43
 
32
- environment_2.stub(:servers => [stub])
33
- expect { validator(config).validate! }.not_to raise_error ConfigError, /environment_2/i
44
+ environment.stub(:has_servers? => true)
45
+ expect { visitor.visit_environment(environment) }.not_to raise_error ConfigError, error_message
34
46
  end
35
47
 
36
- describe "global server aliases" do
37
- let(:server_1) { stub(:Server) }
38
- let(:server_2) { stub(:Server) }
39
- let(:environment) { stub(:Environment, :name => :environment, :servers => [server_1, server_2]) }
40
- let(:project) { stub(:Project, :name => :project, :environments => [environment]) }
41
- let(:config) { stub(:Config, :projects => [project]) }
42
-
43
- it "should not raise error if unique" do
44
- server_1.stub(:global_alias => :foo)
45
- server_2.stub(:global_alias => :bar)
46
- expect { validator(config).validate! }.not_to raise_error(ConfigError, /alias/i)
47
- end
48
+ it "should require servers to have host" do
49
+ server = stub(:Server, :host => '', :path => "my_project:my_environment:my_server", :global_alias => nil)
50
+ error_message = /my_project:my_environment:my_server: host is not defined/
51
+ expect { visitor.visit_server(server) }.to raise_error ConfigError, error_message
48
52
 
49
- it "should raise error unless unique" do
50
- server_1.stub(:global_alias => :foo)
51
- server_2.stub(:global_alias => :foo)
52
- expect { validator(config).validate! }.to raise_error(ConfigError, /alias/i)
53
- end
53
+ server.stub(:host => 'the-host')
54
+ expect { visitor.visit_server(server) }.not_to raise_error ConfigError, error_message
55
+ end
56
+
57
+ it "should require servers to have unique global server aliases" do
58
+ server1 = stub(:Server, :host => 'the-host1', :path => "my_project:my_environment:my_server", :global_alias => 'foo')
59
+ server2 = stub(:Server, :host => 'the-host2', :path => "my_project:my_environment2:my_server3", :global_alias => 'foo')
60
+ error_message = /my_project:my_environment2:my_server3: global alias 'foo' has already been taken.*my_project:my_environment:my_server/
61
+ @visitor = visitor
62
+ expect { @visitor.visit_server(server1) }.not_to raise_error ConfigError, error_message
63
+ expect { @visitor.visit_server(server2) }.to raise_error ConfigError, error_message
54
64
  end
55
65
 
56
- def validator(config)
57
- Taketo::ConfigValidator.new(config)
66
+ it "should require commands to have command specified" do
67
+ command = stub(:Command, :command => '', :name => 'qux')
68
+ error_message = /execute/
69
+ expect { visitor.visit_command(command) }.to raise_error ConfigError, error_message
70
+ command.stub(:command => 'foo')
71
+ expect { visitor.visit_command(command) }.not_to raise_error ConfigError, error_message
58
72
  end
59
73
  end
60
74
 
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'taketo/config_visitor'
3
+
4
+ include Taketo
5
+
6
+ describe "ConfigVisitor" do
7
+ class DummyVisitor < ConfigVisitor
8
+ visit String do |s|
9
+ s.upcase
10
+ end
11
+
12
+ visit NilClass do |n|
13
+ "hooray"
14
+ end
15
+
16
+ visit Fixnum do |n|
17
+ n.to_s.reverse
18
+ end
19
+
20
+ visit Numeric do |n|
21
+ n.to_s
22
+ end
23
+ end
24
+
25
+ subject(:visitor) { DummyVisitor.new }
26
+
27
+ it "defines #visit* methods" do
28
+ expect(visitor.visit_string('qwe')).to eq('QWE')
29
+ expect(visitor.visit_nil_class(nil)).to eq('hooray')
30
+ end
31
+
32
+ it "dispatches according to class" do
33
+ expect(visitor.visit(700)).to eq("007")
34
+ end
35
+
36
+ it "takes ancestors into account" do
37
+ expect(visitor.visit(2.0)).to eq("2.0")
38
+ end
39
+
40
+ it "skips anonymous classes/modules" do
41
+ c = Class.new(Numeric) do
42
+ def to_s; "captured as Numeric"; end
43
+ end
44
+ expect(visitor.visit(c.new)).to eq("captured as Numeric")
45
+ end
46
+
47
+ it "raises if doesn't know how to visit" do
48
+ expect { visitor.visit([]) }.to raise_error /Array/
49
+ end
50
+ end
51
+
@@ -1,84 +1,21 @@
1
- require File.expand_path('../../../../spec_helper', __FILE__)
1
+ require 'spec_helper'
2
2
  require 'taketo/constructs/base_construct'
3
3
 
4
4
  include Taketo
5
5
 
6
6
  describe "BaseConstruct" do
7
- class TestConstruct < Constructs::BaseConstruct
8
- attr_reader :foos
7
+ class TestBaseConstruct < Constructs::BaseConstruct; end
9
8
 
10
- def initialize(name, foos)
11
- super(name)
12
- @foos = foos
13
- end
9
+ subject(:construct) { TestBaseConstruct.new(:my_node) }
14
10
 
15
- def find_foo(name)
16
- @foos[name]
17
- end
11
+ specify "#node_type returns demodulized snake-cased class name" do
12
+ construct.node_type.should == :test_base_construct
18
13
  end
19
14
 
20
- let(:foos) { mock(:NamedNodesCollection) }
21
- let(:construct) { TestConstruct.new(:construct, foos) }
22
-
23
- describe "#find" do
24
- context "if there is such node" do
25
- it "should return node object with corresponding name" do
26
- foos.should_receive(:[]).with(:bar).and_return(:qux)
27
- construct.find(:foo, :bar).should == :qux
28
- end
29
- end
30
-
31
- context "if there is no such command" do
32
- before(:each) { foos.should_receive(:[]).and_return(nil) }
33
-
34
- it "should yield if block given" do
35
- expect { |b| construct.find(:foo, :bar, &b) }.to yield_control
36
- end
37
-
38
- it "should raise KeyError if no block given" do
39
- expect { construct.find(:foo, :bar) }.to raise_error(KeyError)
40
- end
41
- end
15
+ specify "#qualified_name returns node type and name as string" do
16
+ expect(construct.qualified_name).to eq('test_base_construct my_node')
42
17
  end
43
18
 
44
- describe "#node_type" do
45
- it "should return demodulized snake-cased class name" do
46
- construct.node_type.should == :test_construct
47
- end
48
- end
49
-
50
- describe "nodes" do
51
- class TestConstruct2 < Constructs::BaseConstruct
52
- has_nodes :foos, :foo
53
- end
54
-
55
- let(:construct) { TestConstruct2.new(:node_name) }
56
-
57
- let(:foo) { stub(:name => :bar) }
58
-
59
- describe ".has_nodes" do
60
- it "should define add node type to class's node_types" do
61
- TestConstruct2.node_types.should include(:foos)
62
- end
63
-
64
- it "should make nodes accessible via added methods" do
65
- construct.append_foo(foo)
66
- construct.foos.should include(foo)
67
- construct.find_foo(:bar).should == foo
68
- end
69
- end
70
-
71
- describe "#nodes" do
72
- it "should return nodes collection by plural name" do
73
- construct.append_foo(foo)
74
- construct.nodes(:foos).should include(foo)
75
- end
76
-
77
- it "should raise NodesNotDefinedError if non-specified node requested" do
78
- expect { construct.nodes(:bars) }.to raise_error(NodesNotDefinedError, /bars/)
79
- end
80
- end
81
- end
82
19
  end
83
20
 
84
21
 
@@ -1,10 +1,10 @@
1
- require File.expand_path('../../../../spec_helper', __FILE__)
1
+ require 'spec_helper'
2
2
  require 'taketo/constructs/command'
3
3
 
4
4
  include Taketo
5
5
 
6
6
  describe "Command" do
7
- subject { Taketo::Constructs::Command.new(:the_command) }
7
+ subject(:command) { Taketo::Constructs::Command.new(:the_command) }
8
8
 
9
9
  it { should have_accessor(:command) }
10
10
  it { should have_accessor(:description) }
@@ -16,14 +16,26 @@ describe "Command" do
16
16
  :default_location => "/var/apps/the app")
17
17
  end
18
18
 
19
- it "should pick up server's environment variables and location" do
20
- subject.command = "rails c"
21
- subject.render(server).should == "cd /var/apps/the\\ app; FOO=bar\\ baz rails c"
19
+ it "picks up server's environment variables and location" do
20
+ command.command = "rails c"
21
+ expect(command.render(server)).to eq("cd /var/apps/the\\ app; FOO=bar\\ baz rails c")
22
22
  end
23
23
 
24
- it "should let user override default directory" do
25
- subject.command = "rails c"
26
- subject.render(server, :directory => "/var/qux").should == "cd /var/qux; FOO=bar\\ baz rails c"
24
+ it "lets user override default directory" do
25
+ command.command = "rails c"
26
+ expect(command.render(server, :directory => "/var/qux")).to eq("cd /var/qux; FOO=bar\\ baz rails c")
27
+ end
28
+ end
29
+
30
+ describe ".default" do
31
+ it "returns 'bash' command" do
32
+ expect(Taketo::Constructs::Command.default.command).to eq("bash")
33
+ end
34
+ end
35
+
36
+ describe ".explicit_command" do
37
+ it "returns given command string encapsulated" do
38
+ expect(Taketo::Constructs::Command.explicit_command("qq").command).to eq("qq")
27
39
  end
28
40
  end
29
41
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path('../../../../spec_helper', __FILE__)
1
+ require 'spec_helper'
2
2
  require 'support/helpers/construct_spec_helper'
3
3
  require 'taketo/constructs/config'
4
4
 
@@ -9,10 +9,6 @@ describe "Config" do
9
9
 
10
10
  it_behaves_like "a construct with nodes", :projects, :project
11
11
 
12
- it "should set default_destination" do
13
- subject.default_destination.should be_nil
14
- subject.default_destination = "foo:bar:baz"
15
- subject.default_destination.should == "foo:bar:baz"
16
- end
12
+ it { should have_accessor(:default_destination) }
17
13
  end
18
14
 
@@ -1,23 +1,28 @@
1
- require File.expand_path('../../../../spec_helper', __FILE__)
1
+ require 'spec_helper'
2
2
  require 'support/helpers/construct_spec_helper'
3
3
  require 'taketo/constructs/environment'
4
4
 
5
5
  include Taketo
6
6
 
7
7
  describe "Environment" do
8
- subject { Taketo::Constructs::Environment.new(:foo) }
8
+ subject(:environment) { Taketo::Constructs::Environment.new(:foo) }
9
9
 
10
- it "should have name" do
11
- subject.name.should == :foo
10
+ it "has name" do
11
+ expect(environment.name).to eq(:foo)
12
12
  end
13
13
 
14
14
  it_behaves_like "a construct with nodes", :servers, :server
15
15
 
16
- specify "#append_server should set environment attribute on a server to self" do
16
+ specify "#append_server sets environment attribute on a server to self" do
17
17
  server = mock(:Server, :name => :foo)
18
- server.should_receive(:environment=).with(subject)
19
- subject.append_server(server)
18
+ server.should_receive(:environment=).with(environment)
19
+ environment.append_server(server)
20
20
  end
21
+
22
+ specify "#project_name returns project name" do
23
+ environment.project = stub(:Project, :name => "TheProject")
24
+ expect(environment.project_name).to eq("TheProject")
25
+ end
21
26
  end
22
27
 
23
28
 
@@ -1,16 +1,22 @@
1
- require File.expand_path('../../../../spec_helper', __FILE__)
1
+ require 'spec_helper'
2
2
  require 'support/helpers/construct_spec_helper'
3
3
  require 'taketo/constructs/project'
4
4
 
5
5
  include Taketo
6
6
 
7
7
  describe "Project" do
8
- subject { Taketo::Constructs::Project.new(:foo) }
8
+ subject(:project) { Taketo::Constructs::Project.new(:foo) }
9
9
 
10
- it "should have name" do
11
- subject.name.should == :foo
10
+ it "has name" do
11
+ expect(project.name).to eq(:foo)
12
12
  end
13
13
 
14
+ specify "#append_environment sets project attribute on an environment to self" do
15
+ environment = mock(:Environment, :name => :bar)
16
+ environment.should_receive(:project=).with(project)
17
+ project.append_environment(environment)
18
+ end
19
+
14
20
  it_behaves_like "a construct with nodes", :environments, :environment
15
21
  end
16
22
 
@@ -1,42 +1,51 @@
1
- require File.expand_path('../../../../spec_helper', __FILE__)
1
+ require 'spec_helper'
2
2
  require 'support/helpers/construct_spec_helper'
3
3
  require 'taketo/constructs/server'
4
4
 
5
5
  include Taketo
6
6
 
7
7
  describe "Server" do
8
- subject { Taketo::Constructs::Server.new(:foo) }
8
+ subject(:server) { Taketo::Constructs::Server.new(:foo) }
9
9
 
10
- it "should have name" do
11
- subject.name.should == :foo
10
+ it "has name" do
11
+ expect(server.name).to eq(:foo)
12
12
  end
13
13
 
14
14
  it { should have_accessor(:host) }
15
15
  it { should have_accessor(:port) }
16
16
  it { should have_accessor(:username) }
17
17
  it { should have_accessor(:default_location) }
18
+ it { should have_accessor(:default_command) }
18
19
  it { should have_accessor(:global_alias) }
19
20
  it { should have_accessor(:identity_file) }
20
21
 
22
+ module Taketo
23
+ module Constructs
24
+ Command = Class.new unless defined? Command
25
+ end
26
+ end
27
+
28
+ it "has default command" do
29
+ Taketo::Constructs::Command.should_receive(:default).and_return(:qux)
30
+ expect(server.default_command).to eq(:qux)
31
+ end
32
+
21
33
  describe "#environment=" do
22
34
  let(:environment) { environment = stub(:Environment, :name => :the_environment) }
23
35
 
24
- it "should set environment" do
25
- subject.environment = environment
26
- subject.environment.should == environment
27
- end
36
+ it { should have_accessor(:environment, environment) }
28
37
 
29
- it "should set RAILS_ENV environment variable" do
30
- subject.environment_variables.should == {}
31
- subject.environment = environment
32
- subject.environment_variables[:RAILS_ENV].should == environment.name.to_s
38
+ it "sets RAILS_ENV environment variable" do
39
+ server.environment_variables.should == {}
40
+ server.environment = environment
41
+ expect(server.environment_variables[:RAILS_ENV]).to eq(environment.name.to_s)
33
42
  end
34
43
  end
35
44
 
36
- it "should set environment variables" do
37
- subject.env :FOO => "bar"
38
- subject.env :BAR => "baz"
39
- subject.environment_variables.should include(:FOO => "bar", :BAR => "baz")
45
+ it "sets environment variables" do
46
+ server.env :FOO => "bar"
47
+ server.env :BAR => "baz"
48
+ expect(server.environment_variables).to include(:FOO => "bar", :BAR => "baz")
40
49
  end
41
50
 
42
51
  it_behaves_like "a construct with nodes", :commands, :command
@@ -1,4 +1,4 @@
1
- require File.expand_path('../../../spec_helper', __FILE__)
1
+ require 'spec_helper'
2
2
  require 'taketo/constructs_factory'
3
3
 
4
4
  include Taketo
@@ -6,7 +6,7 @@ include Taketo
6
6
  describe "ConstructsFactory" do
7
7
  let(:factory) { Taketo::ConstructsFactory.new }
8
8
 
9
- specify "#create should delegate to appropriate method according to the type" do
9
+ specify "#create delegates to appropriate method according to the type" do
10
10
  factory.should_receive(:create_config)
11
11
  factory.create(:config)
12
12
 
@@ -14,28 +14,28 @@ describe "ConstructsFactory" do
14
14
  factory.create(:project, :foo)
15
15
  end
16
16
 
17
- specify "#create_config should create a config object" do
18
- factory.create_config.should be_an_instance_of(Taketo::Constructs::Config)
17
+ specify "#create_config creates a config object" do
18
+ expect(factory.create_config).to be_an_instance_of(Taketo::Constructs::Config)
19
19
  end
20
20
 
21
- specify "#create_project should create a project object" do
21
+ specify "#create_project creates a project object" do
22
22
  project = factory.create_project(:foo)
23
- project.should be_an_instance_of(Taketo::Constructs::Project)
23
+ expect(project).to be_an_instance_of(Taketo::Constructs::Project)
24
24
  end
25
25
 
26
- specify "#create_environment should create an environment object" do
26
+ specify "#create_environment creates an environment object" do
27
27
  environment = factory.create_environment(:foo)
28
- environment.should be_an_instance_of(Taketo::Constructs::Environment)
28
+ expect(environment).to be_an_instance_of(Taketo::Constructs::Environment)
29
29
  end
30
30
 
31
- specify "#create_server should create a server object" do
31
+ specify "#create_server creates a server object" do
32
32
  server = factory.create_server(:foo)
33
- server.should be_an_instance_of(Taketo::Constructs::Server)
33
+ expect(server).to be_an_instance_of(Taketo::Constructs::Server)
34
34
  end
35
35
 
36
- specify "#create_command should create a command object" do
36
+ specify "#create_command creates a command object" do
37
37
  command = factory.create_command(:foo)
38
- command.should be_an_instance_of(Taketo::Constructs::Command)
38
+ expect(command).to be_an_instance_of(Taketo::Constructs::Command)
39
39
  end
40
40
  end
41
41