Skip to content
This repository
Fetching contributors…

Cannot retrieve contributors at this time

file 149 lines (118 sloc) 3.884 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
class ProxyReceiver : Fancy BasicObject {
  """
A ProxyReceiver is an object which proxies all message sends to it to 2 other objects.
It will send each message first to its @proxy instance variable and then to the @obj instance variable.
"""

  def initialize: @proxy for: @obj {
    """
@proxy Proxy receiver for @obj.
@obj Original receiver object.

Initializes a new ProxyReceiver with @proxy for @obj.
"""

    self
  }

  def unknown_message: msg with_params: params {
    """
@msg Incoming message name.
@params Paremeters of incoming message send.

Forwards all incoming messages to @self to @@proxy and then @@obj.
"""

    @proxy receive_message: msg with_params: params
    @obj receive_message: msg with_params: params
  }
}

Proxy = ProxyReceiver

class RespondsToProxy : Fancy BasicObject {
  """
A RespondsToProxy is a Proxy that forwards any message sent to it to it's @target instance variable
only if it responds to that message. Any messages that @target doesn't respond to simply won't be sent
and @nil will be returned.
"""

  def initialize: @target {
    """
@target Target receiver object.

Initializes a new RespondsToProxy for @target.
"""

    self
  }

  def unknown_message: msg with_params: params {
    """
@msg Incoming message name.
@params Paremeters of incoming message send.

Forwards all incoming message to @self to @@target
only if @@target responds to them.
"""

    if: (@target responds_to?: msg) then: {
      @target receive_message: msg with_params: params
    }
  }
}

class ActorProxy : Fancy BasicObject {
  """
An ActorProxy is a Proxy that forwards any message sent to it to
it's @target object as a future send by default. If explicitly sent
an async message, it will forward the async send to @target,
returning @nil instead of a @FutureSend@, as expected.

Example:
ap = ActorProxy new: target_actor

# this:
f = ap some_future_send: an_arg
# is the same as:
f = target_actor @ some_future_send: an_arg

# and this:
ap @@ some_async_send: another_arg
# is the same as:
target_actor @@ some_async_send: another_arg
"""

  def initialize: @target

  def send_future: m with_params: p {
    @target send_future: m with_params: p
  }

  def send_async: m with_params: p {
    @target send_async: m with_params: p
  }

  def unknown_message: m with_params: p {
    @target send_future: m with_params: p
  }
}

class DistributingProxy : Fancy BasicObject {
  """
DistributingProxy is a Proxy that round-robin distributes messages to objects
in a @Fancy::Enumerable@ specified upon creation.

Example:
p = DistributingProxy new: [worker1, worker2, worker3, worker4]
loop: {
req = @input receive_request
p handle_request: req # will be forwarded to worker1-4
}
"""

  def initialize {
    ArgumentError raise: "Missing list of proxy targets"
  }

  def initialize: @targets {
    @free = @targets to_a dup
    @mutex = Mutex new
    @waiting = ConditionVariable new
  }

  def __with_target__: block {
    t = @mutex synchronize: {
      { @waiting wait: @mutex } until: { @free empty? not }
      @free shift
    }
    val = block call: [t]
    @mutex synchronize: { @free << t; @waiting broadcast }
    val
  }

  instance_methods reject: /^(:initialize|initialize:|__with_target__:|unknown_message:with_params:|send_async:with_params:|send_future:with_params:|__send__)$/ . each: |m| {
    undef_method(m)
  }

  def unknown_message: m with_params: p {
    __with_target__: @{ receive_message: m with_params: p }
  }

  def send_async: m with_params: p {
    __with_target__: @{ send_async: m with_params: p }
  }

  def send_future: m with_params: p {
    __with_target__: @{ send_future: m with_params: p }
  }
}
Something went wrong with that request. Please try again.