class Vanagon::Environment
Environment
is a validating wrapper around a delegated Hash, analogous to Ruby’s built in accessor Env. It’s intended to be used for defining multiple Environments, which can be used and manipulated the same way a bare Hash would be. We’re delegating instead of subclassing because subclassing from Ruby Core is inviting calamity – that stuff is written in C and may not correspond to assumptions you could safely make about Ruby.
Public Class Methods
Create a new Environment
@return [Vanagon::Environment] a new Environment
, with no defined env. vars.
# File lib/vanagon/environment.rb, line 47 def initialize @data = {} end
Public Instance Methods
Associates the value given by value with the key given by key. Keys will be cast to Strings, and should conform to the Open Group’s guidelines for portable shell variable names:
Environment variable names used by the utilities in the Shell and Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase letters, digits, and the '_' (underscore) from the characters defined in Portable Character Set and do not begin with a digit.
Values will be cast to Strings, and will be stored precisely as given, so any escaped characters, single or double quotes, or whitespace will be preserved exactly as passed during assignment.
@param key [String] @param value [String, Integer] @raise [ArgumentError] if key or value cannot be cast to a String
# File lib/vanagon/environment.rb, line 67 def []=(key, value) @data.update({ validate_key(key) => validate_value(value) }) end
Returns a new Environment
containing the contents of other_env and the contents of env. @param other_env [Environment] @example Merge two Environments
>> local = Vanagon::Environment.new => #<Vanagon::Environment:0x007fc54d913f38 @data={}> >> global = Vanagon::Environment.new => #<Vanagon::Environment:0x007fc54b06da70 @data={}> >> local['PATH'] = '/usr/local/bin:/usr/bin:/bin' >> global['CC'] = 'ccache gcc' >> local.merge global => #<Vanagon::Environment:0x007fc54b0a72e8 @data={"PATH"=>"/usr/local/bin:/usr/bin:/bin", "CC"=>"ccache gcc"}>
# File lib/vanagon/environment.rb, line 83 def merge(other_env) env_copy = self.dup other_env.each_pair do |k, v| env_copy[k] = v end env_copy end
Adds the contents of other_env to env. @param other_env [Environment] @example Merge two Environments
>> local = Vanagon::Environment.new => #<Vanagon::Environment:0x007f8c68933b08 @data={}> >> global = Vanagon::Environment.new => #<Vanagon::Environment:0x007f8c644e5640 @data={}> >> local['PATH'] = '/usr/local/bin:/usr/bin:/bin' >> global['CC'] = 'ccache gcc' >> local.merge! global => #<Vanagon::Environment:0x007f8c68933b08 @data={"PATH"=>"/usr/local/bin:/usr/bin:/bin", "CC"=>"ccache gcc"}>
# File lib/vanagon/environment.rb, line 103 def merge!(other_env) @data = merge(other_env).instance_variable_get(:@data) end
Converts env to an array of “#{key}=#{value}” strings, suitable for joining into a command. @example Convert to an Array
>> local = Vanagon::Environment.new => #<Vanagon::Environment:0x007f8c68991258 @data={}> >> local['PATH'] = '/usr/local/bin:/usr/bin:/bin' => "/usr/local/bin:/usr/bin:/bin" >> local['CC'] = 'clang' => "clang" >> local.to_a => ["PATH=\"/usr/local/bin:/usr/bin:/bin\"", "CC=\"clang\""]
# File lib/vanagon/environment.rb, line 118 def to_a(delim = "=") @data.map { |k, v| %(#{k}#{delim}#{v}) } end
Converts env to a string by concatenating together all key-value pairs with a single space. @example Convert to an Array
>> local = Vanagon::Environment.new => #<Vanagon::Environment:0x007f8c68014358 @data={}> >> local['PATH'] = '/usr/local/bin:/usr/bin:/bin' >> local['CC'] = 'clang' >> puts local PATH=/usr/local/bin:/usr/bin:/bin >>
# File lib/vanagon/environment.rb, line 133 def to_s to_a.join("\s") end
Private Instance Methods
# File lib/vanagon/environment.rb, line 138 def sanitize_subshells(str) pattern = %r{\$\$\((.*?)\)} escaped_variables = str.scan(pattern).flatten return str if escaped_variables.empty? warning = [%(Value "#{str}" looks like it's escaping one or more values for subshell interpolation.)] escaped_variables.each { |v| warning.push %(\t"$$(#{v})" will be coerced to "$(shell #{v})") } warning.push <<-WARNING.undent All environment variables will now be resolved by Make before they're executed by the shell. These variables will be mangled for you for now, but you should update your project's parameters. WARNING VanagonLogger.info warning.join("\n") str.gsub(pattern, '$(shell \1)') end
# File lib/vanagon/environment.rb, line 156 def sanitize_variables(str) pattern = %r{\$\$([\w]+)} escaped_variables = str.scan(pattern).flatten return str if escaped_variables.empty? warning = [%(Value "#{str}" looks like it's escaping one or more shell variable names for shell interpolation.)] escaped_variables.each { |v| warning.push %(\t"$$#{v}" will be coerced to "$(#{v})") } warning.push <<-WARNING.undent All environment variables will now be resolved by Make before they're executed by the shell. These variables will be mangled for you for now, but you should update your project's parameters. WARNING VanagonLogger.info warning.join("\n") str.gsub(pattern, '$(\1)') end
Cast key to a String
, and validate that it does not contain invalid
characters, and that it does not begin with a digit
@param key [Object] @raise [ArgumentError] if key is not a String
, if key contains invalid
characters, or if key begins with a digit
# File lib/vanagon/environment.rb, line 179 def validate_key(key) environment_string = key.to_s if environment_string[0] =~ /\d/ raise ArgumentError, 'environment variable Name cannot begin with a digit' end invalid_characters = environment_string .scan(/[^\w]/) .uniq .map { |char| %("#{char}") } .join(', ') return environment_string if invalid_characters.empty? raise ArgumentError, "environment variable Name contains invalid characters: #{invalid_characters}" end
Cast str to a String
, and validate that the value of str cannot be split into more than a single String
by shellsplit. @param value [Object]
# File lib/vanagon/environment.rb, line 201 def validate_value(str) # sanitize the value, which should look for any Shell escaped # variable names inside of the value. sanitize_variables(sanitize_subshells(str.to_s)) end