module Autodeps

Constants

VERSION

Attributes

active[RW]
logger[RW]

Public Class Methods

active=(active) click to toggle source
# File lib/autodeps/autodeps.rb, line 195
def active= (active)
  Thread.current["Autodeps::active"] = active
end
add_pending_computation(pending_computation) click to toggle source
# File lib/autodeps/autodeps.rb, line 14
def add_pending_computation(pending_computation)
  @pending_computations << pending_computation
end
afterFlush(&f) click to toggle source
# File lib/autodeps/autodeps.rb, line 92
def afterFlush(&f)
  @after_flush_callbacks.push(f);
end
autorun(&block) click to toggle source
# File lib/autodeps/autodeps.rb, line 18
def autorun(&block)
  raise 'Autodeps.autorun requires a block' if block.nil?

  @constructingComputation = true
  c = Computation.new(block, Autodeps.current_computation);


  if (Autodeps.active)
    Autodeps.on_invalidate do
      c.stop();
    end
  end

  return c
end
current_computation() click to toggle source
# File lib/autodeps/autodeps.rb, line 199
def current_computation
  Thread.current["Autodeps::current_computation"]
end
current_computation=(computation) click to toggle source
# File lib/autodeps/autodeps.rb, line 202
def current_computation= (computation)
  Thread.current["Autodeps::current_computation"] = computation
  self.active = !! computation;
end
embox(equals=nil, &func) click to toggle source
# File lib/autodeps/autodeps.rb, line 118
def embox(equals=nil, &func)
  raise "must define a block in embox" unless func


  curResult = nil;
  #There's one shared Dependency and Computation for all callers of

  # our box function.  It gets kicked off if necessary, and when
  # there are no more dependents, it gets stopped to avoid leaking
  # memory.
  resultDep = nil;
  computation = nil;

  return proc do
    if (!computation)
      if (!Autodeps.active)
        # Not in a reactive context.  Just call func, and don't start a
        # computation if there isn't one running already.
        break func.call();
      end

      # No running computation, so kick one off.  Since this computation
      # will be shared, avoid any association with the current computation
      # by using `Deps.nonreactive`.
      resultDep = Autodeps::Dependency.new;

      computation = Autodeps.nonreactive do
        break Autodeps.autorun do |c|
          oldResult = curResult;
          curResult = func.call();
          if (!c.first_run)
            if (!(equals ? equals.call(curResult, oldResult) :
                curResult == oldResult))
              resultDep.changed();
            end
          end
        end
      end
    end

    if (Autodeps.active)
      is_new = resultDep.depend();
      if (is_new)
        # For each new dependent, schedule a task for after that dependents
        # invalidation time and the subsequent flush. The task checks
        # whether the computation should be torn down.
        Autodeps.on_invalidate do
          if (resultDep && !resultDep.hasDependents())
            Autodeps.afterFlush do
              # use a second afterFlush to bump ourselves to the END of the
              # flush, after computation re-runs have had a chance to
              # re-establish their connections to our computation.
              Autodeps.afterFlush do
                if (resultDep && !resultDep.hasDependents())
                  computation.stop();
                  computation = nil;
                  resultDep = nil;
                end
              end
            end
          end
        end
      end
    end

    curResult
  end
end
embox_value(value, equals=nil) click to toggle source
# File lib/autodeps/autodeps.rb, line 187
def embox_value(value, equals=nil)
   raise "embox_value on a direct value not implemented"
