Skip to content

Commit

Permalink
allow overriding calculator memory with variables
Browse files Browse the repository at this point in the history
Fixes #223
  • Loading branch information
rubysolo committed Nov 27, 2020
1 parent 7c98f7a commit 74121f4
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 29 deletions.
53 changes: 28 additions & 25 deletions lib/dentaku/bulk_expression_solver.rb
Expand Up @@ -6,9 +6,10 @@

module Dentaku
class BulkExpressionSolver
def initialize(expressions, calculator)
def initialize(expressions, calculator, precedence = :memory)
@expression_hash = FlatHash.from_hash(expressions)
@calculator = calculator
@precedence = precedence
end

def solve!
Expand Down Expand Up @@ -38,6 +39,10 @@ def dependencies

private

def prefer_memory?
@precedence == :memory
end

def self.dependency_cache
@dep_cache ||= {}
end
Expand All @@ -57,31 +62,29 @@ def expression_with_exception_handler(&block)
end

def load_results(&block)
variables_in_resolve_order.each_with_object({}) do |var_name, r|
begin
solved = calculator.memory
value_from_memory = solved[var_name.downcase]

if value_from_memory.nil? &&
expressions[var_name].nil? &&
!solved.has_key?(var_name)
next
end

value = value_from_memory || evaluate!(
expressions[var_name],
expressions.merge(r).merge(solved),
&expression_with_exception_handler(&block)
)

r[var_name] = value
rescue UnboundVariableError, Dentaku::ZeroDivisionError => ex
ex.recipient_variable = var_name
r[var_name] = block.call(ex)
rescue Dentaku::ArgumentError => ex
r[var_name] = block.call(ex)
end
normalized = expressions.each_with_object({}) { |(k, v), h| h[k.downcase] = v }
facts = prefer_memory? ? normalized.merge(calculator.memory) : calculator.memory.merge(normalized)

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

value = evaluate!(
facts[var_name.downcase],
facts.merge(results),
&expression_with_exception_handler(&block)
)

results[var_name] = value

rescue UnboundVariableError, Dentaku::ZeroDivisionError => ex
ex.recipient_variable = var_name
results[var_name] = block.call(ex)

rescue Dentaku::ArgumentError => ex
results[var_name] = block.call(ex)

end

rescue TSort::Cyclic => ex
block.call(ex)
{}
Expand Down
8 changes: 4 additions & 4 deletions lib/dentaku/calculator.rb
Expand Up @@ -68,12 +68,12 @@ def evaluate!(expression, data = {}, &block)
end
end

def solve!(expression_hash)
BulkExpressionSolver.new(expression_hash, self).solve!
def solve!(expression_hash, precedence = :memory)
BulkExpressionSolver.new(expression_hash, self, precedence).solve!
end

def solve(expression_hash, &block)
BulkExpressionSolver.new(expression_hash, self).solve(&block)
def solve(expression_hash, precedence = :memory, &block)
BulkExpressionSolver.new(expression_hash, self, precedence).solve(&block)
end

def dependencies(expression, context = {})
Expand Down
11 changes: 11 additions & 0 deletions spec/calculator_spec.rb
Expand Up @@ -197,6 +197,17 @@
)).to eq(pear: 1, weekly_apple_budget: 21, weekly_fruit_budget: 25)
end

it "allows preferring variables over existing values in memory" do
expect(
with_memory.solve!({
weekly_fruit_budget: "weekly_apple_budget + pear * 4",
weekly_apple_budget: "apples * 7",
pear: "1",
apples: "4"
}, :variables)
).to eq(apples: 4, pear: 1, weekly_apple_budget: 28, weekly_fruit_budget: 32)
end

it "preserves hash keys" do
expect(calculator.solve!(
'meaning_of_life' => 'age + kids',
Expand Down

0 comments on commit 74121f4

Please sign in to comment.