class Alki::Executor

Attributes

meta[RW]
root[RW]

Public Class Methods

new(instance) click to toggle source
# File lib/alki/executor.rb, line 11
def initialize(instance)
  @semaphore = Concurrent::ReentrantReadWriteLock.new
  @lookup_cache = {}
  @call_cache = {}
  @context_cache = {}
  @data = nil
  @instance = instance
end

Public Instance Methods

call(path,*args,&blk) click to toggle source
# File lib/alki/executor.rb, line 26
def call(path,*args,&blk)
  execute({},path,args,blk)
end
canonical_path(from,path) click to toggle source
# File lib/alki/executor.rb, line 45
def canonical_path(from,path)
  from_elem = lookup(from)
  scope = from_elem[:scope]
  path.inject(nil) do |p,elem|
    scope = lookup(p)[:scope] if p
    scope[elem]
  end
end
execute(meta,path,args,blk) click to toggle source
# File lib/alki/executor.rb, line 54
def execute(meta,path,args,blk)
  type,value = nil,nil
  @semaphore.with_read_lock do
    cache_entry = @call_cache[path]
    if cache_entry
      if cache_entry == :building
        raise Alki::CircularReferenceError.new
      end
      type,value = cache_entry.type,cache_entry.value
    else
      @semaphore.with_write_lock do
        @call_cache[path] = :building
        type, value = build(path)
        @call_cache[path] = Alki::Execution::CacheEntry.finished type, value
      end
    end
  end
  call_value(type, value, meta, args, blk)
rescue Alki::CircularReferenceError => e
  e.chain << path
  raise
end
lock() { || ... } click to toggle source
# File lib/alki/executor.rb, line 20
def lock
  @semaphore.with_write_lock do
    yield
  end
end
lookup(path) click to toggle source
# File lib/alki/executor.rb, line 30
def lookup(path)
  @semaphore.with_read_lock do
    unless @lookup_cache[path]
      @semaphore.with_write_lock do
        @lookup_cache[path] = lookup_elem(path).tap do |elem|
          unless elem
            raise InvalidPathError.new("Invalid path #{path.inspect}")
          end
        end
      end
    end
    @lookup_cache[path]
  end
end

Private Instance Methods

build(path) click to toggle source
# File lib/alki/executor.rb, line 79
def build(path)
  action = lookup(path)
  if action[:build]
    build_meta = {building: path.join('.')}
    build_meta.merge!(action[:meta]) if action[:meta]
    build_action = action[:build].merge(scope: action[:scope], modules: action[:modules])
    call_value(*process_action(build_action), build_meta, [action])
  end
  process_action action
end
call_value(type,value,meta,args=[],blk=nil) click to toggle source
# File lib/alki/executor.rb, line 123
def call_value(type,value,meta,args=[],blk=nil)
  case type
    when :value then value
    when :proc then proc.call *args, &blk
    when :class then value.new(@instance,meta).__call__ *args, &blk
  end
end
context_class(action) click to toggle source
# File lib/alki/executor.rb, line 131
def context_class(action)
  desc = {
    scope: action[:scope],
    body: action[:proc],
    modules: action[:modules],
    methods: action[:methods]
  }
  @context_cache[desc] ||= Alki::Execution::ContextClassBuilder.build(desc)
end
data_copy() click to toggle source
# File lib/alki/executor.rb, line 90
def data_copy
  unless @data
    @data = {}
    @meta.each do |from, meta|
      meta.process self, from, @data
    end
    IceNine.deep_freeze @data
  end
  @data.dup
end
lookup_elem(path) click to toggle source
# File lib/alki/executor.rb, line 101
def lookup_elem(path)
  data = data_copy
  elem = @root
  path.each do |key|
    elem = elem.index data, key
    return nil unless elem
  end
  elem.output data
end
process_action(action) click to toggle source
# File lib/alki/executor.rb, line 111
def process_action(action)
  if action.key?(:value)
    [:value,action[:value]]
  elsif action[:proc]
    if action[:scope]
      [:class,context_class(action)]
    else
      [:proc,action[:proc]]
    end
  end or raise "Invalid action"
end