taketo 0.0.6 → 0.0.7

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