class Etcenv::VariableExpander

Constants

MAX_DEPTH_DEFAULT
VARIABLE

Attributes

variables[R]

Public Class Methods

expand(variables, max = MAX_DEPTH_DEFAULT) click to toggle source
# File lib/etcenv/variable_expander.rb, line 10
def self.expand(variables, max = MAX_DEPTH_DEFAULT)
  new(variables).expand(max)
end
new(variables) click to toggle source
# File lib/etcenv/variable_expander.rb, line 14
def initialize(variables)
  @variables = variables.dup.freeze
end

Public Instance Methods

dependees_by_variable() click to toggle source
# File lib/etcenv/variable_expander.rb, line 34
def dependees_by_variable
  @dependees_by_variable ||= variable_with_deps.inject({}) do |r, x|
    k, v, deps = x[0], x[1][0], x[1][1]

    deps.each do |dep|
      r[dep] ||= []
      r[dep] << k
    end
    r
  end.freeze
end
detect_loop!() click to toggle source
# File lib/etcenv/variable_expander.rb, line 56
def detect_loop!
  if !variables.empty? && root_variables.empty?
    raise LoopError, "there's no possible root variables (variables which don't have dependee)"
  else
    dependees_by_variable.each do |k, deps|
      deps.each do |dep|
        if (dependees_by_variable[dep] || []).include?(k)
          raise LoopError, "There's a loop between $#{dep} and $#{k}"
        end
      end
    end
  end
end
expand(max = MAX_DEPTH_DEFAULT) click to toggle source
# File lib/etcenv/variable_expander.rb, line 20
def expand(max = MAX_DEPTH_DEFAULT)
  detect_loop!

  result = {}
  solve_order(max).map do |x|
    result[x] = single_expand(@variables[x], result)
  end
  result
end
root_variables() click to toggle source
# File lib/etcenv/variable_expander.rb, line 46
def root_variables
  @root_variables ||= variable_with_deps.inject([]) do |r, x|
    k, v, deps = x[0], x[1][0], x[1][1]
    if deps.empty?
      r << k
    end
    r
  end + (variables.keys - dependees_by_variable.keys)
end
solve_order(max_depth = 10) click to toggle source
# File lib/etcenv/variable_expander.rb, line 70
def solve_order(max_depth = 10)
  order = []
  solve = nil
  solve = ->(vs, depth = 0) do
    raise DepthLimitError if depth.succ > max_depth
    vs.each do |x|
      order.concat solve.call(dependees_by_variable[x] || [], depth.succ) + [x]
    end
  end
  solve.call(root_variables)

  Utils.uniq_with_keeping_first_appearance(order).reverse
end
variable_with_deps() click to toggle source
# File lib/etcenv/variable_expander.rb, line 30
def variable_with_deps
  @variables.map { |k, v| [k, [v, detect_variables(v)]] }
end

Private Instance Methods

detect_variables(str) click to toggle source
# File lib/etcenv/variable_expander.rb, line 96
def detect_variables(str)
  result = []

  pos = 0
  while match = str.match(VARIABLE, pos)
    pos = match.end(0)
    next if match['escape'] == '\\'

    result << match['name']
  end

  result
end
single_expand(str, variables) click to toggle source
# File lib/etcenv/variable_expander.rb, line 110
def single_expand(str, variables)
  str.gsub(VARIABLE) do |variable|
    match = $~

    if match['escape'] == '\\'
      variable[1..-1]
    else
      variables[match['name']].to_s
    end
  end
end