class SimplyGenius::Atmos::SettingsHash
Constants
- INTERP_PATTERN
- PATH_PATTERN
Attributes
_root_[RW]
enable_expansion[RW]
error_resolver[RW]
Public Class Methods
add_config(yml_file, key, value, additive: true)
click to toggle source
# File lib/simplygenius/atmos/settings_hash.rb, line 169 def self.add_config(yml_file, key, value, additive: true) orig_config_with_comments = File.read(yml_file) comment_places = {} comment_lines = [] orig_config_with_comments.each_line do |line| line.gsub!(/\s+$/, "\n") if line =~ /^\s*(#.*)?$/ comment_lines << line else if comment_lines.present? comment_places[line.chomp] = comment_lines comment_lines = [] end end end comment_places["<EOF>"] = comment_lines orig_config = SettingsHash.new((YAML.load_file(yml_file) rescue {})) # expansion disabled by default, but being explicit since we don't want # expansion when mutating config files from generators orig_config.enable_expansion = false orig_config.notation_put(key, value, additive: additive) new_config_no_comments = YAML.dump(orig_config.to_hash) new_config_no_comments.sub!(/\A---\n/, "") new_yml = "" new_config_no_comments.each_line do |line| line.gsub!(/\s+$/, "\n") cline = comment_places.keys.find {|k| line =~ /^#{k}/ } comments = comment_places[cline] comments.each {|comment| new_yml << comment } if comments new_yml << line end comment_places["<EOF>"].each {|comment| new_yml << comment } return new_yml end
Public Instance Methods
each() { |key, self| ... }
click to toggle source
allows expansion when iterating
# File lib/simplygenius/atmos/settings_hash.rb, line 58 def each each_key do |key| yield key, self[key] end end
expand(value)
click to toggle source
# File lib/simplygenius/atmos/settings_hash.rb, line 111 def expand(value) result = value case value when Hash value when String expand_string(value) when Enumerable value.map! {|v| expand(v)} # HACK: accounting for the case when someone wants to force an override using '^' as the first list item, when # there is no upstream to override (i.e. merge proc doesn't get triggered as key is unique, so just added verbatim) value.delete_at(0) if value[0] == "^" value else value end end
expand_results(name, &blk)
click to toggle source
# File lib/simplygenius/atmos/settings_hash.rb, line 21 def expand_results(name, &blk) # NOTE: we lookup locally first, then globally if a value is missing # locally. To force a global lookup, use the explicit qualifier like # "_root_.path.to.config" value = blk.call(name) if value.nil? && _root_ && enable_expansion value = _root_[name] end if value.kind_of?(self.class) && value._root_.nil? value._root_ = _root_ || self end enable_expansion ? expand(value) : value end
expand_string(obj)
click to toggle source
# File lib/simplygenius/atmos/settings_hash.rb, line 82 def expand_string(obj) result = obj result.scan(INTERP_PATTERN).each do |substr, statement| # TODO: add an explicit check for cycles instead of relying on Stack error begin if substr.start_with?('##') val = substr[1..-1] else # TODO: be consistent with dot notation between eval and # notation_get. eval ends up calling Hashie method_missing, # which returns nil if a key doesn't exist, causing a nil # exception for next item in chain, while notation_get returns # nil gracefully for the entire chain (preferred) val = eval(statement, binding, __FILE__) end rescue SystemStackError => e raise ConfigInterpolationError.new(format_error("Cycle in interpolated config", substr)) rescue StandardError => e raise ConfigInterpolationError.new(format_error("Failing config statement", substr, e)) end result = result.sub(substr, val.to_s) end result = true if result == "true" result = false if result == "false" result end
expanding_reader(key)
click to toggle source
# File lib/simplygenius/atmos/settings_hash.rb, line 39 def expanding_reader(key) expand_results(key) {|k| orig_reader(k) } end
Also aliased as: []
fetch(key, *args)
click to toggle source
Calls superclass method
# File lib/simplygenius/atmos/settings_hash.rb, line 43 def fetch(key, *args) expand_results(key) {|k| super(k, *args) } end
format_error(msg, expr, ex=nil)
click to toggle source
# File lib/simplygenius/atmos/settings_hash.rb, line 69 def format_error(msg, expr, ex=nil) file, line = nil, nil if error_resolver file, line = error_resolver.call(expr) end file_msg = file.nil? ? "" : " in #{File.basename(file)}:#{line}" msg = "#{msg} '#{expr}'#{file_msg}" if ex msg += " => #{ex.class} #{ex.message}" end return msg end
notation_get(key)
click to toggle source
# File lib/simplygenius/atmos/settings_hash.rb, line 129 def notation_get(key) path = key.to_s.split(PATH_PATTERN).compact path = path.collect {|p| p =~ /^\d+$/ ? p.to_i : p } result = nil begin result = deep_fetch(*path) rescue Hashie::Extensions::DeepFetch::UndefinedPathError => e logger.debug("Settings missing value for key='#{key}'") end return result end
notation_put(key, value, additive: true)
click to toggle source
# File lib/simplygenius/atmos/settings_hash.rb, line 143 def notation_put(key, value, additive: true) path = key.to_s.split(PATH_PATTERN).compact path = path.collect {|p| p =~ /^\d+$/ ? p.to_i : p } current_level = self path.each_with_index do |p, i| if i == path.size - 1 if additive && current_level[p].is_a?(Array) current_level[p] = current_level[p] | Array(value) else current_level[p] = value end else if current_level[p].nil? if path[i+1].is_a?(Integer) current_level[p] = [] else current_level[p] = {} end end end current_level = current_level[p] end end
to_a()
click to toggle source
allows expansion for to_a
(which doesn't use each)
# File lib/simplygenius/atmos/settings_hash.rb, line 65 def to_a self.collect {|k, v| [k, v]} end