Report abuse

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