taketo 0.0.2 → 0.0.3

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/README.md CHANGED
@@ -4,7 +4,15 @@ Take Me To
4
4
  [![Build Status](https://secure.travis-ci.org/v-yarotsky/taketo.png)](http://travis-ci.org/v-yarotsky/taketo)
5
5
  [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/v-yarotsky/taketo)
6
6
 
7
- A tiny helper utility to make access to servers eaiser for different projects and environments.
7
+ A tiny helper utility to make access to servers easier for different projects and environments.
8
+
9
+ Important note:
10
+ ---------------
11
+
12
+ The project is currently actively developed, thus it may contain bugs;
13
+ backward compatibility is not guaranteed.
14
+
15
+ Suggestions and contributions are highly appreciated :)
8
16
 
9
17
  Usage:
10
18
  ------
@@ -28,9 +36,42 @@ puts a config into ```~/.taketo.rc.rb```:
28
36
  end
29
37
  ```
30
38
 
31
- Then execute ```taketo my_project staging server -c console``` to execute the "rails c" with corresponding environment variables set on desired server
32
- or just ```taketo my_project staging server``` to open bash
39
+ Then execute ```taketo my_project:staging:server -c console``` to execute the "rails c" with corresponding environment variables set on desired server
40
+ or just ```taketo my_project:staging:server``` to open bash
41
+
42
+ Destination resolving works intelligently. Given the following config:
43
+
44
+ ```ruby
45
+ default_destination "my_project2:staging:s2"
33
46
 
47
+ project :my_project do
48
+ environment :staging do
49
+ server :s1 do
50
+ host "1.2.3.4"
51
+ end
52
+ end
53
+ environment :production do
54
+ server :ps1 do
55
+ host "3.4.5.6"
56
+ end
57
+ end
58
+ end
59
+
60
+ project :my_project2 do
61
+ environment :staging do
62
+ server :s2 do
63
+ host "2.3.4.5"
64
+ end
65
+ end
66
+ end
67
+ ```
68
+
69
+ ```taketo my_project:staging``` will ssh to s1 with host = 1.2.3.4
70
+ ```taketo my_project2``` will ssh to s2 with host = 2.3.4.5
71
+
72
+ Note that default destination can be specified via ```default_destination``` config option
73
+
74
+
34
75
  To-Do:
35
76
  ------
36
77
 
@@ -40,6 +81,10 @@ To-Do:
40
81
  The Changelog:
41
82
  --------------
42
83
 
84
+ ### v0.0.3 (22.07.2012) ###
85
+ * Add default_destination config option
86
+ * Add intelligent destination resolving
87
+
43
88
  ### v0.0.2 (21.07.2012) ###
44
89
  * Add ability to define environment variables
45
90
  * Add support for server commands
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
data/bin/taketo CHANGED
@@ -6,7 +6,7 @@ begin
6
6
  require 'taketo'
7
7
  require 'taketo/constructs_factory'
8
8
  require 'taketo/commands'
9
- require 'taketo/taketo_argv_parser'
9
+ require 'taketo/destination_resolver'
10
10
  require 'optparse'
11
11
  rescue LoadError => e #development
12
12
  raise if $loaded
@@ -62,7 +62,7 @@ end
62
62
 
63
63
  def remote_command(server, options)
64
64
  command_name = options[:command] or return default_command(options)
65
- server.command_by_name(command_name.to_sym) { default_command(options) }
65
+ server.find(:command, command_name.to_sym) { default_command(options) }
66
66
  end
67
67
 
68
68
  def default_command(options)
@@ -75,7 +75,8 @@ begin
75
75
  options = parse_options
76
76
  config = parse_config(options[:config])
77
77
 
78
- server = TaketoArgvParser.new(config, ARGV).parse
78
+ destination_path = ARGV.shift.to_s
79
+ server = DestinationResolver.new(config, destination_path).resolve
79
80
 
80
81
  server_command = remote_command(server, options)
81
82
  command_to_execute = Commands::SSHCommand.new(server).render(server_command)
@@ -91,3 +92,4 @@ rescue Exception => e
91
92
  STDERR.puts "An error occurred: #{e.message}"
92
93
  raise if options && options[:debug]
93
94
  end
95
+
@@ -16,7 +16,7 @@ Feature:
16
16
  end
17
17
  end
18
18
  """
19
- And I successfully run `taketo --config=/tmp/taketo_test_cfg.rb --dry-run --command "TERM=xterm-256color bash" slots staging s1`
19
+ And I successfully run `taketo --config=/tmp/taketo_test_cfg.rb --dry-run --command "TERM=xterm-256color bash"`
20
20
  Then the output should contain
21
21
  """
22
22
  ssh -t 1.2.3.4 "cd /var/apps/slots; RAILS_ENV=staging TERM=xterm-256color bash"
@@ -37,7 +37,7 @@ Feature:
37
37
  end
38
38
  end
39
39
  """
40
- And I successfully run `taketo --config=/tmp/taketo_test_cfg.rb --dry-run --command console slots staging s1`
40
+ And I successfully run `taketo --config=/tmp/taketo_test_cfg.rb --dry-run --command console slots:staging:s1`
41
41
  Then the output should contain
42
42
  """
43
43
  ssh -t 1.2.3.4 "cd /var/apps/slots; RAILS_ENV=staging rails c"
@@ -14,11 +14,15 @@ Feature:
14
14
  user "deployer"
15
15
  location "/var/apps/slots"
16
16
  end
17
+
18
+ server :s2 do
19
+ host "2.3.4.5"
20
+ end
17
21
  end
18
22
  end
19
23
 
20
24
  """
21
- And I successfully run `taketo --config=/tmp/taketo_test_cfg.rb --dry-run slots staging s1`
25
+ And I successfully run `taketo --config=/tmp/taketo_test_cfg.rb --dry-run slots:staging:s1`
22
26
  Then the output should contain
23
27
  """
24
28
  ssh -t deployer@1.2.3.4 "cd /var/apps/slots; RAILS_ENV=staging bash"
@@ -65,3 +69,44 @@ Feature:
65
69
  FOO=the\ value
66
70
  """
67
71
 
72
+ Scenario: Reopen config scopes
73
+ When I have the following config in "/tmp/taketo_test_cfg.rb"
74
+ """
75
+ project :slots do
76
+ environment :staging do
77
+ server :s1 do
78
+ host "1.2.3.4"
79
+ end
80
+ end
81
+ end
82
+
83
+ project :slots do
84
+ environment :staging do
85
+ server :s1 do
86
+ env :FOO => "bar"
87
+ end
88
+ end
89
+ end
90
+ """
91
+ And I successfully run `taketo --config=/tmp/taketo_test_cfg.rb slots:staging:s1 --dry-run`
92
+ Then the output should match /ssh -t 1\.2\.3\.4 "(RAILS_ENV=staging FOO=bar|FOO=bar RAILS_ENV=staging) bash"/
93
+
94
+ Scenario: Default destination
95
+ When I have the following config in "/tmp/taketo_test_cfg.rb"
96
+ """
97
+ default_destination "slots:staging:s2"
98
+ project :slots do
99
+ environment :staging do
100
+ server :s1 do
101
+ end
102
+ server :s2 do
103
+ host "2.3.4.5"
104
+ end
105
+ end
106
+ end
107
+ """
108
+ And I successfully run `taketo --config=/tmp/taketo_test_cfg.rb --dry-run`
109
+ Then the output should contain
110
+ """
111
+ ssh -t 2.3.4.5 "RAILS_ENV=staging bash"
112
+ """
@@ -0,0 +1,24 @@
1
+ require 'taketo/support'
2
+
3
+ module Taketo
4
+ module Constructs
5
+ class BaseConstruct
6
+ attr_reader :name
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+
12
+ def find(scope, name)
13
+ send("find_#{scope}", name) or
14
+ if block_given?
15
+ yield
16
+ else
17
+ raise KeyError, "#{scope.to_s.capitalize} #{name} not found for #{self.class.name} #{name}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+
@@ -1,19 +1,15 @@
1
+ require 'taketo/constructs/base_construct'
1
2
  require 'shellwords'
2
3
 
3
4
  module Taketo
4
5
  module Constructs
5
- class Command
6
+ class Command < BaseConstruct
6
7
  include Shellwords
7
8
 
8
- attr_reader :name
9
9
  attr_accessor :command
10
10
 
11
- def initialize(name)
12
- @name = name
13
- end
14
-
15
11
  def render(server)
16
- %Q[#{location(server)} #{environment_variables(server)} #{command}].squeeze(" ")
12
+ %Q[#{location(server)} #{environment_variables(server)} #{command}].strip.squeeze(" ")
17
13
  end
18
14
 
19
15
  private
@@ -1,9 +1,11 @@
1
+ require 'taketo/constructs/base_construct'
1
2
  require 'taketo/support'
2
3
 
3
4
  module Taketo
4
5
  module Constructs
5
- class Config
6
+ class Config < BaseConstruct
6
7
  attr_reader :projects
8
+ attr_accessor :default_destination
7
9
 
8
10
  def initialize
9
11
  @projects = Taketo::Support::NamedNodesCollection.new
@@ -12,6 +14,10 @@ module Taketo
12
14
  def append_project(project)
13
15
  @projects << project
14
16
  end
17
+
18
+ def find_project(name)
19
+ @projects.find_by_name(name)
20
+ end
15
21
  end
16
22
  end
17
23
  end
@@ -1,12 +1,13 @@
1
+ require 'taketo/constructs/base_construct'
1
2
  require 'taketo/support'
2
3
 
3
4
  module Taketo
4
5
  module Constructs
5
- class Environment
6
- attr_reader :name, :servers
6
+ class Environment < BaseConstruct
7
+ attr_reader :servers
7
8
 
8
9
  def initialize(name)
9
- @name = name
10
+ super
10
11
  @servers = Taketo::Support::NamedNodesCollection.new
11
12
  end
12
13
 
@@ -14,6 +15,10 @@ module Taketo
14
15
  server.environment = self
15
16
  @servers << server
16
17
  end
18
+
19
+ def find_server(name)
20
+ @servers.find_by_name(name)
21
+ end
17
22
  end
18
23
  end
19
24
  end
@@ -1,18 +1,23 @@
1
+ require 'taketo/constructs/base_construct'
1
2
  require 'taketo/support'
2
3
 
3
4
  module Taketo
4
5
  module Constructs
5
- class Project
6
- attr_reader :name, :environments
6
+ class Project < BaseConstruct
7
+ attr_reader :environments
7
8
 
8
9
  def initialize(name)
9
- @name = name
10
+ super
10
11
  @environments = Taketo::Support::NamedNodesCollection.new
11
12
  end
12
13
 
13
14
  def append_environment(environment)
14
15
  @environments << environment
15
16
  end
17
+
18
+ def find_environment(name)
19
+ @environments.find_by_name(name)
20
+ end
16
21
  end
17
22
  end
18
23
  end
@@ -1,15 +1,18 @@
1
+ require 'taketo/constructs/base_construct'
2
+ require 'taketo/support'
3
+
1
4
  module Taketo
2
5
  module Constructs
3
- class Server
6
+ class Server < BaseConstruct
4
7
  class CommandNotFoundError < StandardError; end
5
8
 
6
- attr_reader :name, :environment_variables, :commands
9
+ attr_reader :environment_variables, :commands
7
10
  attr_accessor :host, :port, :username, :default_location, :environment
8
11
 
9
12
  def initialize(name)
10
- @name = name
13
+ super
11
14
  @environment_variables = {}
12
- @commands = []
15
+ @commands = Taketo::Support::NamedNodesCollection.new
13
16
  end
14
17
 
15
18
  def env(env_variables)
@@ -20,20 +23,14 @@ module Taketo
20
23
  @commands << command
21
24
  end
22
25
 
26
+ def find_command(name)
27
+ @commands.find_by_name(name)
28
+ end
29
+
23
30
  def environment=(environment)
24
31
  env(:RAILS_ENV => environment.name.to_s)
25
32
  @environment = environment
26
33
  end
27
-
28
- def command_by_name(command_name)
29
- @commands.detect { |c| c.name == command_name } or
30
- if block_given?
31
- yield
32
- else
33
- raise CommandNotFoundError, "Command #{command_name} not found for server #{name}"
34
- end
35
- end
36
-
37
34
  end
38
35
  end
39
36
  end
@@ -0,0 +1,118 @@
1
+ module Taketo
2
+ class ConfigError < StandardError; end
3
+ class AmbiguousDestinationError < StandardError; end
4
+ class NonExistentDestinationError < StandardError; end
5
+
6
+ class DestinationResolver
7
+ def initialize(config, path)
8
+ @config = config
9
+ set_destination(path)
10
+ end
11
+
12
+ def resolve
13
+ case @path.size
14
+ when 3 then resolve_by_three_segments
15
+ when 2 then resolve_by_two_segments
16
+ when 1 then resolve_by_one_segment
17
+ when 0 then resolve_with_no_segments
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def set_destination(path)
24
+ @path_str = path
25
+ @path = String(path).split(":").map(&:to_sym)
26
+ end
27
+
28
+ def resolve_by_three_segments
29
+ get_server(*@path)
30
+ end
31
+
32
+ def resolve_by_two_segments
33
+ project, environment = get_project_and_environment(*@path)
34
+ get_only_server_for_project_and_environment(project, environment)
35
+ end
36
+
37
+ def resolve_by_one_segment
38
+ project = get_project(*@path)
39
+ get_only_server_for_project(project)
40
+ end
41
+
42
+ def resolve_with_no_segments
43
+ unless @config.default_destination.nil?
44
+ set_destination(@config.default_destination.to_s)
45
+ return resolve
46
+ end
47
+ get_only_server
48
+ end
49
+
50
+ def get_server(project_name, environment_name, server_name)
51
+ handling_failure("There is no such project, environment or server for destination: #@path_str") do
52
+ project, environment = get_project_and_environment(project_name, environment_name)
53
+ environment.servers[server_name]
54
+ end
55
+ end
56
+
57
+ def get_project_and_environment(project_name, environment_name)
58
+ handling_failure("There is no such project - environment pair: #@path_str") do
59
+ project = get_project(project_name)
60
+ environment = project.environments[environment_name]
61
+ [project, environment]
62
+ end
63
+ end
64
+
65
+ def get_only_server_for_project_and_environment(project, environment)
66
+ if environment.servers.one?
67
+ return environment.servers.first
68
+ else
69
+ raise_server_ambiguous!(project, environment)
70
+ end
71
+ end
72
+
73
+ def get_project(project_name)
74
+ handling_failure("There is no such project: #@path_str") do
75
+ @config.projects[project_name]
76
+ end
77
+ end
78
+
79
+ def get_only_server_for_project(project)
80
+ if project.environments.one?
81
+ environment = project.environments.first
82
+ get_only_server_for_project_and_environment(project, environment)
83
+ else
84
+ raise_environment_ambiguous!(project)
85
+ end
86
+ end
87
+
88
+ def get_only_server
89
+ if @config.projects.one?
90
+ project = @config.projects.first
91
+ get_only_server_for_project(project)
92
+ else
93
+ raise_project_ambiguous!
94
+ end
95
+ end
96
+
97
+ def raise_project_ambiguous!
98
+ raise AmbiguousDestinationError, "There are multiple projects #{@config.projects.map(&:name).join(", ")}"
99
+ end
100
+
101
+ def raise_environment_ambiguous!(project)
102
+ raise AmbiguousDestinationError, "There are multiple environments for project #{project.name}: " \
103
+ "#{project.environments.map(&:name).join(", ")}"
104
+ end
105
+
106
+ def raise_server_ambiguous!(project, environment)
107
+ raise AmbiguousDestinationError, "There are multiple servers for project #{project.name} " \
108
+ "in environment #{environment.name}: #{environment.servers.map(&:name).join(", ")}"
109
+ end
110
+
111
+ def handling_failure(message)
112
+ yield
113
+ rescue KeyError, NonExistentDestinationError
114
+ raise NonExistentDestinationError, message
115
+ end
116
+ end
117
+ end
118
+
data/lib/taketo/dsl.rb CHANGED
@@ -11,7 +11,7 @@ module Taketo
11
11
  raise ScopeError,
12
12
  "#{scope} can't be defined in #{current_scope} scope"
13
13
  end
14
- scope_object = @factory.create(scope, name)
14
+ scope_object = current_scope_object.find(scope, name) { @factory.create(scope, name) }
15
15
  in_scope(scope, scope_object) do
16
16
  block.call
17
17
  end
@@ -55,12 +55,13 @@ module Taketo
55
55
  define_scope :server, :environment
56
56
  define_scope :command, :server
57
57
 
58
- define_attribute(:host, :server) { |hostname| current_scope_object.host = hostname }
59
- define_attribute(:port, :server) { |port_number| current_scope_object.port = port_number }
60
- define_attribute(:user, :server) { |username| current_scope_object.username = username }
61
- define_attribute(:location, :server) { |path| current_scope_object.default_location = path }
62
- define_attribute(:env, :server) { |env| current_scope_object.env(env) }
63
- define_attribute(:execute, :command) { |command| current_scope_object.command = command }
58
+ define_attribute(:default_destination, :config) { |destination| current_scope_object.default_destination = destination }
59
+ define_attribute(:host, :server) { |hostname| current_scope_object.host = hostname }
60
+ define_attribute(:port, :server) { |port_number| current_scope_object.port = port_number }
61
+ define_attribute(:user, :server) { |username| current_scope_object.username = username }
62
+ define_attribute(:location, :server) { |path| current_scope_object.default_location = path }
63
+ define_attribute(:env, :server) { |env| current_scope_object.env(env) }
64
+ define_attribute(:execute, :command) { |command| current_scope_object.command = command }
64
65
 
65
66
  private
66
67
 
@@ -78,7 +79,7 @@ module Taketo
78
79
 
79
80
  def in_scope(scope, new_scope_object)
80
81
  parent_scope_object, @current_scope_object = @current_scope_object, new_scope_object
81
- @scope << scope
82
+ @scope.push(scope)
82
83
  yield
83
84
  parent_scope_object.send("append_#{scope}", current_scope_object)
84
85
  @scope.pop
@@ -7,19 +7,28 @@ module Taketo
7
7
  include Enumerable
8
8
  extend Forwardable
9
9
 
10
- def_delegators :@nodes, :each, :<<, :push, :length, :size, :empty?
10
+ def_delegators :@nodes, :each, :length, :size, :empty?
11
11
 
12
12
  def initialize(nodes = [])
13
13
  @nodes = nodes
14
14
  end
15
15
 
16
+ def push(node)
17
+ @nodes << node unless find_by_name(node.name)
18
+ end
19
+ alias :<< :push
20
+
16
21
  def [](index)
17
22
  if index.is_a?(Symbol)
18
- node = @nodes.detect { |n| n.name == index } or raise KeyError, "Element with name #{index} not found"
23
+ node = find_by_name(index) or raise KeyError, "Element with name #{index} not found"
19
24
  return node
20
25
  end
21
26
  @nodes[index] or raise KeyError, "Element ##{index} not found"
22
27
  end
28
+
29
+ def find_by_name(name)
30
+ @nodes.detect { |n| n.name == name }
31
+ end
23
32
  end
24
33
  end
25
34
  end
@@ -11,6 +11,10 @@ describe "Taketo DSL" do
11
11
  host "127.0.0.2"
12
12
  user "deployer"
13
13
  location "/var/app"
14
+ env :FOO => "bar"
15
+ command :console do
16
+ execute "rails c"
17
+ end
14
18
  end
15
19
  end
16
20
 
@@ -40,6 +44,9 @@ describe "Taketo DSL" do
40
44
  staging_server.host.should == "127.0.0.2"
41
45
  staging_server.username.should == "deployer"
42
46
  staging_server.default_location.should == "/var/app"
47
+ staging_server.environment_variables.should == { :RAILS_ENV => "staging", :FOO => "bar" }
48
+ staging_server.commands.length.should == 1
49
+ staging_server.commands[:console].command.should == "rails c"
43
50
 
44
51
  production = project.environments[:production]
45
52
  production.servers.length.should == 2
@@ -0,0 +1,45 @@
1
+ require File.expand_path('../../../../spec_helper', __FILE__)
2
+ require 'taketo/constructs/base_construct'
3
+
4
+ include Taketo
5
+
6
+ describe "BaseConstruct" do
7
+ class TestConstruct < Constructs::BaseConstruct
8
+ attr_reader :foos
9
+
10
+ def initialize(name, foos)
11
+ super(name)
12
+ @foos = foos
13
+ end
14
+
15
+ def find_foo(name)
16
+ @foos[name]
17
+ end
18
+ end
19
+
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
42
+ end
43
+ end
44
+
45
+
@@ -13,5 +13,18 @@ describe "Config" do
13
13
  config.projects.should include(project)
14
14
  end
15
15
  end
16
+
17
+ describe "#find_project" do
18
+ it "should find project by name" do
19
+ config.projects.should_receive(:find_by_name).with(:foo).and_return(:bar)
20
+ config.find_project(:foo).should == :bar
21
+ end
22
+ end
23
+
24
+ it "should set default_destination" do
25
+ config.default_destination.should be_nil
26
+ config.default_destination = "foo:bar:baz"
27
+ config.default_destination.should == "foo:bar:baz"
28
+ end
16
29
  end
17
30
 
@@ -23,6 +23,13 @@ describe "Environment" do
23
23
  environment.append_server(server)
24
24
  end
25
25
  end
26
+
27
+ describe "#find_server" do
28
+ it "should find server by name" do
29
+ environment.servers.should_receive(:find_by_name).with(:foo).and_return(:bar)
30
+ environment.find_server(:foo).should == :bar
31
+ end
32
+ end
26
33
  end
27
34
 
28
35
 
@@ -17,5 +17,12 @@ describe "Project" do
17
17
  project.environments.should include(environment)
18
18
  end
19
19
  end
20
+
21
+ describe "#find_environment" do
22
+ it "should find environment by name" do
23
+ project.environments.should_receive(:find_by_name).with(:foo).and_return(:bar)
24
+ project.find_environment(:foo).should == :bar
25
+ end
26
+ end
20
27
  end
21
28
 
@@ -60,22 +60,10 @@ describe "Server" do
60
60
  end
61
61
  end
62
62
 
63
- describe "#command_by_name" do
64
- context "if there is such command" do
65
- it "should return command object with corresponding name" do
66
- server.append_command(command)
67
- server.command_by_name(:foo).should == command
68
- end
69
- end
70
-
71
- context "if there is no such command" do
72
- it "should yield if block given" do
73
- expect { |b| server.command_by_name(:bar, &b) }.to yield_control
74
- end
75
-
76
- it "should raise CommandNotFoundError if no block given" do
77
- expect { server.command_by_name(:bar) }.to raise_error(Taketo::Constructs::Server::CommandNotFoundError)
78
- end
63
+ describe "#find_command" do
64
+ it "should find command by name" do
65
+ server.commands.should_receive(:find_by_name).with(:foo).and_return(:bar)
66
+ server.find_command(:foo).should == :bar
79
67
  end
80
68
  end
81
69
  end
@@ -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 appripriate method according to type" do
9
+ specify "#create should delegate to appropriate method according to the type" do
10
10
  factory.should_receive(:create_config)
11
11
  factory.create(:config)
12
12
 
@@ -0,0 +1,170 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require 'taketo/destination_resolver'
3
+ require 'taketo/support/named_nodes_collection'
4
+
5
+ include Taketo
6
+
7
+ describe "DestinationResolver" do
8
+ let(:server1) { stub(:Server, :name => :s1) }
9
+ let(:server2) { stub(:Server, :name => :s2) }
10
+ let(:server3) { stub(:Server, :name => :s3) }
11
+ let(:server4) { stub(:Server, :name => :s4) }
12
+ let(:config) do
13
+ stub(:Config, :projects => list(
14
+ stub(:name => :foo, :environments => list(
15
+ stub(:name => :bar, :servers => list(server1))
16
+ )),
17
+ stub(:name => :baz, :environments => list(
18
+ stub(:name => :qux, :servers => list(server2))
19
+ )),
20
+ stub(:name => :quux, :environments => list(
21
+ stub(:name => :corge, :servers => list(server3, server4))
22
+ )),
23
+ stub(:name => :grault, :environments => list(
24
+ stub(:name => :garply, :servers => list(anything, anything)),
25
+ stub(:name => :waldo, :servers => list(anything))
26
+ ))
27
+ ))
28
+ end
29
+
30
+ context "when project, environment and server specified" do
31
+ it "should return server if it exists" do
32
+ resolver(config, "foo:bar:s1").resolve.should == server1
33
+ end
34
+
35
+ it "should raise error if server does not exist" do
36
+ config = stub(:Config, :projects => list())
37
+ expect { resolver(config, "foo:bar:noserver").resolve }.to raise_error(NonExistentDestinationError, /no such/i)
38
+ end
39
+ end
40
+
41
+ context "when there are 2 segments in path" do
42
+ context "when there is matching project - environment pair" do
43
+ it "should return server if only one specified" do
44
+ resolver(config, "foo:bar").resolve.should == server1
45
+ resolver(config, "baz:qux").resolve.should == server2
46
+ end
47
+
48
+ it "should raise error if there are multiple servers" do
49
+ expect { resolver(config, "quux:corge").resolve }.to raise_error(AmbiguousDestinationError, /servers/i)
50
+ end
51
+ end
52
+
53
+ context "when there is no matching project - environment pair" do
54
+ it "should raise error if no such project - environment pair exist" do
55
+ expect { resolver(config, "chunky:bacon").resolve }.to raise_error(NonExistentDestinationError, /project.*environment/i)
56
+ end
57
+ end
58
+ end
59
+
60
+ context "when there is only one segment in the path" do
61
+ context "when project with given name exists" do
62
+ context "when there's one environment" do
63
+ context "when there's one server" do
64
+ it "should return the server" do
65
+ resolver(config, "foo").resolve.should == server1
66
+ end
67
+ end
68
+
69
+ context "when there are multiple servers" do
70
+ it "should raise error" do
71
+ expect { resolver(config, "quux").resolve }.to raise_error(AmbiguousDestinationError, /servers/i)
72
+ end
73
+ end
74
+ end
75
+
76
+ context "when there are multiple environments" do
77
+ it "should raise error" do
78
+ expect { resolver(config, "grault").resolve }.to raise_error(AmbiguousDestinationError, /environments/i)
79
+ end
80
+ end
81
+ end
82
+
83
+ context "when there is no such project" do
84
+ it "should raise error" do
85
+ expect { resolver(config, "chunky").resolve }.to raise_error(NonExistentDestinationError, /project/i)
86
+ end
87
+ end
88
+ end
89
+
90
+ context "when passed path is empty" do
91
+ context "when there is no default destination set" do
92
+ context "when there's one project" do
93
+ context "when there's one environment" do
94
+ context "when there's one server" do
95
+ it "should execute command without asking project/environment/server" do
96
+ config = stub(:Config, :default_destination => nil, :projects => list(
97
+ stub(:name => :foo, :environments => list(
98
+ stub(:name => :bar, :servers => list(
99
+ server1
100
+ ))
101
+ ))
102
+ ))
103
+
104
+ resolver(config, "").resolve.should == server1
105
+ end
106
+ end
107
+
108
+ context "when there are multiple servers" do
109
+ let(:server1) { stub(:Server, :name => :s1) }
110
+ let(:server2) { stub(:Server, :name => :s2) }
111
+
112
+ it "should ask for server" do
113
+ config = stub(:Config, :default_destination => nil, :projects => list(
114
+ stub(:name => :foo, :environments => list(
115
+ stub(:name => :bar, :servers => list(
116
+ server1, server2
117
+ ))
118
+ ))
119
+ ))
120
+
121
+ expect { resolver(config, "").resolve }.to raise_error AmbiguousDestinationError, /server/i
122
+ end
123
+ end
124
+ end
125
+
126
+ context "when there are multiple environments" do
127
+ it "should ask for environment" do
128
+ config = stub(:Config, :default_destination => nil, :projects => list(
129
+ stub(:name => :foo, :environments => list(
130
+ stub(:name => :bar, :servers => [anything]),
131
+ stub(:name => :baz, :servers => [anything])
132
+ ))
133
+ ))
134
+
135
+ expect { resolver(config, "").resolve }.to raise_error AmbiguousDestinationError, /environment/i
136
+ end
137
+ end
138
+ end
139
+
140
+ context "when there are multiple projects" do
141
+ it "should ask for project" do
142
+ config = stub(:Config, :default_destination => nil, :projects => list(
143
+ stub(:name => :foo, :environments => [anything]),
144
+ stub(:name => :bar, :environments => [anything])
145
+ ))
146
+
147
+ expect { resolver(config, "").resolve }.to raise_error AmbiguousDestinationError, /projects/i
148
+ end
149
+ end
150
+ end
151
+
152
+ context "when there is default destination" do
153
+ it "should resolve by default destination" do
154
+ config.stub(:default_destination => "foo:bar:s1")
155
+ resolver(config, "").resolve.should == server1
156
+ end
157
+ end
158
+ end
159
+
160
+
161
+ def resolver(*args)
162
+ DestinationResolver.new(*args)
163
+ end
164
+
165
+ def list(*items)
166
+ Taketo::Support::NamedNodesCollection.new(items)
167
+ end
168
+
169
+ end
170
+
@@ -18,28 +18,36 @@ describe "DSL" do
18
18
  it { should_not be_appropriate_construct(scope_name, :foo).under(inappropriate_scope) }
19
19
  end
20
20
 
21
- it "should create a #{scope_name} and set it as current scope object" do
22
- dsl(parent_scope, factory.create(parent_scope_name)) do |c|
23
- factory.should_receive(:create).with(scope_name, :bar)
24
- c.send(scope_name, :bar) do
25
- c.current_scope_object.should == factory.send(scope_name)
26
- end
27
- end
28
- end
29
-
30
- it "should not leak #{scope_name} as current scope object" do
31
- dsl(parent_scope, factory.create(parent_scope_name)) do |c|
32
- c.send(scope_name, :bar) {}
33
- c.current_scope_object.should_not == factory.send(scope_name)
34
- end
35
- end
36
-
37
- it "should add a #{scope_name} to the #{parent_scope_name}'s #{scope_name}s collection" do
38
- dsl(parent_scope, factory.create(parent_scope_name)) do |c|
39
- c.current_scope_object.should_receive("append_#{scope_name}").with(factory.send("create_#{scope_name}", :bar))
40
- c.send(scope_name, :bar) {}
41
- end
42
- end
21
+ it "should create a #{scope_name} and set it as current scope object" do # it "should create project and set it as current scope object"
22
+ dsl(parent_scope, factory.create(parent_scope_name)) do |c| # dsl([:config], factory.create(:config)) do |c|
23
+ c.current_scope_object.should_receive(:find).with(scope_name, :bar). # c.current_scope_object.should_receive(:find).with(:project, :bar).
24
+ and_yield.and_return(:bacon) # and_yield.and_return(:bacon)
25
+ factory.should_receive(:create).with(scope_name, :bar) # factory.should_receive(:create).with(:project, :bar)
26
+ c.send(scope_name, :bar) do # c.project(:bar) do
27
+ c.current_scope_object.should_not be_nil # c.current_scope_object.should_not be_nil
28
+ c.current_scope_object.should == :bacon # c.current_scope_object.should == factory.project
29
+ end # end
30
+ end # end
31
+ end # end
32
+
33
+ it "should not leak #{scope_name} as current scope object" do # it "should not leak project as current scope object"
34
+ dsl(parent_scope, factory.create(parent_scope_name)) do |c| # dsl([:config], factory.create(:config)) do |c|
35
+ c.current_scope_object.stub(:find => factory.create(scope_name, :bar)) # c.current_scope.stub(:find => factory.create(:project, :bar))
36
+ c.send(scope_name, :bar) do # c.project(:bar) do
37
+ c.current_scope_object.should == factory.send(scope_name) # c.current_scope_object.should == factory.project
38
+ end # end
39
+ c.current_scope_object.should_not == factory.send(scope_name) # c.current_scope_object.should_not == factory.project
40
+ end # end
41
+ end # end
42
+
43
+ it "should add a #{scope_name} to the #{parent_scope_name}'s #{scope_name}s collection" do # it "should add a project to the config's projects collection" do
44
+ dsl(parent_scope, factory.create(parent_scope_name)) do |c| # dsl([:config], factory.create(:config)) do |c|
45
+ c.current_scope_object.stub(:find => factory.create(scope_name, :bar)) # c.current_scope_object.stub(:find => factory.create(:project, :bar))
46
+ c.current_scope_object.should_receive("append_#{scope_name}"). # c.current_scope_object.should_receive(:append_project).
47
+ with(factory.send(scope_name)) # with(factory.project)
48
+ c.send(scope_name, :bar) {} # c.project(:bar) {}
49
+ end # end
50
+ end # end
43
51
  end
44
52
 
45
53
  shared_examples "an attribute" do |attribute_name, parent_scope_name, parent_scope_method, example_value|
@@ -51,14 +59,18 @@ describe "DSL" do
51
59
  it { should_not be_appropriate_construct(attribute_name, example_value).under(inaproppriate_scope) }
52
60
  end
53
61
 
54
- it "should call #{parent_scope_method} on current #{parent_scope_name}" do
55
- dsl(parent_scope, factory.create(parent_scope_name, :foo)) do |c|
56
- factory.send(parent_scope_name).should_receive(parent_scope_method).with(example_value)
57
- c.send(attribute_name, example_value)
58
- end
62
+ it "should call #{parent_scope_method} on current #{parent_scope_name}" do # it "should call default_location= on current server" do
63
+ dsl(parent_scope, factory.create(parent_scope_name, :foo)) do |c| # dsl([:config, :project, :environment, :server], factory.create(:server, :foo)) do |c|
64
+ factory.send(parent_scope_name).should_receive(parent_scope_method).with(example_value) # factory.server.should_receive(:default_location=).with('/var/app/')
65
+ c.send(attribute_name, example_value) # c.location "/var/app"
66
+ end # end
59
67
  end
60
68
  end
61
69
 
70
+ describe "#default_destination" do
71
+ it_behaves_like "an attribute", :default_destination, :config, :default_destination=, "foo:bar:baz"
72
+ end
73
+
62
74
  describe "#project" do
63
75
  it_behaves_like "a scope", :project, :config
64
76
 
@@ -26,7 +26,13 @@ describe Taketo::Support::EvalDelegator do
26
26
  end.not_to raise_error
27
27
  end
28
28
 
29
- it "should pass local variables through the scopes" do
29
+ it "should raise if external context does not respond" do
30
+ expect do
31
+ EvalDelegatorContext.new.evaluate { qux }
32
+ end.to raise_error(NameError)
33
+ end
34
+
35
+ it "should pass local variabnes through the scopes" do
30
36
  baz = :foo
31
37
  expect do
32
38
  EvalDelegatorContext.new.evaluate { baz }
@@ -24,6 +24,13 @@ describe "NamedNodesCollection" do
24
24
  expect { collection[3] }.to raise_error KeyError, /#3/i
25
25
  expect { collection[:quux] }.to raise_error KeyError, /name/i
26
26
  end
27
+
28
+ it "should not add if node with same name exists" do
29
+ collection << node1
30
+ collection.push node1
31
+ collection.length.should == 1
32
+ collection.first.should == node1
33
+ end
27
34
  end
28
35
 
29
36
 
@@ -6,7 +6,7 @@ module DSLSpec
6
6
  send("create_#{type}", *args)
7
7
  end
8
8
 
9
- def create_config
9
+ def create_config(*args)
10
10
  @config ||= RSpec::Mocks::Mock.new(:Config).as_null_object
11
11
  end
12
12
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taketo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-21 00:00:00.000000000 Z
12
+ date: 2012-07-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -86,6 +86,7 @@ files:
86
86
  - lib/taketo/commands/ssh_command.rb
87
87
  - lib/taketo/commands.rb
88
88
  - lib/taketo/config_validator.rb
89
+ - lib/taketo/constructs/base_construct.rb
89
90
  - lib/taketo/constructs/command.rb
90
91
  - lib/taketo/constructs/config.rb
91
92
  - lib/taketo/constructs/environment.rb
@@ -93,26 +94,27 @@ files:
93
94
  - lib/taketo/constructs/server.rb
94
95
  - lib/taketo/constructs.rb
95
96
  - lib/taketo/constructs_factory.rb
97
+ - lib/taketo/destination_resolver.rb
96
98
  - lib/taketo/dsl.rb
97
99
  - lib/taketo/support/eval_delegator.rb
98
100
  - lib/taketo/support/key_error.rb
99
101
  - lib/taketo/support/named_nodes_collection.rb
100
102
  - lib/taketo/support.rb
101
- - lib/taketo/taketo_argv_parser.rb
102
103
  - lib/taketo.rb
103
104
  - spec/integration/dsl_integration_spec.rb
104
105
  - spec/lib/taketo/commands/ssh_command_spec.rb
105
106
  - spec/lib/taketo/config_validator_spec.rb
107
+ - spec/lib/taketo/constructs/base_construct_spec.rb
106
108
  - spec/lib/taketo/constructs/command_spec.rb
107
109
  - spec/lib/taketo/constructs/config_spec.rb
108
110
  - spec/lib/taketo/constructs/environment_spec.rb
109
111
  - spec/lib/taketo/constructs/project_spec.rb
110
112
  - spec/lib/taketo/constructs/server_spec.rb
111
113
  - spec/lib/taketo/constructs_factory_spec.rb
114
+ - spec/lib/taketo/destination_resolver_spec.rb
112
115
  - spec/lib/taketo/dsl_spec.rb
113
116
  - spec/lib/taketo/support/eval_delegator_spec.rb
114
117
  - spec/lib/taketo/support/named_nodes_collection_spec.rb
115
- - spec/lib/taketo/taketo_argv_parser_spec.rb
116
118
  - spec/spec_helper.rb
117
119
  - spec/support/helpers/dsl_spec_helper.rb
118
120
  - spec/support/matchers/be_appropriate_construct_matcher.rb
@@ -1,48 +0,0 @@
1
- module Taketo
2
- class ConfigError < StandardError; end
3
-
4
- class TaketoArgvParser
5
- def initialize(config, argv)
6
- @config = config
7
- @argv = argv
8
- end
9
-
10
- def parse
11
- if all_specified?
12
- project_name, environment_name, server_name = @argv.shift(3).map(&:to_sym)
13
-
14
- begin
15
- return @config.projects[project_name].environments[environment_name].servers[server_name]
16
- rescue KeyError
17
- raise ArgumentError,
18
- "Server #{server_name} in environment #{environment_name} for project #{project_name} does not exist"
19
- end
20
- end
21
-
22
- if @config.projects.one?
23
- project = @config.projects.first
24
- if project.environments.one?
25
- environment = project.environments.first
26
- if environment.servers.one?
27
- return environment.servers.first
28
- elsif environment.servers.length > 1
29
- if server_name = @argv.shift
30
- return environment.servers[server_name.to_sym]
31
- else
32
- raise ArgumentError, "There are multiple servers for project #{project.name} " \
33
- "in environment #{environment.name}: #{environment.servers.map(&:name).join(", ")}"
34
- end
35
- end
36
- end
37
- end
38
- end
39
-
40
- private
41
-
42
- def all_specified?
43
- @argv.size >= 3
44
- end
45
-
46
- end
47
- end
48
-
@@ -1,92 +0,0 @@
1
- require File.expand_path('../../../spec_helper', __FILE__)
2
- require 'taketo/taketo_argv_parser'
3
- require 'taketo/support/named_nodes_collection'
4
-
5
- include Taketo
6
-
7
- describe "TaketoArgvParser" do
8
- context "when project, environment and server specified" do
9
- let(:server) { stub(:Server, :name => :baz) }
10
-
11
- it "should return server if it exists" do
12
- config = stub(:Config, :projects => list(
13
- stub(:name => :foo, :environments => list(
14
- stub(:name => :bar, :servers => list(
15
- server
16
- ))
17
- ))
18
- ))
19
-
20
- parser(config, %w(foo bar baz)).parse.should == server
21
- end
22
-
23
- it "should raise error if server does not exist" do
24
- config = stub(:Config, :projects => list())
25
- expect do
26
- parser(config, %w(foo bar baz)).parse
27
- end.to raise_error ArgumentError, /exist/i
28
- end
29
- end
30
-
31
- context "when there are two arguments" do
32
- end
33
-
34
- context "when there are no args" do
35
- context "when there's one project" do
36
- context "when there's one environment" do
37
- context "when there's one server" do
38
- let(:server) { stub(:Server) }
39
-
40
- it "should execute command without asking project/environment/server" do
41
- config = stub(:Config, :projects => list(
42
- stub(:name => :foo, :environments => list(
43
- stub(:name => :bar, :servers => list(
44
- server
45
- ))
46
- ))
47
- ))
48
-
49
- parser(config, []).parse.should == server
50
- end
51
- end
52
-
53
- context "when there are multiple servers" do
54
- let(:server1) { stub(:Server, :name => :s1) }
55
- let(:server2) { stub(:Server, :name => :s2) }
56
-
57
- it "should ask for server if it's not specified" do
58
- config = stub(:Config, :projects => list(
59
- stub(:name => :foo, :environments => list(
60
- stub(:name => :bar, :servers => list(
61
- server1, server2
62
- ))
63
- ))
64
- ))
65
-
66
- expect do
67
- parser(config, []).parse
68
- end.to raise_error ArgumentError, /server/i
69
-
70
- parser(config, ["s1"]).parse.should == server1
71
- end
72
- end
73
- end
74
-
75
- context "when there are multiple environments" do
76
- context "when environment not specified" do
77
- it "should ask for environment"
78
- end
79
- end
80
- end
81
- end
82
-
83
- def parser(*args)
84
- TaketoArgvParser.new(*args)
85
- end
86
-
87
- def list(*items)
88
- Taketo::Support::NamedNodesCollection.new(items)
89
- end
90
-
91
- end
92
-