vaguerent/lib/vagrant/config.rb
2010-09-22 09:43:30 -06:00

175 lines
5.7 KiB
Ruby

require 'vagrant/config/base'
require 'vagrant/config/error_recorder'
module Vagrant
# The config class is responsible for loading Vagrant configurations, which
# are usually found in Vagrantfiles but may also be procs. The loading is done
# by specifying a queue of files or procs that are for configuration, and then
# executing them. The config loader will run each item in the queue, so that
# configuration from later items overwrite that from earlier items. This is how
# Vagrant "scoping" of Vagranfiles is implemented.
#
# If you're looking to create your own configuration classes, see {Base}.
#
# # Loading Configuration Files
#
# If you are in fact looking to load configuration files, then this is the
# class you are looking for. Loading configuration is quite easy. The following
# example assumes `env` is already a loaded instance of {Environment}:
#
# config = Vagrant::Config.new(env)
# config.queue << "/path/to/some/Vagrantfile"
# result = config.load!
#
# p "Your box is: #{result.vm.box}"
#
class Config
extend Util::StackedProcRunner
@@config = nil
attr_reader :queue
class << self
# Resets the current loaded config object to the specified environment.
# This clears the proc stack and initializes a new {Top} for loading.
# This method shouldn't be called directly, instead use an instance of this
# class for config loading.
#
# @param [Environment] env
def reset!(env=nil)
@@config = nil
proc_stack.clear
# Reset the configuration to the specified environment
config(env)
end
# Returns the current {Top} configuration object. While this is still
# here for implementation purposes, it shouldn't be called directly. Instead,
# use an instance of this class.
def config(env=nil)
@@config ||= Config::Top.new(env)
end
# Adds the given proc/block to the stack of config procs which are all
# run later on a single config object. This is the main way to configure
# Vagrant, and is how all Vagrantfiles are formatted:
#
# Vagrant::Config.run do |config|
# # ...
# end
#
def run(&block)
push_proc(&block)
end
# Executes all the config procs onto the currently loaded {Top} object,
# and returns the final configured object. This also validates the
# configuration by calling {Top#validate!} on every configuration
# class.
def execute!(config_object=nil)
config_object ||= config
run_procs!(config_object)
config_object.validate!
config_object
end
end
# Initialize a {Config} object for the given {Environment}.
#
# @param [Environment] env Environment which config object will be part
# of.
def initialize(env)
@env = env
@queue = []
end
# Loads the queue of files/procs, executes them in the proper
# sequence, and returns the resulting configuration object.
def load!
self.class.reset!(@env)
queue.flatten.each do |item|
if item.is_a?(String) && File.exist?(item)
begin
load item
rescue SyntaxError => e
# Report syntax errors in a nice way for Vagrantfiles
raise Errors::VagrantfileSyntaxError.new(:file => e.message)
end
elsif item.is_a?(Proc)
self.class.run(&item)
end
end
return self.class.execute!
end
end
class Config
# This class is the "top" configure class, which handles registering
# other configuration classes as well as validation of all configured
# classes. This is the object which is returned by {Environment#config}
# and has accessors to all other configuration classes.
#
# If you're looking to create your own configuration class, see {Base}.
class Top < Base
@@configures = []
class << self
# The list of registered configuration classes as well as the key
# they're registered under.
def configures_list
@@configures ||= []
end
# Registers a configuration class with the given key. This method shouldn't
# be called. Instead, inherit from {Base} and call {Base.configures}.
def configures(key, klass)
configures_list << [key, klass]
attr_reader key.to_sym
end
end
def initialize(env=nil)
self.class.configures_list.each do |key, klass|
config = klass.new
config.env = env
instance_variable_set("@#{key}".to_sym, config)
end
@env = env
end
# Validates the configuration classes of this instance and raises an
# exception if they are invalid. If you are implementing a custom configuration
# class, the method you want to implement is {Base#validate}. This is
# the method that checks all the validation, not one which defines
# validation rules.
def validate!
# Validate each of the configured classes and store the results into
# a hash.
errors = self.class.configures_list.inject({}) do |container, data|
key, _ = data
recorder = ErrorRecorder.new
send(key.to_sym).validate(recorder)
container[key.to_sym] = recorder if !recorder.errors.empty?
container
end
return if errors.empty?
raise Errors::ConfigValidationFailed.new(:messages => Util::TemplateRenderer.render("config/validation_failed", :errors => errors))
end
end
end
end
# The built-in configuration classes
require 'vagrant/config/vagrant'
require 'vagrant/config/ssh'
require 'vagrant/config/nfs'
require 'vagrant/config/vm'
require 'vagrant/config/package'