lib/build-cloud.rb in build-cloud-0.0.8 vs lib/build-cloud.rb in build-cloud-0.0.9

- old
+ new

@@ -1,11 +1,10 @@ require 'fog' require 'yaml' require 'pry' require 'logger' require 'pp' -require 'pathname' class BuildCloud @config @log @@ -14,32 +13,80 @@ def initialize( options ) @log = options[:logger] or Logger.new( STDERR ) @mock = options[:mock] or false - - first_config_file = options[:config].shift + # Parse the first config file. We'll merge the remainder (if any) into + # this one, regardless of whether they're passed on the command line + # or in the YAML for this file itself. + first_config_file = options[:config].shift @config = YAML::load( File.open( first_config_file ) ) - include_files = options[:config] + # include_files is going to be a list of files that we're going to + # merge in to the first file. + include_files = [] + + # Work out the full, standardised pathnames for any further files + # specified on the command line. note that options[:config] only + # contains the extra files at this point, as we shifted the first + # one off the array earlier. + # + # IMPORTANT: relative paths given on the command line are considered + # to be relative to $CWD. This decision is based on the principle of + # least surprise, as that is how everything else works. + cli_include_files = options[:config] + cli_include_files.each do |inc| + include_files << File.absolute_path( inc ) + end + + # Now look in the :include key in the YAML of the first file for + # either a single, or an array of files to include. Work out the + # standardised paths for each of these files, and push them onto + # the include_files array. + # + # IMPORTANT: relative paths given in the :include key in the YAML + # are considered to be relative to the config file specified, not + # $CWD. This is to ensure consistency of application and backwards + # compatibility. If this were relative to $CWD, a relative path + # specified in the file could have different meanings, and would end + # up being unpredictable. if include_yaml = @config.delete(:include) if include_yaml.is_a?(Array) - include_files.concat(include_yaml) + # the :include key is an array, we need to iterate over it + include_yaml.each do |yml| + include_files << File.expand_path( yml, File.dirname( File.absolute_path(first_config_file) ) ) + end else - include_files.push(include_yaml) + # the :include key is a scalar, so just standardise that path + include_files.push( File.expand_path( include_yaml, File.dirname( File.absolute_path(first_config_file) ) ) ) end end - include_files.each do |include_file| + include_files.each do |include_path| - include_path = Pathname.new(File.join( File.dirname( first_config_file ), include_file)).realpath - if File.exists?( include_path ) @log.info( "Including YAML file #{include_path}" ) included_conf = YAML::load( File.open( include_path ) ) @config = @config.merge(included_conf) do |keys, oldval, newval| - (newval.is_a?(Array) ? (oldval + newval) : (oldval << newval)).uniq + # we're iterating over elements that are in both the + # config we've parsed so far, and the new file. + (newval.is_a?(Array) ? (oldval + newval).uniq : newval) + # oldval is from the existing config, newval is the incoming + # value from this file. if newval is an array, merge it in with + # what we already have, and make it unique. if newval is a + # string, the new value takes precedence over what we have + # already. + # + # edge cases: + # 1. if we have a key :foo which is a scalar, and then a + # :foo in a subsequent file which is an array (or v.v.) + # then this will blow up. I think this is acceptable. + # 2. if we have, eg. an instance, defined twice in + # separate files, then the behaviour of uniq is to use + # the entire hash as a test for uniqueness. Therefore + # if the definition of those instances varies slightly, + # the attempt to create those instances will likely fail. end end end