module Actor
eigenmodule
def new
case function(:loop).arity
when 0
spawn { self::loop }
when 1
spawn { self::loop(self::initial_state) }
else
spawn { self::loop(*self::initial_state) }
end
end
end
end

# Actor.new produces a basic actor that endlessly loops,
# but a new definition can always be assigned to the actor's eigenmodule
def loop
loop
end

# for convenience, default implementation returns empty hash
def initial_state
{}
end

def call(actor, name, *args)
cast(actor, name, *args)
receive
reply
end
end
end

module Server
eigenmodule
set_callable? = true
set_castable? = false
callable_functions = Set.new
castable_functions = Set.new

def public
callable
castable
end

def private
uncallable
uncastable
end

def callable
set_callable = true
end

def callable(name)
callable_functions.add(name)
end

def uncallable
set_callable = false
end

def castable
set_castable = true
end

def castable(name)
castable_functions.add(name)
end

def uncastable
set_castable = false
end

def function_added(name, function)
callable_functions.add(name) if set_callable?
castable_functions.add(name) if set_castable?
end
end

def loop(state)
receive
sender:call(name, *arguments)
handle(state, :call, sender, name, arguments)
end
sender:cast(name, *arguments)
handle(state, :cast, sender, name, arguments)
end
end
end

def handle(state, invocation_type, sender, name, arguments)
fn = function(name)
sender:error("No function named #{name}.") unless fn
sender:error("Function #{name} not #{type}able") unless able?(name, invocation_type)

case invocation_type
when :call
[result, state] = fn(state, *arguments)
sender:reply(result)
when :cast
fn(state, *arguments)
end
loop(state)
end

def function(name)
module.function(name)
end

def able?(name, invocation_type)
case invocation_type
when :call
module.callable_functions.has?(name)
when :cast
module.castable_functions.has?(name)
end
end
end