Class: SwitchTower::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/switchtower/configuration.rb

Overview

Represents a specific SwitchTower configuration. A Configuration instance may be used to load multiple recipe files, define and describe tasks, define roles, create an actor, and set configuration variables.

Defined Under Namespace

Classes: Role

Constant Summary collapse

DEFAULT_VERSION_DIR_NAME =

:nodoc:

"releases"
DEFAULT_CURRENT_DIR_NAME =

:nodoc:

"current"
DEFAULT_SHARED_DIR_NAME =

:nodoc:

"shared"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(actor_class = Actor) ⇒ Configuration

:nodoc:



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/switchtower/configuration.rb', line 36

def initialize(actor_class=Actor) #:nodoc:
  @roles = Hash.new { |h,k| h[k] = [] }
  @actor = actor_class.new(self)
  @logger = Logger.new
  @load_paths = [".", File.join(File.dirname(__FILE__), "recipes")]
  @variables = {}
  @now = Time.now.utc

  # for preserving the original value of Proc-valued variables
  set :original_value, Hash.new

  set :application, nil
  set :repository,  nil
  set :gateway,     nil
  set :user,        nil
  set :password,    nil
  
  set :ssh_options, Hash.new
		
  set(:deploy_to)   { "/u/apps/#{application}" }

  set :version_dir, DEFAULT_VERSION_DIR_NAME
  set :current_dir, DEFAULT_CURRENT_DIR_NAME
  set :shared_dir,  DEFAULT_SHARED_DIR_NAME
  set :scm,         :subversion

  set(:revision)    { source.latest_revision }
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args, &block) ⇒ Object

:nodoc:



235
236
237
238
239
240
241
# File 'lib/switchtower/configuration.rb', line 235

def method_missing(sym, *args, &block) #:nodoc:
  if args.length == 0 && block.nil? && @variables.has_key?(sym)
    self[sym]
  else
    super
  end
end

Instance Attribute Details

#actorObject (readonly)

The actor created for this configuration instance.



18
19
20
# File 'lib/switchtower/configuration.rb', line 18

def actor
  @actor
end

#load_pathsObject (readonly)

The load paths used for locating recipe files.



27
28
29
# File 'lib/switchtower/configuration.rb', line 27

def load_paths
  @load_paths
end

#loggerObject (readonly)

The logger instance defined for this configuration.



24
25
26
# File 'lib/switchtower/configuration.rb', line 24

def logger
  @logger
end

#nowObject (readonly)

The time (in UTC) at which this configuration was created, used for determining the release path.



31
32
33
# File 'lib/switchtower/configuration.rb', line 31

def now
  @now
end

#rolesObject (readonly)

The list of Role instances defined for this configuration.



21
22
23
# File 'lib/switchtower/configuration.rb', line 21

def roles
  @roles
end

#variablesObject (readonly)

The has of variables currently known by the configuration



34
35
36
# File 'lib/switchtower/configuration.rb', line 34

def variables
  @variables
end

Instance Method Details

#[](variable) ⇒ Object

Access a named variable. If the value of the variable responds_to? :call, #call will be invoked (without parameters) and the return value cached and returned.



85
86
87
88
89
90
91
# File 'lib/switchtower/configuration.rb', line 85

def [](variable)
  if @variables[variable].respond_to?(:call)
    self[:original_value][variable] = @variables[variable]
    set variable, @variables[variable].call
  end
  @variables[variable]
end

#current_pathObject

Return the path identifying the current symlink, used to identify the current release.



216
217
218
# File 'lib/switchtower/configuration.rb', line 216

def current_path
  File.join(deploy_to, current_dir)
end

#desc(text) ⇒ Object

Describe the next task to be defined. The given text will be attached to the next task that is defined and used as its description.



179
180
181
# File 'lib/switchtower/configuration.rb', line 179

def desc(text)
  @next_description = text
end

#load(*args, &block) ⇒ Object

Load a configuration file or string into this configuration.

Usage:

load("recipe"):
  Look for and load the contents of 'recipe.rb' into this
  configuration.

load(:file => "recipe"):
  same as above

load(:string => "set :scm, :subversion"):
  Load the given string as a configuration specification.

load { ... }
  Load the block in the context of the configuration.


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/switchtower/configuration.rb', line 123

