Fuel plugin to deploy Sensu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bulk_expression_solver.rb 2.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. require 'dentaku/calculator'
  2. require 'dentaku/dependency_resolver'
  3. require 'dentaku/exceptions'
  4. require 'dentaku/parser'
  5. require 'dentaku/tokenizer'
  6. module Dentaku
  7. class BulkExpressionSolver
  8. def initialize(expression_hash, calculator)
  9. self.expression_hash = expression_hash
  10. self.calculator = calculator
  11. end
  12. def solve!
  13. solve(&raise_exception_handler)
  14. end
  15. def solve(&block)
  16. error_handler = block || return_undefined_handler
  17. results = load_results(&error_handler)
  18. expression_hash.each_with_object({}) do |(k, _), r|
  19. r[k] = results[k.to_s]
  20. end
  21. end
  22. private
  23. def self.dependency_cache
  24. @dep_cache ||= {}
  25. end
  26. attr_accessor :expression_hash, :calculator
  27. def return_undefined_handler
  28. ->(*) { :undefined }
  29. end
  30. def raise_exception_handler
  31. ->(ex) { raise ex }
  32. end
  33. def load_results(&block)
  34. variables_in_resolve_order.each_with_object({}) do |var_name, r|
  35. begin
  36. value_from_memory = calculator.memory[var_name]
  37. if value_from_memory.nil? &&
  38. expressions[var_name].nil? &&
  39. !calculator.memory.has_key?(var_name)
  40. next
  41. end
  42. value = value_from_memory ||
  43. evaluate!(expressions[var_name], expressions.merge(r))
  44. r[var_name] = value
  45. rescue Dentaku::UnboundVariableError, ZeroDivisionError => ex
  46. ex.recipient_variable = var_name
  47. r[var_name] = block.call(ex)
  48. end
  49. end
  50. end
  51. def expressions
  52. @expressions ||= Hash[expression_hash.map { |k,v| [k.to_s, v] }]
  53. end
  54. def expression_dependencies
  55. Hash[expressions.map { |var, expr| [var, calculator.dependencies(expr)] }].tap do |d|
  56. d.values.each do |deps|
  57. unresolved = deps.reject { |ud| d.has_key?(ud) }
  58. unresolved.each { |u| add_dependencies(d, u) }
  59. end
  60. end
  61. end
  62. def add_dependencies(current_dependencies, variable)
  63. node = calculator.memory[variable]
  64. if node.respond_to?(:dependencies)
  65. current_dependencies[variable] = node.dependencies
  66. node.dependencies.each { |d| add_dependencies(current_dependencies, d) }
  67. end
  68. end
  69. def variables_in_resolve_order
  70. cache_key = expressions.keys.map(&:to_s).sort.join("|")
  71. @ordered_deps ||= self.class.dependency_cache.fetch(cache_key) {
  72. DependencyResolver.find_resolve_order(expression_dependencies).tap do |d|
  73. self.class.dependency_cache[cache_key] = d if Dentaku.cache_dependency_order?
  74. end
  75. }
  76. end
  77. def evaluate!(expression, results)
  78. calculator.evaluate!(expression, results)
  79. end
  80. end
  81. end