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.
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -3
- data/README.md +20 -7
- data/VERSION +1 -1
- data/bin/taketo +30 -31
- data/features/config.feature +33 -3
- data/features/config_validation.feature +29 -4
- data/features/connect_to_server.feature +2 -1
- data/features/error_handling.feature +1 -0
- data/features/help.feature +5 -3
- data/features/support/env.rb +2 -0
- data/lib/taketo/associated_nodes.rb +90 -0
- data/lib/taketo/commands/ssh_command.rb +0 -4
- data/lib/taketo/config_printer_visitor.rb +68 -0
- data/lib/taketo/config_traverser.rb +55 -0
- data/lib/taketo/config_validator.rb +33 -34
- data/lib/taketo/config_visitor.rb +29 -0
- data/lib/taketo/constructs/base_construct.rb +9 -54
- data/lib/taketo/constructs/command.rb +9 -1
- data/lib/taketo/constructs/environment.rb +11 -1
- data/lib/taketo/constructs/project.rb +5 -0
- data/lib/taketo/constructs/server.rb +7 -4
- data/lib/taketo/dsl.rb +13 -2
- data/lib/taketo/support/named_nodes_collection.rb +10 -0
- data/lib/taketo/support.rb +0 -1
- data/lib/taketo.rb +2 -1
- data/spec/integration/dsl_integration_spec.rb +1 -1
- data/spec/lib/taketo/associated_nodes_spec.rb +101 -0
- data/spec/lib/taketo/commands/ssh_command_spec.rb +5 -15
- data/spec/lib/taketo/config_printer_visitor_spec.rb +112 -0
- data/spec/lib/taketo/config_traverser_spec.rb +63 -0
- data/spec/lib/taketo/config_validator_spec.rb +53 -39
- data/spec/lib/taketo/config_visitor_spec.rb +51 -0
- data/spec/lib/taketo/constructs/base_construct_spec.rb +7 -70
- data/spec/lib/taketo/constructs/command_spec.rb +20 -8
- data/spec/lib/taketo/constructs/config_spec.rb +2 -6
- data/spec/lib/taketo/constructs/environment_spec.rb +12 -7
- data/spec/lib/taketo/constructs/project_spec.rb +10 -4
- data/spec/lib/taketo/constructs/server_spec.rb +25 -16
- data/spec/lib/taketo/constructs_factory_spec.rb +12 -12
- data/spec/lib/taketo/destination_resolver_spec.rb +32 -32
- data/spec/lib/taketo/dsl_spec.rb +115 -98
- data/spec/lib/taketo/support/named_nodes_collection_spec.rb +49 -21
- data/spec/support/helpers/construct_spec_helper.rb +5 -5
- data/spec/support/matchers/be_appropriate_construct_matcher.rb +9 -1
- data/spec/support/matchers/have_accessor_matcher.rb +6 -3
- metadata +18 -7
- data/lib/taketo/config_printer.rb +0 -84
- data/lib/taketo/support/eval_delegator.rb +0 -25
- data/spec/lib/taketo/config_printer_spec.rb +0 -116
- data/spec/lib/taketo/support/eval_delegator_spec.rb +0 -43
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'taketo/constructs'
|
2
|
+
|
3
|
+
module Taketo
|
4
|
+
def self.downcased_construct_class_name(klass)
|
5
|
+
klass.name.gsub("Taketo::Constructs::", "").gsub(/[A-Z][^A-Z]*/) { |s| s.gsub("::", "").downcase + "_" }.chop
|
6
|
+
end
|
7
|
+
|
8
|
+
class ConfigVisitor
|
9
|
+
include Taketo::Constructs
|
10
|
+
|
11
|
+
def self.visit(*klasses, &block)
|
12
|
+
klasses.each do |klass|
|
13
|
+
define_method(:"visit_#{Taketo.downcased_construct_class_name(klass)}", block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def visit(obj)
|
18
|
+
obj.class.ancestors.each do |ancestor|
|
19
|
+
next unless ancestor.name # skip anonymous classes
|
20
|
+
method_name = :"visit_#{Taketo.downcased_construct_class_name(ancestor)}"
|
21
|
+
next unless respond_to?(method_name)
|
22
|
+
return send(method_name, obj)
|
23
|
+
end
|
24
|
+
|
25
|
+
raise "Don't know how to visit #{obj.class}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -1,72 +1,27 @@
|
|
1
1
|
require 'taketo/support'
|
2
|
+
require 'taketo/associated_nodes'
|
2
3
|
|
3
4
|
module Taketo
|
4
|
-
class NodesNotDefinedError < StandardError; end
|
5
|
-
|
6
5
|
module Constructs
|
7
6
|
class BaseConstruct
|
8
|
-
attr_reader :name
|
9
|
-
|
10
|
-
##
|
11
|
-
# Adds nodes collections to the construct
|
12
|
-
#
|
13
|
-
# Example:
|
14
|
-
#
|
15
|
-
# class Bar < BaseConstruct
|
16
|
-
# has_nodes :foos, :foo
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# bar = Bar.new
|
20
|
-
# bar.foos # => foos collection
|
21
|
-
# bar.append_foo(foo) # adds node the collection
|
22
|
-
# bar.find_foo(:foo_name) # find foo in foos collection by name
|
23
|
-
#
|
24
|
-
def self.has_nodes(name_plural, name_singular)
|
25
|
-
self.node_types << name_plural
|
26
|
-
|
27
|
-
define_method "append_#{name_singular}" do |object|
|
28
|
-
nodes(name_plural) << object
|
29
|
-
end
|
30
7
|
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
define_method name_plural do
|
36
|
-
nodes(name_plural)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.node_types
|
41
|
-
@node_types ||= []
|
42
|
-
end
|
8
|
+
include AssociatedNodes
|
9
|
+
attr_reader :name
|
43
10
|
|
44
11
|
def initialize(name)
|
12
|
+
super
|
45
13
|
@name = name
|
46
|
-
@nodes = {}
|
47
|
-
end
|
48
|
-
|
49
|
-
def find(singular_node_name, name)
|
50
|
-
send("find_#{singular_node_name}", name) or
|
51
|
-
if block_given?
|
52
|
-
yield
|
53
|
-
else
|
54
|
-
raise KeyError, "#{singular_node_name} #{name} not found for #{node_type} #{self.name}"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def nodes(name_plural)
|
59
|
-
unless self.class.node_types.include?(name_plural)
|
60
|
-
raise NodesNotDefinedError, "#{name_plural} not defined for #{node_type}"
|
61
|
-
end
|
62
|
-
@nodes[name_plural] ||= Taketo::Support::NamedNodesCollection.new
|
63
14
|
end
|
64
15
|
|
65
16
|
def node_type
|
66
17
|
demodulized = self.class.name.gsub(/.*::/, '')
|
67
18
|
demodulized.gsub(/([a-z])([A-Z])/, '\\1_\\2').downcase.to_sym
|
68
19
|
end
|
69
|
-
|
20
|
+
|
21
|
+
def qualified_name
|
22
|
+
"#{node_type} #{self.name}"
|
23
|
+
end
|
24
|
+
|
70
25
|
end
|
71
26
|
end
|
72
27
|
end
|
@@ -7,7 +7,15 @@ module Taketo
|
|
7
7
|
include Shellwords
|
8
8
|
|
9
9
|
attr_accessor :command, :description
|
10
|
-
|
10
|
+
|
11
|
+
def self.default
|
12
|
+
new(:default).tap { |cmd| cmd.command = "bash" }
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.explicit_command(command_string)
|
16
|
+
new(:explicit_command).tap { |cmd| cmd.command = command_string }
|
17
|
+
end
|
18
|
+
|
11
19
|
def render(server, options = {})
|
12
20
|
%Q[#{location(server, options)} #{environment_variables(server)} #{command}].strip.squeeze(" ")
|
13
21
|
end
|
@@ -6,9 +6,19 @@ module Taketo
|
|
6
6
|
class Environment < BaseConstruct
|
7
7
|
has_nodes :servers, :server
|
8
8
|
|
9
|
+
attr_accessor :project
|
10
|
+
|
11
|
+
def initialize(name)
|
12
|
+
super(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def project_name
|
16
|
+
@project.name if defined? @project
|
17
|
+
end
|
18
|
+
|
9
19
|
def append_server(server)
|
10
20
|
server.environment = self
|
11
|
-
|
21
|
+
super
|
12
22
|
end
|
13
23
|
end
|
14
24
|
end
|
@@ -1,14 +1,13 @@
|
|
1
1
|
require 'taketo/constructs/base_construct'
|
2
|
+
require 'taketo/constructs/command'
|
2
3
|
require 'taketo/support'
|
3
4
|
|
4
5
|
module Taketo
|
5
|
-
class CommandNotFoundError < StandardError; end
|
6
|
-
|
7
6
|
module Constructs
|
8
7
|
class Server < BaseConstruct
|
9
8
|
attr_reader :environment_variables
|
10
|
-
attr_accessor :host, :port, :username, :default_location, :environment, :global_alias, :identity_file
|
11
|
-
|
9
|
+
attr_accessor :host, :port, :username, :default_location, :default_command, :environment, :global_alias, :identity_file
|
10
|
+
|
12
11
|
has_nodes :commands, :command
|
13
12
|
|
14
13
|
def initialize(name)
|
@@ -24,6 +23,10 @@ module Taketo
|
|
24
23
|
env(:RAILS_ENV => environment.name.to_s)
|
25
24
|
@environment = environment
|
26
25
|
end
|
26
|
+
|
27
|
+
def default_command
|
28
|
+
defined?(@default_command) ? @default_command : Command.default
|
29
|
+
end
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
data/lib/taketo/dsl.rb
CHANGED
@@ -74,9 +74,20 @@ module Taketo
|
|
74
74
|
@shared_server_configs.store(name.to_sym, blk)
|
75
75
|
end
|
76
76
|
|
77
|
-
define_method_in_scope(:include_shared_server_config, :server) do
|
78
|
-
|
77
|
+
define_method_in_scope(:include_shared_server_config, :server) do |*args|
|
78
|
+
if args.first.is_a?(Hash)
|
79
|
+
configs_and_arguments = args.shift
|
80
|
+
configs_and_arguments.each do |config_name, arguments|
|
81
|
+
instance_exec(*arguments, &shared_server_configs[config_name])
|
82
|
+
end
|
83
|
+
else
|
84
|
+
args.each do |config_name|
|
85
|
+
instance_exec(&shared_server_configs[config_name])
|
86
|
+
end
|
87
|
+
end
|
79
88
|
end
|
89
|
+
alias :include_shared_server_configs :include_shared_server_config
|
90
|
+
|
80
91
|
|
81
92
|
private
|
82
93
|
|
@@ -15,6 +15,7 @@ module Taketo
|
|
15
15
|
|
16
16
|
def push(node)
|
17
17
|
@nodes << node unless find_by_name(node.name)
|
18
|
+
self
|
18
19
|
end
|
19
20
|
alias :<< :push
|
20
21
|
|
@@ -29,6 +30,15 @@ module Taketo
|
|
29
30
|
def find_by_name(name)
|
30
31
|
@nodes.detect { |n| n.name == name }
|
31
32
|
end
|
33
|
+
|
34
|
+
def ==(other)
|
35
|
+
return true if other.equal?(self)
|
36
|
+
@nodes == other.is_a?(NamedNodesCollection) ? other.nodes : other
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
attr_reader :nodes
|
32
42
|
end
|
33
43
|
end
|
34
44
|
end
|
data/lib/taketo/support.rb
CHANGED
data/lib/taketo.rb
CHANGED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'taketo/associated_nodes'
|
3
|
+
|
4
|
+
include Taketo
|
5
|
+
|
6
|
+
class TestAssociatedNodes
|
7
|
+
include AssociatedNodes
|
8
|
+
|
9
|
+
has_nodes :foos, :foo
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "AssociatedNodes" do
|
13
|
+
subject(:construct) { TestAssociatedNodes.new(:my_node) }
|
14
|
+
let(:foo) { stub(:FooNode, :name => :bar) }
|
15
|
+
|
16
|
+
describe ".has_nodes" do
|
17
|
+
it "adds node type to class's node_types" do
|
18
|
+
expect(TestAssociatedNodes.node_types).to include(:foos)
|
19
|
+
end
|
20
|
+
|
21
|
+
specify "#foos returns mutable foos collection" do
|
22
|
+
construct.nodes(:foos) << foo
|
23
|
+
expect(construct.foos).to eq([foo])
|
24
|
+
end
|
25
|
+
|
26
|
+
specify "#append_foo adds a foo to nested foos collection" do
|
27
|
+
construct.nodes(:foos) << foo
|
28
|
+
expect(construct.nodes(:foos)).to include(foo)
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "#find_foo returns node with corresponding name" do
|
32
|
+
construct.nodes(:foos) << foo
|
33
|
+
expect(construct.find_foo(:bar)).to eq(foo)
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#has_foos?" do
|
37
|
+
context "when there are no foos" do
|
38
|
+
it "returns false" do
|
39
|
+
expect(construct).not_to have_foos
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when there are some foos" do
|
44
|
+
it "returns true" do
|
45
|
+
construct.nodes(:foos) << foo
|
46
|
+
expect(construct).to have_foos
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#find" do
|
53
|
+
context "when node is present" do
|
54
|
+
it "returns node object with corresponding name" do
|
55
|
+
construct.nodes(:foos) << foo
|
56
|
+
expect(construct.find(:foo, :bar)).to eq(foo)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when node is absent" do
|
61
|
+
it "yields if block given" do
|
62
|
+
expect { |b| construct.find(:foo, :bar, &b) }.to yield_control
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raises KeyError if no block given" do
|
66
|
+
expect { construct.find(:foo, :bar) }.to raise_error(KeyError)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#nodes" do
|
72
|
+
it "returns nodes collection by plural name" do
|
73
|
+
construct.nodes(:foos) << foo
|
74
|
+
expect(construct.nodes(:foos)).to eq([foo])
|
75
|
+
end
|
76
|
+
|
77
|
+
it "raises NodesNotDefinedError if non-specified node requested" do
|
78
|
+
expect { construct.nodes(:bars) }.to raise_error(NodesNotDefinedError, /bars/)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#has_nodes?" do
|
83
|
+
context "when nodes are present" do
|
84
|
+
it "returns true" do
|
85
|
+
construct.nodes(:foos) << foo
|
86
|
+
expect(construct).to have_nodes(:foos)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when nodes are absent" do
|
91
|
+
it "returns false" do
|
92
|
+
expect(construct).not_to have_nodes(:foos)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "raises if node can not have children of specified type" do
|
97
|
+
expect { construct.has_nodes?(:quack) }.to raise_error NodesNotDefinedError
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
@@ -1,10 +1,9 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
require 'taketo/commands/ssh_command'
|
3
3
|
|
4
4
|
include Taketo::Commands
|
5
5
|
|
6
6
|
describe "SSH Command" do
|
7
|
-
let(:environment) { stub(:Environment, :name => :staging) }
|
8
7
|
let(:server) do
|
9
8
|
stub(:Server, :name => :s1,
|
10
9
|
:host => "1.2.3.4",
|
@@ -12,27 +11,18 @@ describe "SSH Command" do
|
|
12
11
|
:username => "deployer",
|
13
12
|
:default_location => "/var/app",
|
14
13
|
:identity_file => "/home/gor/.ssh/qqq",
|
15
|
-
:environment => environment,
|
16
14
|
:environment_variables => {})
|
17
15
|
end
|
18
|
-
let(:command) { mock(:Command, :render => "the_command") }
|
19
16
|
|
20
|
-
|
17
|
+
subject(:ssh_command) { SSHCommand.new(server) }
|
21
18
|
|
22
|
-
it "
|
19
|
+
it "composes command based on provided server object" do
|
23
20
|
ssh_command.render("foobar").should == %q[ssh -t -p 22 -i /home/gor/.ssh/qqq deployer@1.2.3.4 "foobar"]
|
24
21
|
end
|
25
22
|
|
26
|
-
it "
|
27
|
-
server.stub(:port => nil)
|
28
|
-
server.stub(:username => nil)
|
29
|
-
server.stub(:identity_file => nil)
|
23
|
+
it "ignores absent parts if they are not required" do
|
24
|
+
server.stub(:port => nil, :username => nil, :identity_file => nil)
|
30
25
|
ssh_command.render("foobar").should == %q[ssh -t 1.2.3.4 "foobar"]
|
31
26
|
end
|
32
|
-
|
33
|
-
it "should require host" do
|
34
|
-
server.stub(:host => nil)
|
35
|
-
expect { ssh_command.render("bash") }.to raise_error ArgumentError, /host/
|
36
|
-
end
|
37
27
|
end
|
38
28
|
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'taketo/config_printer_visitor'
|
3
|
+
|
4
|
+
include Taketo
|
5
|
+
|
6
|
+
describe "ConfigPrinterVisitor" do
|
7
|
+
subject(:printer) { ConfigPrinterVisitor.new }
|
8
|
+
|
9
|
+
describe "#visit_command" do
|
10
|
+
let(:command) { stub(:Command, :name => :foo, :description => nil) }
|
11
|
+
|
12
|
+
it "renders command name" do
|
13
|
+
printer.visit_command(command)
|
14
|
+
expect(printer.result).to eq("foo")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "renders description if available" do
|
18
|
+
command.stub(:description => "The description")
|
19
|
+
printer.visit_command(command)
|
20
|
+
expect(printer.result).to eq("foo - The description")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#visit_server" do
|
25
|
+
let(:server) do
|
26
|
+
stub(:Server,
|
27
|
+
:name => :sponge,
|
28
|
+
:host => "1.2.3.4",
|
29
|
+
:port => 8000,
|
30
|
+
:username => "bob",
|
31
|
+
:default_location => "/var/app",
|
32
|
+
:environment_variables => { :FOO => "bar", :BOO => "baz" })
|
33
|
+
end
|
34
|
+
|
35
|
+
it "renders available server info" do
|
36
|
+
server.stub(:has_commands? => true)
|
37
|
+
printer.visit_server(server)
|
38
|
+
expect(printer.result).to match(
|
39
|
+
%r[Server: sponge
|
40
|
+
Host: 1\.2\.3\.4
|
41
|
+
Port: 8000
|
42
|
+
User: bob
|
43
|
+
Default location: /var/app
|
44
|
+
Environment: (FOO=bar BOO=baz|BOO=baz FOO=bar)])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "renders appropriate message if there are no commands" do
|
48
|
+
server.stub(:has_commands? => false)
|
49
|
+
printer.visit_server(server)
|
50
|
+
expect(printer.result).to include("(No commands)")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#visit_environment" do
|
55
|
+
let(:environment) { stub(:Environment, :name => :foo) }
|
56
|
+
|
57
|
+
it "renders environment info" do
|
58
|
+
environment.stub(:has_servers? => true)
|
59
|
+
printer.visit_environment(environment)
|
60
|
+
expect(printer.result).to eq("Environment: foo")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "renders appropriate message if there are no servers" do
|
64
|
+
environment.stub(:has_servers? => false)
|
65
|
+
printer.visit_environment(environment)
|
66
|
+
expect(printer.result).to include("Environment: foo\n (No servers)")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#visit_project" do
|
71
|
+
let(:project) { stub(:Project, :name => :quux) }
|
72
|
+
|
73
|
+
it "renders project info" do
|
74
|
+
project.stub(:has_environments? => true)
|
75
|
+
printer.visit_project(project)
|
76
|
+
expect(printer.result).to eq("\nProject: quux")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "renders appropriate message if there are no environments for project" do
|
80
|
+
project.stub(:has_environments? => false)
|
81
|
+
printer.visit_project(project)
|
82
|
+
expect(printer.result).to eq("\nProject: quux\n (No environments)")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#visit_config" do
|
87
|
+
let(:config) { stub(:Config, :default_destination => "hello:bye") }
|
88
|
+
|
89
|
+
it "renders default destination and all projects" do
|
90
|
+
config.stub(:has_projects? => true)
|
91
|
+
printer.visit_config(config)
|
92
|
+
expect(printer.result).to eq("Default destination: hello:bye")
|
93
|
+
end
|
94
|
+
|
95
|
+
it "renders appropriate message if there are no projects" do
|
96
|
+
config.stub(:has_projects? => false)
|
97
|
+
printer.visit_config(config)
|
98
|
+
expect(printer.result).to eq("There are no projects yet...")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it "indents relatively" do
|
103
|
+
config = stub(:Config, :default_destination => "hello:bye", :has_projects? => true)
|
104
|
+
project = stub(:Project, :name => :quux, :has_environments? => true)
|
105
|
+
environment = stub(:Environment, :name => :foo, :has_servers? => false)
|
106
|
+
printer.visit_config(config)
|
107
|
+
printer.visit_project(project)
|
108
|
+
printer.visit_environment(environment)
|
109
|
+
expect(printer.result).to eq("Default destination: hello:bye\n\nProject: quux\n Environment: foo\n (No servers)")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'taketo/config_traverser'
|
3
|
+
|
4
|
+
# config 1
|
5
|
+
# / \
|
6
|
+
# project 1 2
|
7
|
+
# / \
|
8
|
+
# environment 1 2
|
9
|
+
# /|\
|
10
|
+
# server 1 2 3
|
11
|
+
|
12
|
+
describe Taketo::ConfigTraverser do
|
13
|
+
let(:server_1) { stub(:Server1, :node_type => :server, :name => :server_1).as_null_object }
|
14
|
+
let(:server_2) { stub(:Server2, :node_type => :server, :name => :server_2).as_null_object }
|
15
|
+
let(:server_3) { stub(:Server3, :node_type => :server, :name => :server_3).as_null_object }
|
16
|
+
|
17
|
+
let(:environment_1) { stub(:Environment1, :node_type => :environment, :name => :environment_1) }
|
18
|
+
let(:environment_2) { stub(:Environment2, :node_type => :environment, :name => :environment_2, :servers => [server_1, server_2, server_3]) }
|
19
|
+
|
20
|
+
let(:project_1) { stub(:Project1, :node_type => :project, :name => :project_1, :environments => [environment_1, environment_2]) }
|
21
|
+
let(:project_2) { stub(:Project2, :node_type => :project, :name => :project_2) }
|
22
|
+
|
23
|
+
let(:config) { stub(:Config, :node_type => :config, :projects => [project_1, project_2], :name => :config) }
|
24
|
+
|
25
|
+
let(:traverser) { described_class.new(config) }
|
26
|
+
|
27
|
+
before do
|
28
|
+
config.stub(:has_nodes?).with(:projects).and_return(true)
|
29
|
+
config.stub(:nodes).with(:projects).and_return([project_1, project_2])
|
30
|
+
|
31
|
+
project_1.stub(:has_nodes?).with(:environments).and_return(true)
|
32
|
+
project_1.stub(:nodes).with(:environments).and_return([environment_1, environment_2])
|
33
|
+
|
34
|
+
project_2.stub(:has_nodes?).with(:environments).and_return(false)
|
35
|
+
project_2.should_not_receive(:nodes)
|
36
|
+
|
37
|
+
environment_1.stub(:has_nodes?).with(:servers).and_return(false)
|
38
|
+
environment_1.should_not_receive(:nodes)
|
39
|
+
|
40
|
+
environment_2.stub(:has_nodes?).with(:servers).and_return(true)
|
41
|
+
environment_2.stub(:nodes).and_return([server_1, server_2, server_3])
|
42
|
+
end
|
43
|
+
|
44
|
+
class PrintingVisitor
|
45
|
+
def initialize
|
46
|
+
@result = []
|
47
|
+
end
|
48
|
+
|
49
|
+
def visit(type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#visit_depth_first" do
|
54
|
+
it "traverses in depth with visitor" do
|
55
|
+
visitor = stub(:Visitor)
|
56
|
+
[config, project_1, environment_1, environment_2, server_1, server_2, server_3, project_2].each do |node|
|
57
|
+
visitor.should_receive(:visit).with(node).ordered
|
58
|
+
end
|
59
|
+
traverser.visit_depth_first(visitor)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|