taketo 0.0.1

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 ADDED
@@ -0,0 +1,9 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem 'rspec'
5
+ gem 'rake'
6
+ gem 'aruba'
7
+ gem 'simplecov', :require => false
8
+ end
9
+
data/Gemfile.lock ADDED
@@ -0,0 +1,44 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ aruba (0.4.11)
5
+ childprocess (>= 0.2.3)
6
+ cucumber (>= 1.1.1)
7
+ ffi (>= 1.0.11)
8
+ rspec (>= 2.7.0)
9
+ builder (3.0.0)
10
+ childprocess (0.3.2)
11
+ ffi (~> 1.0.6)
12
+ cucumber (1.2.1)
13
+ builder (>= 2.1.2)
14
+ diff-lcs (>= 1.1.3)
15
+ gherkin (~> 2.11.0)
16
+ json (>= 1.4.6)
17
+ diff-lcs (1.1.3)
18
+ ffi (1.0.11)
19
+ gherkin (2.11.0)
20
+ json (>= 1.4.6)
21
+ json (1.7.3)
22
+ multi_json (1.3.6)
23
+ rake (0.9.2.2)
24
+ rspec (2.10.0)
25
+ rspec-core (~> 2.10.0)
26
+ rspec-expectations (~> 2.10.0)
27
+ rspec-mocks (~> 2.10.0)
28
+ rspec-core (2.10.1)
29
+ rspec-expectations (2.10.0)
30
+ diff-lcs (~> 1.1.3)
31
+ rspec-mocks (2.10.1)
32
+ simplecov (0.6.4)
33
+ multi_json (~> 1.0)
34
+ simplecov-html (~> 0.5.3)
35
+ simplecov-html (0.5.3)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ aruba
42
+ rake
43
+ rspec
44
+ simplecov
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Vladimir
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ Take Me To
2
+ ==========
3
+
4
+ [![Build Status](https://secure.travis-ci.org/v-yarotsky/taketo.png)](http://travis-ci.org/v-yarotsky/taketo)
5
+
6
+ A tiny helper utility to make access to servers eaiser for different projects and environments.
7
+
8
+ Usage:
9
+ ------
10
+
11
+ puts a config into ```~/.taketo.rc.rb```:
12
+
13
+ ```ruby
14
+ project :my_project do
15
+ environment :staging do
16
+ server :s1 do
17
+ host "1.2.3.4"
18
+ port 10001
19
+ user "app"
20
+ localtion "/var/app"
21
+ end
22
+ end
23
+ end
24
+ ```
25
+
26
+ Then execute:
27
+
28
+ ```taketo my_project staging s1 [command to execute, bash by default]```
29
+
30
+ To-Do:
31
+ ------
32
+
33
+ * Add support for defaults
34
+ * Add support for generating shortcuts
35
+
36
+ The Changelog:
37
+ --------------
38
+
39
+ ### v0.0.1 (13.06.2012) ###
40
+ * Initial release
41
+ * Support for simplest configs
42
+
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+
13
+ require 'rake'
14
+ require 'rake/testtask'
15
+ require 'cucumber'
16
+ require 'cucumber/rake/task'
17
+
18
+ Rake::TestTask.new do |t|
19
+ t.test_files = Dir.glob('spec/**/*_spec.rb')
20
+ t.verbose = true
21
+ end
22
+
23
+ Cucumber::Rake::Task.new(:features) do |t|
24
+ t.cucumber_opts = "features --tags ~@wip --format pretty -x"
25
+ t.fork = false
26
+ end
27
+
28
+ task :default => [:test, :features]
29
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/taketo ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'taketo'
7
+ require 'taketo/constructs_factory'
8
+ require 'taketo/commands'
9
+ require 'optparse'
10
+ rescue LoadError => e #development
11
+ $: << File.expand_path('../../lib', __FILE__)
12
+ require 'bundler/setup'
13
+ retry
14
+ end
15
+
16
+ Signal.trap("SIGINT") do
17
+ puts "Terminating"
18
+ exit 1
19
+ end
20
+
21
+ include Taketo
22
+
23
+ def parse_options
24
+ options = { :config => File.join(ENV['HOME'], ".taketo.rc.rb") }
25
+
26
+ OptionParser.new do |opts|
27
+ opts.banner = <<-DESC
28
+ A tiny helper utility to make access to servers
29
+ eaiser for different projects and environments.
30
+ DESC
31
+
32
+ opts.on("-c CONFIG", "--config") do |c|
33
+ options[:config] = c
34
+ end
35
+
36
+ opts.on("--dry-run") do |v|
37
+ options[:dry_run] = v
38
+ end
39
+ end.parse!
40
+
41
+ options
42
+ end
43
+
44
+ def parse_config(options)
45
+ factory = Taketo::ConstructsFactory.new
46
+
47
+ Taketo::DSL.new(factory).configure do
48
+ config_text = File.read(options[:config])
49
+ eval config_text, binding, options[:config], 1
50
+ end
51
+ end
52
+
53
+ options = parse_options
54
+ config = parse_config(options)
55
+
56
+ project, environment, server = ARGV.shift(3).map(&:to_sym)
57
+
58
+ server = begin
59
+ config.projects.fetch(project).environments.fetch(environment).servers.fetch(server)
60
+ rescue KeyError => e
61
+ raise ArgumentError, e
62
+ end
63
+
64
+ remote_command = ARGV.join(" ")
65
+ remote_command = "bash" if remote_command.empty?
66
+ command_to_execute = Commands::SSHCommand.new(server).render(remote_command)
67
+ if options[:dry_run]
68
+ puts command_to_execute
69
+ else
70
+ system command_to_execute
71
+ end
72
+
@@ -0,0 +1,25 @@
1
+ Feature:
2
+ In order to be able to access my servers quickly
3
+ As a developer
4
+ I want to have a nifty little utility
5
+ configurable with simple DSL
6
+
7
+ Scenario: SSH to server
8
+ When I have the following config in "/tmp/taketo_test_cfg.rb"
9
+ """
10
+ project :slots do
11
+ environment :staging do
12
+ server :s1 do
13
+ host "1.2.3.4"
14
+ user "deployer"
15
+ location "/var/apps/slots"
16
+ end
17
+ end
18
+ end
19
+
20
+ """
21
+ And I successfully run `taketo --config=/tmp/taketo_test_cfg.rb --dry-run slots staging s1`
22
+ Then the output should contain
23
+ """
24
+ ssh -t "deployer"@"1.2.3.4" "cd '/var/apps/slots'; RAILS_ENV=staging bash"
25
+ """
@@ -0,0 +1,10 @@
1
+ When /^I have the following config in "(.*?)"$/ do |path, config|
2
+ @config_path = path
3
+ File.open(path, "w") do |f|
4
+ f.write(config)
5
+ end
6
+ end
7
+
8
+ Then /^the output should contain$/ do |expected|
9
+ assert_partial_output(expected, all_output)
10
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'aruba/cucumber'
4
+ require 'fileutils'
5
+
6
+ ENV['PATH'] = "#{File.expand_path('../../../bin', __FILE__)}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
7
+
8
+ After do
9
+ if defined? @config_path and File.exist?(@config_path)
10
+ FileUtils.rm(@config_path)
11
+ end
12
+ end
@@ -0,0 +1,44 @@
1
+ require 'forwardable'
2
+
3
+ module Taketo
4
+ module Commands
5
+ class SSHCommand
6
+ extend Forwardable
7
+
8
+ def_delegators :@server, :host, :port, :username, :default_location
9
+
10
+ def initialize(server, options = {})
11
+ @server = server
12
+ @environment = @server.environment
13
+ end
14
+
15
+ def render(command = "bash")
16
+ %Q[ssh -t #{port} #{username}#{host} "#{[location, environment, command].compact.join(" ")}"].gsub(/ +/, " ")
17
+ end
18
+
19
+ def host
20
+ unless @server.host
21
+ raise ArgumentError, "host for server #{@server.name} in #{@environment.name} is not defined!"
22
+ end
23
+ %Q["#{@server.host}"]
24
+ end
25
+
26
+ def port
27
+ %Q[-p #{@server.port}] if @server.port
28
+ end
29
+
30
+ def username
31
+ %Q["#{@server.username}"@] if @server.username
32
+ end
33
+
34
+ def location
35
+ %Q[cd '#{@server.default_location}';] if @server.default_location
36
+ end
37
+
38
+ def environment
39
+ %Q[RAILS_ENV=#{@environment.name}]
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,6 @@
1
+ module Taketo
2
+ module Commands
3
+ end
4
+ end
5
+
6
+ Dir.glob(File.expand_path('../commands/*.rb', __FILE__)).each { |c| require c }
@@ -0,0 +1,16 @@
1
+ module Taketo
2
+ module Constructs
3
+ class Config
4
+ attr_reader :projects
5
+
6
+ def initialize
7
+ @projects = {}
8
+ end
9
+
10
+ def append_project(project)
11
+ @projects[project.name] = project
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,18 @@
1
+ module Taketo
2
+ module Constructs
3
+ class Environment
4
+ attr_reader :name, :servers
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ @servers = {}
9
+ end
10
+
11
+ def append_server(server)
12
+ server.environment = self
13
+ @servers[server.name] = server
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,17 @@
1
+ module Taketo
2
+ module Constructs
3
+ class Project
4
+ attr_reader :name, :environments
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ @environments = {}
9
+ end
10
+
11
+ def append_environment(environment)
12
+ @environments[environment.name] = environment
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,13 @@
1
+ module Taketo
2
+ module Constructs
3
+ class Server
4
+ attr_reader :name
5
+ attr_accessor :host, :port, :username, :default_location, :environment
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,6 @@
1
+ module Taketo
2
+ module Constructs
3
+ end
4
+ end
5
+
6
+ Dir.glob(File.expand_path('../constructs/*.rb', __FILE__)).each { |c| require c }
@@ -0,0 +1,22 @@
1
+ require 'taketo/constructs'
2
+
3
+ module Taketo
4
+ class ConstructsFactory
5
+ def create_config
6
+ Constructs::Config.new
7
+ end
8
+
9
+ def create_project(*args)
10
+ Constructs::Project.new(*args)
11
+ end
12
+
13
+ def create_environment(*args)
14
+ Constructs::Environment.new(*args)
15
+ end
16
+
17
+ def create_server(*args)
18
+ Constructs::Server.new(*args)
19
+ end
20
+ end
21
+ end
22
+
data/lib/taketo/dsl.rb ADDED
@@ -0,0 +1,90 @@
1
+ require 'taketo/support'
2
+
3
+ module Taketo
4
+ class DSL
5
+ class ScopeError < StandardError; end
6
+
7
+ class << self
8
+ def define_scope(scope, parent_scope)
9
+ define_method scope do |name, &block|
10
+ unless current_scope?(parent_scope)
11
+ raise ScopeError,
12
+ "#{scope} can't be defined in #{current_scope} scope"
13
+ end
14
+ scope_object = @factory.send("create_#{scope}", name)
15
+ in_scope(scope, scope_object) do
16
+ block.call
17
+ end
18
+ end
19
+ end
20
+
21
+ def define_attribute(name, parent_scope, &block)
22
+ define_method name do |attribute|
23
+ unless server_scope?
24
+ raise ScopeError,
25
+ "#{name} can't be defined in #{current_scope} scope"
26
+ end
27
+ instance_exec(attribute, &block)
28
+ end
29
+ end
30
+ end
31
+
32
+ attr_reader :current_scope_object, :config
33
+
34
+ def initialize(factory)
35
+ @factory = factory
36
+ @scope = [:config]
37
+ @config = @current_scope_object = factory.create_config
38
+ end
39
+
40
+ def configure(&block)
41
+ instance_eval(&block)
42
+ @config
43
+ end
44
+
45
+ define_scope :project, :config
46
+ define_scope :environment, :project
47
+ define_scope :server, :environment
48
+
49
+ define_attribute :host, :server do |hostname|
50
+ current_scope_object.host = hostname
51
+ end
52
+
53
+ define_attribute :port, :server do |port_number|
54
+ current_scope_object.port = port_number
55
+ end
56
+
57
+ define_attribute :user, :server do |username|
58
+ current_scope_object.username = username
59
+ end
60
+
61
+ define_attribute :location, :server do |path|
62
+ current_scope_object.default_location = path
63
+ end
64
+
65
+ private
66
+
67
+ def current_scope
68
+ @scope.last
69
+ end
70
+
71
+ def current_scope?(scope)
72
+ current_scope == scope
73
+ end
74
+
75
+ [:config, :project, :environment, :server].each do |scope|
76
+ define_method("#{scope}_scope?") { current_scope == scope }
77
+ end
78
+
79
+ def in_scope(scope, new_scope_object)
80
+ parent_scope_object, @current_scope_object = @current_scope_object, new_scope_object
81
+ @scope << scope
82
+ yield
83
+ parent_scope_object.send("append_#{scope}", current_scope_object)
84
+ @scope.pop
85
+ @current_scope_object = parent_scope_object
86
+ end
87
+
88
+ end
89
+ end
90
+
@@ -0,0 +1,25 @@
1
+ module Taketo
2
+ module Support
3
+
4
+ ##
5
+ # This module allows to use external
6
+ # methods in block used by instance_eval,
7
+ # that it effectively mimics real closure
8
+ #
9
+ module EvalDelegator
10
+ def evaluate(&block)
11
+ @external_self = eval "self", block.binding
12
+ self.instance_eval(&block)
13
+ end
14
+
15
+ def method_missing(method_name, *args, &block)
16
+ if @external_self.respond_to?(method_name)
17
+ @external_self.send(method_name, *args, &block)
18
+ else
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,7 @@
1
+ module Taketo
2
+ module Support
3
+ end
4
+ end
5
+
6
+ require 'taketo/support/eval_delegator'
7
+
data/lib/taketo.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Taketo
2
+ version_file = File.expand_path('../VERSION', File.dirname(__FILE__))
3
+ VERSION = File.read(version_file).freeze
4
+ end
5
+
6
+ require 'taketo/support'
7
+ require 'taketo/dsl'
8
+ require 'taketo/commands'
9
+
@@ -0,0 +1,48 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'taketo/dsl'
3
+
4
+ describe "Taketo DSL" do
5
+ it "should parse config and instantiate objects" do
6
+ factory = Taketo::ConstructsFactory.new
7
+ config = Taketo::DSL.new(factory).configure do
8
+ project :slots do
9
+ environment :staging do
10
+ server :staging do
11
+ host "127.0.0.2"
12
+ user "deployer"
13
+ location "/var/app"
14
+ end
15
+ end
16
+
17
+ environment :production do
18
+ {
19
+ :s1 => "127.0.0.3",
20
+ :s2 => "127.0.0.4",
21
+ }.each do |server_name, host_name|
22
+ server server_name do
23
+ host host_name
24
+ location "/var/app"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ config.projects.length.should == 1
32
+ project = config.projects[:slots]
33
+ project.name.should == :slots
34
+
35
+ project.environments.length.should == 2
36
+ staging = project.environments[:staging]
37
+
38
+ staging.servers.length.should == 1
39
+ staging_server = staging.servers[:staging]
40
+ staging_server.host.should == "127.0.0.2"
41
+ staging_server.username.should == "deployer"
42
+ staging_server.default_location.should == "/var/app"
43
+
44
+ production = project.environments[:production]
45
+ production.servers.length.should == 2
46
+ end
47
+ end
48
+
@@ -0,0 +1,43 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require 'taketo/commands/ssh_command'
3
+
4
+ include Taketo::Commands
5
+
6
+ describe "SSH Command" do
7
+ let(:environment) { stub(:Environment, :name => :staging) }
8
+ let(:server) do
9
+ stub(:Server, :name => :s1,
10
+ :host => "1.2.3.4",
11
+ :port => 22,
12
+ :username => "deployer",
13
+ :default_location => "/var/app",
14
+ :environment => environment)
15
+ end
16
+
17
+ let(:ssh_command) { SSHCommand.new(server) }
18
+
19
+ it "should compose command based on provided server object" do
20
+ ssh_command.render("bash").should == %q[ssh -t -p 22 "deployer"@"1.2.3.4" "cd '/var/app'; RAILS_ENV=staging bash"]
21
+ end
22
+
23
+ it "should ignore absent parts" do
24
+ server.stub(:port => nil)
25
+ server.stub(:username => nil)
26
+ ssh_command.render("bash").should == %q[ssh -t "1.2.3.4" "cd '/var/app'; RAILS_ENV=staging bash"]
27
+
28
+ server.stub(:default_location => nil)
29
+ ssh_command.render("bash").should == %q[ssh -t "1.2.3.4" "RAILS_ENV=staging bash"]
30
+ end
31
+
32
+ it "should require host" do
33
+ server.stub(:host => nil)
34
+ expect do
35
+ ssh_command.render("bash")
36
+ end.to raise_error ArgumentError, /host/
37
+ end
38
+
39
+ it "should have 'bash' as command default" do
40
+ ssh_command.render.should =~ /bash/
41
+ end
42
+ end
43
+
@@ -0,0 +1,17 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require 'taketo/constructs/config'
3
+
4
+ include Taketo
5
+
6
+ describe "Config" do
7
+ let(:config) { Taketo::Constructs::Config.new }
8
+
9
+ describe "#append_project" do
10
+ it "should add project to config's projects collection" do
11
+ project = stub(:name => :foo)
12
+ config.append_project(project)
13
+ config.projects[:foo].should == project
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require 'taketo/constructs/environment'
3
+
4
+ include Taketo
5
+
6
+ describe "Environment" do
7
+ let(:environment) { Taketo::Constructs::Environment.new(:foo) }
8
+
9
+ it "should have name" do
10
+ environment.name.should == :foo
11
+ end
12
+
13
+ describe "#append_server" do
14
+ let(:server) { mock(:Server, :name => :foo).as_null_object }
15
+
16
+ it "should add a server to environment's servers collection" do
17
+ environment.append_server(server)
18
+ environment.servers[:foo].should == server
19
+ end
20
+
21
+ it "should set environment attribute on a server to self" do
22
+ server.should_receive(:environment=).with(environment)
23
+ environment.append_server(server)
24
+ end
25
+ end
26
+ end
27
+
28
+
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require 'taketo/constructs/project'
3
+
4
+ include Taketo
5
+
6
+ describe "Project" do
7
+ let(:project) { Taketo::Constructs::Project.new(:foo) }
8
+
9
+ it "should have name" do
10
+ project.name.should == :foo
11
+ end
12
+
13
+ describe "#append_environment" do
14
+ it "should add an environment to project's environments collection" do
15
+ environment = stub(:name => :foo)
16
+ project.append_environment(environment)
17
+ project.environments[:foo].should == environment
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,41 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require 'taketo/constructs/server'
3
+ require 'stringio'
4
+
5
+ include Taketo
6
+
7
+ describe "Server" do
8
+ let(:server) { Taketo::Constructs::Server.new(:foo) }
9
+
10
+ it "should have name" do
11
+ server.name.should == :foo
12
+ end
13
+
14
+ it "should set host" do
15
+ server.host = "foo"
16
+ server.host.should == "foo"
17
+ end
18
+
19
+ it "should set port" do
20
+ server.port = "foo"
21
+ server.port.should == "foo"
22
+ end
23
+
24
+ it "should set username" do
25
+ server.username = "foo"
26
+ server.username.should == "foo"
27
+ end
28
+
29
+ it "should set default_location" do
30
+ server.default_location = "foo"
31
+ server.default_location.should == "foo"
32
+ end
33
+
34
+ it "should set environment" do
35
+ environment = stub(:Environment)
36
+ server.environment = environment
37
+ server.environment.should == environment
38
+ end
39
+ end
40
+
41
+
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'taketo/constructs_factory'
3
+
4
+ include Taketo
5
+
6
+ describe "ConstructsFactory" do
7
+ let(:factory) { Taketo::ConstructsFactory.new }
8
+
9
+ specify "#create_config should create a config object" do
10
+ factory.create_config.should be_an_instance_of(Taketo::Constructs::Config)
11
+ end
12
+
13
+ specify "#create_project should create a project object" do
14
+ project = factory.create_project(:foo)
15
+ project.should be_an_instance_of(Taketo::Constructs::Project)
16
+ end
17
+
18
+ specify "#create_environment should create an environment object" do
19
+ environment = factory.create_environment(:foo)
20
+ environment.should be_an_instance_of(Taketo::Constructs::Environment)
21
+ end
22
+
23
+ specify "#create_server should create a server object" do
24
+ server = factory.create_server(:foo)
25
+ server.should be_an_instance_of(Taketo::Constructs::Server)
26
+ end
27
+ end
28
+
@@ -0,0 +1,108 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'support/helpers/dsl_spec_helper'
3
+ require 'taketo/dsl'
4
+
5
+ include Taketo
6
+
7
+ describe "DSL" do
8
+ extend DSLSpec
9
+ include DSLSpec
10
+
11
+ shared_examples "a scope" do |scope_name, parent_scope_name|
12
+ parent_scope = scopes[parent_scope_name]
13
+
14
+ it { should enclose_scope(scope_name).under(parent_scope) }
15
+ it { should be_appropriate_construct(scope_name, :foo).under(parent_scope) }
16
+
17
+ scopes.except(parent_scope_name).each do |inappropriate_scope|
18
+ it { should_not be_appropriate_construct(scope_name, :foo).under(inappropriate_scope) }
19
+ end
20
+
21
+ it "should create a #{scope_name} and set it as current scope object" do
22
+ factory.should_receive("create_#{scope_name}").with(:bar)
23
+ dsl(parent_scope, factory.send("create_#{parent_scope_name}")) do |c|
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.send("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.send("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
43
+ end
44
+
45
+ shared_examples "an attribute" do |attribute_name, parent_scope_name, parent_scope_method, example_value|
46
+ parent_scope = scopes[parent_scope_name]
47
+
48
+ it { should be_appropriate_construct(attribute_name, example_value).under(parent_scope) }
49
+
50
+ scopes.except(parent_scope_name).each do |inaproppriate_scope|
51
+ it { should_not be_appropriate_construct(attribute_name, example_value).under(inaproppriate_scope) }
52
+ end
53
+
54
+ it "should set #{parent_scope_method.to_s.gsub('=', '')} attribute on current server" do
55
+ dsl(parent_scope, factory.send("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
59
+ end
60
+ end
61
+
62
+ describe "#project" do
63
+ it_behaves_like "a scope", :project, :config
64
+
65
+ describe "#environment" do
66
+ it_behaves_like "a scope", :environment, :project
67
+
68
+ describe "#server" do
69
+ it_behaves_like "a scope", :server, :environment
70
+
71
+ describe "#host" do
72
+ it_behaves_like "an attribute", :host, :server, :host=, "127.0.0.2"
73
+ end
74
+
75
+ describe "#port" do
76
+ it_behaves_like "an attribute", :port, :server, :port=, 4096
77
+ end
78
+
79
+ describe "#user" do
80
+ it_behaves_like "an attribute", :user, :server, :username=, "deployer"
81
+ end
82
+
83
+ describe "#location" do
84
+ it_behaves_like "an attribute", :location, :server, :default_location=, "/var/app/"
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ describe "#current_scope_object" do
91
+ it "should be config initially" do
92
+ dsl do |c|
93
+ c.current_scope_object.should == factory.config
94
+ end
95
+ end
96
+
97
+ it "should correspond to current scope" do
98
+ dsl(:project, factory.create_project(:foo)) do |c|
99
+ c.current_scope_object.should == factory.project
100
+ end
101
+
102
+ dsl(:server, factory.create_server(:foo)) do |c|
103
+ c.current_scope_object.should == factory.server
104
+ end
105
+ end
106
+ end
107
+ end
108
+
@@ -0,0 +1,37 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+ require 'taketo/support/eval_delegator'
3
+
4
+ class EvalDelegatorContext
5
+ include Taketo::Support::EvalDelegator
6
+
7
+ def foo
8
+ end
9
+ end
10
+
11
+ describe Taketo::Support::EvalDelegator do
12
+ describe "#evaluate" do
13
+ it "should execute methods on context if it responds" do
14
+ expect do
15
+ EvalDelegatorContext.new.evaluate { foo }
16
+ end.not_to raise_error
17
+ end
18
+
19
+ it "should delegate unknown methods to calling context" do
20
+ class << self
21
+ define_method(:bar) {}
22
+ end
23
+
24
+ expect do
25
+ EvalDelegatorContext.new.evaluate { bar }
26
+ end.not_to raise_error
27
+ end
28
+
29
+ it "should pass local variables through the scopes" do
30
+ baz = :foo
31
+ expect do
32
+ EvalDelegatorContext.new.evaluate { baz }
33
+ end.not_to raise_error
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ if ENV['COVERAGE']
5
+ require 'simplecov'
6
+ SimpleCov.start
7
+ end
8
+
9
+ $: << File.expand_path('../../lib', __FILE__)
10
+
11
+ require 'rspec'
12
+ require 'rspec/autorun'
13
+
14
+ require 'taketo/dsl'
15
+
16
+ Dir[File.dirname(__FILE__) + "/support/matchers/*.rb"].each {|f| require f}
17
+
@@ -0,0 +1,59 @@
1
+ module DSLSpec
2
+ class TestConstructsFactory
3
+ attr_reader :config, :project, :environment, :server
4
+
5
+ def create_config
6
+ @config ||= RSpec::Mocks::Mock.new(:Config).as_null_object
7
+ end
8
+
9
+ def create_project(name = :foo)
10
+ @project ||= RSpec::Mocks::Mock.new(:Project, :name => name).as_null_object
11
+ end
12
+
13
+ def create_environment(name = :foo)
14
+ @environment ||= RSpec::Mocks::Mock.new(:Environment, :name => name).as_null_object
15
+ end
16
+
17
+ def create_server(name = :foo)
18
+ @server ||= RSpec::Mocks::Mock.new(:Server, :name => name).as_null_object
19
+ end
20
+ end
21
+
22
+ def factory
23
+ @factory ||= TestConstructsFactory.new
24
+ end
25
+
26
+ RSpec.configure do |config|
27
+ config.after(:each) do
28
+ @factory = nil
29
+ end
30
+ end
31
+
32
+ def dsl(scope = [:config], scope_object = nil)
33
+ context = DSL.new(factory)
34
+
35
+ def context.set_scope(s, obj)
36
+ @scope = s
37
+ @current_scope_object = obj
38
+ end
39
+
40
+ context.set_scope(scope, scope_object || factory.create_config)
41
+
42
+ yield context
43
+ end
44
+
45
+ def scopes
46
+ scopes_hash = {
47
+ :config => [:config],
48
+ :project => [:config, :project],
49
+ :environment => [:config, :project, :environment],
50
+ :server => [:config, :project, :environment, :server]
51
+ }
52
+
53
+ def scopes_hash.except(*keys)
54
+ self.values_at(*(self.keys - keys))
55
+ end
56
+
57
+ scopes_hash
58
+ end
59
+ end
@@ -0,0 +1,38 @@
1
+ require 'taketo/dsl'
2
+
3
+ RSpec::Matchers.define :be_appropriate_construct do |construct, *args|
4
+ chain(:under) do |enclosing_scope|
5
+ @enclosing_scope = enclosing_scope
6
+ end
7
+
8
+ match do |actual|
9
+ unless @enclosing_scope
10
+ raise ArgumentError, "#under must be called to set enclosing scope"
11
+ end
12
+
13
+ @result = true
14
+ dsl(@enclosing_scope) do |c|
15
+ begin
16
+ c.send(construct, *args) {}
17
+ rescue Taketo::DSL::ScopeError => e
18
+ @result = false
19
+ rescue
20
+ #nothing
21
+ end
22
+ end
23
+ @result
24
+ end
25
+
26
+ description do
27
+ "#{name_to_sentence} ##{construct}(#{args.join(", ")}) under #{@enclosing_scope.inspect}"
28
+ end
29
+
30
+ failure_message_for_should do |actual|
31
+ "expected construct #{construct} to be appropriate under #{@enclosing_scope.inspect} scope"
32
+ end
33
+
34
+ failure_message_for_should_not do |actual|
35
+ "expected construct #{construct} not to be appropriate under #{@enclosing_scope.inspect} scope"
36
+ end
37
+ end
38
+
@@ -0,0 +1,18 @@
1
+ RSpec::Matchers.define :enclose_scope do |expected_scope|
2
+ chain(:under) do |scope|
3
+ @external_scope = scope
4
+ end
5
+
6
+ match do |actual|
7
+ @result = true
8
+ dsl(@external_scope || [:config]) do |c|
9
+ @result &= !c.send(:current_scope?, expected_scope)
10
+ c.send(expected_scope, :foo) do
11
+ @result &= c.send(:current_scope?, expected_scope)
12
+ end
13
+ @result &= !c.send(:current_scope?, expected_scope)
14
+ end
15
+ @result
16
+ end
17
+ end
18
+
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: taketo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Vladimir Yarotsky
9
+ - Maksim Yermalovich
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-06-13 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '2.10'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: '2.10'
31
+ - !ruby/object:Gem::Dependency
32
+ name: rake
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: '0.9'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '0.9'
47
+ - !ruby/object:Gem::Dependency
48
+ name: aruba
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.4'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: '0.4'
63
+ - !ruby/object:Gem::Dependency
64
+ name: simplecov
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ version: '0.6'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ version: '0.6'
79
+ description:
80
+ email: vladimir.yarotksy@gmail.com
81
+ executables:
82
+ - taketo
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - bin/taketo
87
+ - lib/taketo/commands/ssh_command.rb
88
+ - lib/taketo/commands.rb
89
+ - lib/taketo/constructs/config.rb
90
+ - lib/taketo/constructs/environment.rb
91
+ - lib/taketo/constructs/project.rb
92
+ - lib/taketo/constructs/server.rb
93
+ - lib/taketo/constructs.rb
94
+ - lib/taketo/constructs_factory.rb
95
+ - lib/taketo/dsl.rb
96
+ - lib/taketo/support/eval_delegator.rb
97
+ - lib/taketo/support.rb
98
+ - lib/taketo.rb
99
+ - spec/integration/dsl_integration_spec.rb
100
+ - spec/lib/commands/ssh_command_spec.rb
101
+ - spec/lib/constructs/config_spec.rb
102
+ - spec/lib/constructs/environment_spec.rb
103
+ - spec/lib/constructs/project_spec.rb
104
+ - spec/lib/constructs/server_spec.rb
105
+ - spec/lib/constructs_factory_spec.rb
106
+ - spec/lib/dsl_spec.rb
107
+ - spec/lib/support/eval_delegator_spec.rb
108
+ - spec/spec_helper.rb
109
+ - spec/support/helpers/dsl_spec_helper.rb
110
+ - spec/support/matchers/be_appropriate_construct_matcher.rb
111
+ - spec/support/matchers/enclose_scope_matcher.rb
112
+ - features/connect_to_server.feature
113
+ - features/step_definitions/main_steps.rb
114
+ - features/support/env.rb
115
+ - Gemfile
116
+ - Gemfile.lock
117
+ - Rakefile
118
+ - LICENSE.txt
119
+ - README.md
120
+ - VERSION
121
+ homepage: http://github.com/v-yarotsky/taketo
122
+ licenses:
123
+ - MIT
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: 1.3.6
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 1.8.21
143
+ signing_key:
144
+ specification_version: 3
145
+ summary: A tiny helper utility to make access to servers eaiser for different projects
146
+ and environments
147
+ test_files: []