class Computable

Constants

Unknown

This is a special value to mark a variable to be computed.

VERSION

Public Class Methods

calc_value(name, format=nil, freeze: true, &block) click to toggle source
# File lib/computable.rb, line 285
def self.calc_value name, format=nil, freeze: true, &block
  calc_method_id = "calc_#{name}".intern
  define_method(calc_method_id, &block)

  calc_method2_id = "calc_#{name}_with_tracking".intern
  define_method(calc_method2_id) do |v|
    old_caller = Thread.current.thread_variable_get("Computable #{object_id}")
    Thread.current.thread_variable_set("Computable #{self.object_id}", v)
    begin
      puts "do calc #{v.inspect}" if @computable_debug
      begin
        res = send(calc_method_id)
      rescue Exception => err
        improve_backtrace(err, block, name)
      end
      Computable.verify_format(name, res, format)
      res.freeze if freeze
      res
    ensure
      Thread.current.thread_variable_set("Computable #{self.object_id}", old_caller)
    end
  end

  define_method("#{name}=") do |value|
    Computable.verify_format(name, value, format)
    @computable_mutex.synchronize do
      v = @computable_variables[name]
      puts "set #{name}: #{value.inspect} #{v.inspect}" if @computable_debug
      v = @computable_variables[name] = Variable.new(name, method(calc_method2_id), self, @computable_mutex) unless v

      value.freeze if freeze
      v.assign_value(value)
    end
  end

  define_method(name) do
    @computable_mutex.synchronize do
      v = @computable_variables[name]
      puts "called #{name} #{v.inspect}" if @computable_debug
      v = @computable_variables[name] = Variable.new(name, method(calc_method2_id), self, @computable_mutex) unless v

      kaller = Thread.current.thread_variable_get("Computable #{object_id}")
      v.query_value(kaller)
    end
  end
end
input_value(name, format=nil, **kwargs) click to toggle source
# File lib/computable.rb, line 332
def self.input_value name, format=nil, **kwargs
  calc_value name, format, **kwargs do
    raise UndefinedValue, "input variable '#{name}' is not assigned"
  end
end
new() click to toggle source
# File lib/computable.rb, line 261
def initialize
  @computable_debug = false
  @computable_max_threads = 0
  @computable_variables = {}
  @computable_caller = nil
  @computable_mutex = Mutex.new
end
verify_format(name, value, format) click to toggle source
# File lib/computable.rb, line 270
def self.verify_format(name, value, format)
  if format && !(Unknown==value) && !(format === value)
    raise InvalidFormat, "variable '#{name}': value #{value.inspect} is not in format #{format.inspect}"
  end
end

Public Instance Methods

computable_debug() click to toggle source
# File lib/computable.rb, line 222
def computable_debug
  @computable_debug
end
computable_debug=(v) click to toggle source
# File lib/computable.rb, line 219
def computable_debug=(v)
  @computable_debug = v
end
computable_display_dot(**kwargs) click to toggle source
# File lib/computable.rb, line 233
def computable_display_dot(**kwargs)
  IO.popen("dot -Tpng | display -", "w") do |fd|
    fd.puts computable_to_dot(**kwargs)
  end
end
computable_max_threads() click to toggle source
# File lib/computable.rb, line 229
def computable_max_threads
  @computable_max_threads
end
computable_max_threads=(v) click to toggle source
# File lib/computable.rb, line 226
def computable_max_threads=(v)
  @computable_max_threads = v
end
computable_to_dot(rankdir: "TB", multiline: true) click to toggle source
# File lib/computable.rb, line 239
def computable_to_dot(rankdir: "TB", multiline: true)
  dot = "digraph #{self.class.name.inspect} {\n"
  dot << "graph [ dpi = 45, rankdir=#{rankdir} ];\n"
  @computable_variables.each do |name, v|
    col = case
      when !v.value_calced then "color = red,"
      when !v.used_for.empty? then "color = green,"
      else "color = blue,"
    end
    label = if multiline
      "#{name.to_s.gsub("_","\n")}\n(#{v.count})"
    else
      "#{name.to_s} (#{v.count})"
    end
    dot << "#{name.to_s.inspect} [#{col} label=#{label.inspect}];\n"
    v.used_for.each do |name2, v2|
      dot << "#{name.to_s.inspect} -> #{name2.to_s.inspect};\n"
    end
  end
  dot << "}\n"
end

Private Instance Methods

improve_backtrace(err, block, text) click to toggle source
# File lib/computable.rb, line 276
        def improve_backtrace(err, block, text)
  fpath, lineno = block.source_location
  bt = err.backtrace
  myloc = err.backtrace_locations.select.with_index{|loc, i| loc.path == fpath && loc.lineno >= lineno && !bt[i].include?("#") }.min{|a,b| a.lineno <=> b.lineno }
  idx = err.backtrace_locations.index(myloc)
  bt[idx] += " ##{text}"
  raise err
end