def load(*args, &block)
  options = args.last.is_a?(Hash) ? args.pop : {}
  args.each { |arg| load options.merge(:file => arg) }
  return unless args.empty?

  if block
    raise "loading a block requires 0 parameters" unless args.empty?
    load(options.merge(:proc => block))

  elsif options[:file]
    file = options[:file]
    unless file[0] == ?/
      load_paths.each do |path|
        if File.file?(File.join(path, file))
          file = File.join(path, file)
          break
        elsif File.file?(File.join(path, file) + ".rb")
          file = File.join(path, file + ".rb")
          break
        end
      end
    end

    load :string => File.read(file), :name => options[:name] || file

  elsif options[:string]
    logger.trace "loading configuration #{options[:name] || "<eval>"}"
    instance_eval(options[:string], options[:name] || "<eval>")

  elsif options[:proc]
    logger.trace "loading configuration #{options[:proc].inspect}"
    instance_eval(&options[:proc])

  else
    raise ArgumentError, "don't know how to load #{options.inspect}"
  end
end

#release_path(release = now.strftime("%Y%m%d%H%M%S")) ⇒ Object

Return the full path to the named release. If a release is not specified, now is used (the time at which the configuration was created).



227
228
229
# File 'lib/switchtower/configuration.rb', line 227

def release_path(release=now.strftime("%Y%m%d%H%M%S"))
  File.join(releases_path, release)
end

#releases_pathObject

Return the path into which releases should be deployed.



210
211
212
# File 'lib/switchtower/configuration.rb', line 210

def releases_path
  File.join(deploy_to, version_dir)
end

#require(*args) ⇒ Object

Require another file. This is identical to the standard require method, with the exception that it sets the reciever as the “current” configuration so that third-party task bundles can include themselves relative to that configuration.



201
202
203
204
205
206
207
# File 'lib/switchtower/configuration.rb', line 201

def require(*args) #:nodoc:
  original, SwitchTower.configuration = SwitchTower.configuration, self
  super
ensure
  # restore the original, so that require's can be nested
  SwitchTower.configuration = original
end

#respond_to?(sym) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


231
232
233
# File 'lib/switchtower/configuration.rb', line 231

def respond_to?(sym) #:nodoc:
  @variables.has_key?(sym) || super
end

#role(which, *args) ⇒ Object

Define a new role and its associated servers. You must specify at least one host for each role. Also, you can specify additional information (in the form of a Hash) which can be used to more uniquely specify the subset of servers specified by this specific role definition.

Usage:

role :db, "db1.example.com", "db2.example.com"
role :db, "master.example.com", :primary => true
role :app, "app1.example.com", "app2.example.com"

Raises:

  • (ArgumentError)


171
172
173
174
175
# File 'lib/switchtower/configuration.rb', line 171

def role(which, *args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  raise ArgumentError, "must give at least one host" if args.empty?
  args.each { |host| roles[which] << Role.new(host, options) }
end

#set(variable, value = nil, &block) ⇒ Object Also known as: []=

Set a variable to the given value.



66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/switchtower/configuration.rb', line 66

def set(variable, value=nil, &block)
  # if the variable is uppercase, then we add it as a constant to the
  # actor. This is to allow uppercase "variables" to be set and referenced
  # in recipes.
  if variable.to_s[0].between?(?A, ?Z)
    klass = @actor.metaclass
    klass.send(:remove_const, variable) if klass.const_defined?(variable)
    klass.const_set(variable, value)
  end

  value = block if value.nil? && block_given?
  @variables[variable] = value
end

#shared_pathObject

Return the path into which shared files should be stored.



221
222
223
# File 'lib/switchtower/configuration.rb', line 221

def shared_path
  File.join(deploy_to, shared_dir)
end

#sourceObject

Based on the current value of the :scm variable, instantiate and return an SCM module representing the desired source control behavior.



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/switchtower/configuration.rb', line 95

def source
  @source ||= case scm
    when Class then
      scm.new(self)
    when String, Symbol then
      require "switchtower/scm/#{scm.to_s.downcase}"
      SwitchTower::SCM.const_get(scm.to_s.downcase.capitalize).new(self)
    else
      raise "invalid scm specification: #{scm.inspect}"
  end
end

#task(name, options = {}, &block) ⇒ Object

Define a new task. If a description is active (see #desc), it is added to the options under the :desc key. This method ultimately delegates to Actor#define_task.

Raises:

  • (ArgumentError)


186
187
188
189
190
191
192
193
194
195
# File 'lib/switchtower/configuration.rb', line 186

def task(name, options={}, &block)
  raise ArgumentError, "expected a block" unless block

  if @next_description
    options = options.merge(:desc => @next_description)
    @next_description = nil
  end

  actor.define_task(name, options, &block)
end