jdantonio/concurrent-ruby — pull request #48 by jdantonio (2014-04-07 01:07:08+00:00)
Title: Initial implementation of ActorContext and the #spawn function

This PR implements the simplest case of the new actor framework as discussed in issue #36. It deprecates the previous `Actor` base class and replaces it with the `ActorContext` mixin module. The only implemented factory method is `spawn` which creates a single instance of the actor class, wraps it in an `ActorRef` object, and restarts the actor thread should it crash.

Creating an actor is done simply by including `ActorContext` and implementing a `#receive` method that takes with the signature `def receive(*msg)`. It receives a message of zero or more arguments and returns the result of message processing.

Actors which mix in the `ActorContext` module have a new lifecycle: `on_start`, `on_reset`, and `on_shutdown`. The first and the last are each called once. The `on_reset` method is called every time the actor is reset. Actor resetting is a larger topic which will eventually be written about in the wiki. The lifecycle that triggers `on_start` is controlled by several factory options including `:abort_on_exception`, `:reset_on_error`, and `:rescue_exception`.

This implementation reduces the number of message passing methods from four down to two: `post` and `post!`. The `forward` method has been deprecated and will not likely return. The original `post` method, which returned a boolean value, has been deprecated. It was based on a Scala method which returned `void`. Ruby does not have void functions so there was no value in having both `post` and `post?` (the latter of which returned a future).

The `post` method now always returns a future object (specifically, an `IVar`). Additionally the `post` method can be given a block which will act as a callback on message completion. The block parameters are the same as for observation. The difference is that observers are notified every time a message is processed whereas the `post` callback only applies to the given message.

The `post!` method works the way it did in the previous `Actor` implementation. It blocks for the given number of seconds and bubbles raised errors.

An example:

```ruby
require 'concurrent'

class EchoActor
include Concurrent::ActorContext

def receive(*msg)
print "Received message #{msg}\n"
msg
end
end

ref = EchoActor.spawn #=> # # Received message [:foo, :bar]

# this method provides a callback
ref.post(:hello, :world) do |time, msg, value, reason|
print "Callback called at #{time} with #{value} as the value.\n"
end
#> Received message [:hello, :world]
#> Callback called at 2014-04-06 21:05:30 -0400 with as the value.

# this method blocks for 1 second
ref.post!(1, :pow, :zap, :boom) #=> [:pow, :zap, :boom]
#> Received message [:pow, :zap, :boom]
```