class Dentaku::BulkExpressionSolver

Attributes

calculator[R]
evaluator[R]
expression_hash[R]

Public Class Methods

dependency_cache() click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 66
def self.dependency_cache
  @dep_cache ||= {}
end
new(expressions, calculator) click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 32
def initialize(expressions, calculator)
  @expression_hash = FlatHash.from_hash(expressions)
  @calculator = calculator
end

Public Instance Methods

dependencies() click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 55
def dependencies
  Hash[expression_deps].tap do |d|
    d.values.each do |deps|
      unresolved = deps.reject { |ud| d.has_key?(ud) }
      unresolved.each { |u| add_dependencies(d, u) }
    end
  end
end
solve(&block) click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 42
def solve(&block)
  @evaluator ||= PermissiveEvaluator.new(calculator, block)
  error_handler = block || return_undefined_handler
  results = load_results(&error_handler)

  FlatHash.expand(
    expression_hash.each_with_object({}) do |(k, v), r|
      default = v.nil? ? v : :undefined
      r[k] = results.fetch(k.to_s, default)
    end
  )
end
solve!() click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 37
def solve!
  @evaluator = StrictEvaluator.new(calculator)
  solve(&raise_exception_handler)
end

Private Instance Methods

add_dependencies(current_dependencies, variable) click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 141
def add_dependencies(current_dependencies, variable)
  node = calculator.memory[variable]
  if node.respond_to?(:dependencies)
    current_dependencies[variable] = node.dependencies
    node.dependencies.each { |d| add_dependencies(current_dependencies, d) }
  end
end
expression_deps() click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 135
def expression_deps
  expressions.map do |var, expr|
    [var, calculator.dependencies(expr)]
  end
end
expression_with_exception_handler(var_name, &block) click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 80
def expression_with_exception_handler(var_name, &block)
  ->(_expr, ex) {
    ex.recipient_variable = var_name
    block.call(ex)
  }
end
expressions() click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 131
def expressions
  @expressions ||= Hash[expression_hash.map { |k, v| [k.to_s, v] }]
end
load_results(&block) click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 87
def load_results(&block)
  facts, _formulas = expressions.transform_keys(&:downcase)
                                .transform_values { |v| calculator.ast(v) }
                                .partition { |_, v| calculator.dependencies(v, nil).empty? }

  evaluated_facts = facts.to_h.each_with_object({}) do |(var_name, ast), h|
    with_rescues(var_name, h, block) do
      h[var_name] = ast.is_a?(Array) ? ast.map(&:value) : ast.value
    end
  end

  context = calculator.memory.merge(evaluated_facts)

  variables_in_resolve_order.each_with_object({}) do |var_name, results|
    next if expressions[var_name].nil?

    with_rescues(var_name, results, block) do
      results[var_name] = evaluated_facts[var_name] || evaluator.evaluate(
        expressions[var_name],
        context.merge(results),
        &expression_with_exception_handler(var_name, &block)
      ).tap { |res|
        res.recipient_variable = var_name if res.respond_to?(:recipient_variable=)
        res
      }
    end
  end

rescue TSort::Cyclic => ex
  block.call(ex)
  {}
end
raise_exception_handler() click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 76
def raise_exception_handler
  ->(ex) { raise ex }
end
return_undefined_handler() click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 72
def return_undefined_handler
  ->(*) { :undefined }
end
variables_in_resolve_order() click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 149
def variables_in_resolve_order
  cache_key = expressions.keys.map(&:to_s).sort.join("|")
  @ordered_deps ||= self.class.dependency_cache.fetch(cache_key) {
    DependencyResolver.find_resolve_order(dependencies).tap do |d|
      self.class.dependency_cache[cache_key] = d if Dentaku.cache_dependency_order?
    end
  }
end
with_rescues(var_name, results, block) { || ... } click to toggle source
# File lib/dentaku/bulk_expression_solver.rb, line 120
def with_rescues(var_name, results, block)
  yield
rescue Dentaku::UnboundVariableError, Dentaku::ZeroDivisionError, Dentaku::ArgumentError => ex
  ex.recipient_variable = var_name
  results[var_name] = block.call(ex)
ensure
  if results[var_name] == :undefined && calculator.memory.has_key?(var_name.downcase)
    results[var_name] = calculator.memory[var_name.downcase]
  end
end