end
flush() click to toggle source
# File lib/autodeps/autodeps.rb, line 44
  def flush

    #if (inFlush)
    #  throw new Error("Can't call Deps.flush while flushing");
    #
    #  if (inCompute)
    #    throw new Error("Can't flush inside Deps.autorun");

    @inFlush = true
    @willFlush = true


    while (@pending_computations.length > 0 ||
        @after_flush_callbacks.length > 0) do

        #recompute all pending computations
      comps = @pending_computations;
      @pending_computations = [];

      comps.each do |comp|
        comp.recompute();

      #if (afterFlushCallbacks.length) {
      #    // call one afterFlush callback, which may
      #// invalidate more computations
      #var func = afterFlushCallbacks.shift();
      #try {
      #  func();
      #} catch (e) {
      #    _debugFunc()("Exception from Deps afterFlush function:",
      #    e.stack || e.message);
      #}
      end
    end

    inFlush = false;
    willFlush = false;

  end

  def on_invalidate(&f)
    if (! Autodeps.active)
      raise "AutoDeps.on_invalidate requires a currentComputation"
    end

      Autodeps.current_computation.on_invalidate(f);
  end

  def afterFlush(&f)
    @after_flush_callbacks.push(f);
  end

  def isolateValue(equals=nil, &f)
    raise "must define a block in isolateValue" unless f
    if (!Autodeps.active)
      return f.call();
    end

    result_dep = Autodeps::Dependency.new;
    orig_result = nil
    Autodeps.autorun do |c|
      result = f.call();
      if (c.first_run)
        orig_result = result;
      elsif (!(equals ? equals(result, orig_result) :
          result == orig_result))
        result_dep.changed();
      end
    end
    result_dep.depend();

    return orig_result;
  end

  def embox(equals=nil, &func)
    raise "must define a block in embox" unless func


    curResult = nil;
    #There's one shared Dependency and Computation for all callers of

    # our box function.  It gets kicked off if necessary, and when
    # there are no more dependents, it gets stopped to avoid leaking
    # memory.
    resultDep = nil;
    computation = nil;

    return proc do
      if (!computation)
        if (!Autodeps.active)
          # Not in a reactive context.  Just call func, and don't start a
          # computation if there isn't one running already.
          break func.call();
        end

        # No running computation, so kick one off.  Since this computation
        # will be shared, avoid any association with the current computation
        # by using `Deps.nonreactive`.
        resultDep = Autodeps::Dependency.new;

        computation = Autodeps.nonreactive do
          break Autodeps.autorun do |c|
            oldResult = curResult;
            curResult = func.call();
            if (!c.first_run)
              if (!(equals ? equals.call(curResult, oldResult) :
                  curResult == oldResult))
                resultDep.changed();
              end
            end
          end
        end
      end

      if (Autodeps.active)
        is_new = resultDep.depend();
        if (is_new)
          # For each new dependent, schedule a task for after that dependents
          # invalidation time and the subsequent flush. The task checks
          # whether the computation should be torn down.
          Autodeps.on_invalidate do
            if (resultDep && !resultDep.hasDependents())
              Autodeps.afterFlush do
                # use a second afterFlush to bump ourselves to the END of the
                # flush, after computation re-runs have had a chance to
                # re-establish their connections to our computation.
                Autodeps.afterFlush do
                  if (resultDep && !resultDep.hasDependents())
                    computation.stop();
                    computation = nil;
                    resultDep = nil;
                  end
                end
              end
            end
          end
        end
      end

      curResult
    end
  end

  def embox_value(value, equals=nil)
     raise "embox_value on a direct value not implemented"
  end

  def active
    Thread.current["Autodeps::active"]
  end

  def active= (active)
    Thread.current["Autodeps::active"] = active
  end

  def current_computation
    Thread.current["Autodeps::current_computation"]
  end
  def current_computation= (computation)
    Thread.current["Autodeps::current_computation"] = computation
    self.active = !! computation;
  end
end
isolateValue(equals=nil, &f) click to toggle source
# File lib/autodeps/autodeps.rb, line 96
def isolateValue(equals=nil, &f)
  raise "must define a block in isolateValue" unless f
  if (!Autodeps.active)
    return f.call();
  end

  result_dep = Autodeps::Dependency.new;
  orig_result = nil
  Autodeps.autorun do |c|
    result = f.call();
    if (c.first_run)
      orig_result = result;
    elsif (!(equals ? equals(result, orig_result) :
        result == orig_result))
      result_dep.changed();
    end
  end
  result_dep.depend();

  return orig_result;
end
nonreactive(&f) click to toggle source
# File lib/autodeps/autodeps.rb, line 34
def nonreactive(&f)
  previous = self.current_computation;
  self.current_computation = nil;
  begin
    f.call()
  ensure
    self.current_computation = previous;
  end
end
on_invalidate(&f) click to toggle source
# File lib/autodeps/autodeps.rb, line 84
def on_invalidate(&f)
  if (! Autodeps.active)
    raise "AutoDeps.on_invalidate requires a currentComputation"
  end

    Autodeps.current_computation.on_invalidate(f);
end