README.md in celluloid-0.1.0 vs README.md in celluloid-0.2.0
- old
+ new
@@ -1,7 +1,8 @@
Celluloid
=========
+[![Build Status](http://travis-ci.org/tarcieri/celluloid.png)](http://travis-ci.org/tarcieri/celluloid)
> "I thought of objects being like biological cells and/or individual
> computers on a network, only able to communicate with messages"
> _--Alan Kay, creator of Smalltalk, on the meaning of "object oriented programming"_
@@ -25,14 +26,14 @@
Celluloid works on Rubinius in either 1.8 or 1.9 mode.
Usage
-----
-To use Celluloid, define a normal Ruby class that includes Celluloid::Actor
+To use Celluloid, define a normal Ruby class that includes Celluloid:
class Sheen
- include Celluloid::Actor
+ include Celluloid
def initialize(name)
@name = name
end
@@ -43,28 +44,27 @@
def report
"#{@name} is #{@status}"
end
end
-If you call Sheen.new, you'll wind up with a plain old Ruby object. To
-create a concurrent object instead of a regular one, use Sheen.spawn:
+Now when you create new instances of this class, they're actually concurrent
+objects, each running in their own thread:
- >> charlie = Sheen.spawn "Charlie Sheen"
+ >> charlie = Sheen.new "Charlie Sheen"
=> #<Celluloid::Actor(Sheen:0x00000100a312d0) @name="Charlie Sheen">
>> charlie.set_status "winning!"
=> "winning!"
>> charlie.report
=> "Charlie Sheen is winning!"
>> charlie.set_status! "asynchronously winning!"
=> nil
>> charlie.report
=> "Charlie Sheen is asynchronously winning!"
-Calling spawn creates a concurrent object running inside its own thread. You
-can call methods on this concurrent object just like you would any other Ruby
-object. The Sheen#set_status method works exactly like you'd expect, returning
-the last expression evaluated.
+You can call methods on this concurrent object just like you would any other
+Ruby object. The Sheen#set_status method works exactly like you'd expect,
+returning the last expression evaluated.
However, Celluloid's secret sauce kicks in when you call banged predicate
methods (i.e. methods ending in !). Even though the Sheen class has no
set_status! method, you can still call it. Why is this? Because bang methods
have a special meaning in Celluloid. (Note: this also means you can't define
@@ -87,10 +87,42 @@
crashed object will not raise an error.
However, you can still handle errors created by asynchronous calls using
two features of Celluloid called _supervisors_ and _linking_.
+Futures
+-------
+
+Futures allow you to request a computation and get the result later. There are
+two types of futures supported by Celluloid: method futures and block futures.
+Method futures work by invoking the _future_ method on an actor. This method
+is analogous to the typical _send_ method in that it takes a method name,
+followed by an arbitrary number of arguments, and a block. Let's invoke the
+report method from the charlie object used in the above example using a future:
+
+ >> future = charlie.future :report
+ => #<Celluloid::Future:0x000001009759b8>
+ >> future.value
+ => "Charlie Sheen is winning!"
+
+The call to charlie.future immediately returns a Celluloid::Future object,
+regardless of how long it takes to execute the "report" method. To obtain
+the result of the call to "report", we call the _value_ method of the
+future object. This call will block until the method call is available.
+
+Futures also allow you to background the computation of any block:
+
+ >> future = Celluloid::Future.new { 2 + 2 }
+ => #<Celluloid::Future:0x000001008425f0>
+ >> future.value
+ => 4
+
+One thing to be aware of when using futures: always make sure to obtain the
+value of any future you make. Futures create a thread in the background which
+will continue to run until the future's value is obtained. Failing to obtain
+the value of futures you create will leak threads.
+
Supervisors
-----------
You may be familiar with tools like Monit or God which keep an eye on your
applications and restart them when they crash. Celluloid supervisors work in
@@ -104,11 +136,11 @@
>> supervisor = Sheen.supervise "Charlie Sheen"
=> #<Celluloid::Supervisor(Sheen) "Charlie Sheen">
This created a new Celluloid::Supervisor actor, and also created a new Sheen
actor, giving its initialize method the argument "Charlie Sheen". The
-_supervise_ method has the same method signature as _spawn_. However, rather
+_supervise_ method has the same method signature as _new_. However, rather
than returning the newly created actor, _supervise_ returns the supervisor.
To retrieve the actor that the supervisor is currently using, use the
Celluloid::Supervisor#actor method:
>> supervisor = Sheen.supervise "Charlie Sheen"
@@ -126,32 +158,32 @@
In this case, the supervisor will ensure that an actor of the Sheen class,
created using the given arguments, is aways available by calling
Celluloid::Actor[:charlie]. The first argument to supervise_as is the name
you'd like the newly created actor to be registered under. The remaining
-arguments are passed to initialize just like you called _new_ or _spawn_.
+arguments are passed to initialize just like you called _new_.
See the "Registry" section below for more information on the actor registry
Linking
-------
Whenever any unhandled exceptions occur in any of the methods of an actor,
that actor crashes and dies. Let's start with an example:
class JamesDean
- include Celluloid::Actor
+ include Celluloid
class CarInMyLaneError < StandardError; end
def drive_little_bastard
raise CarInMyLaneError, "that guy's gotta stop. he'll see us"
end
end
Now, let's have James drive Little Bastard and see what happens:
- >> james = JamesDean.spawn
+ >> james = JamesDean.new
=> #<Celluloid::Actor(JamesDean:0x1068)>
>> james.drive_little_bastard!
=> nil
>> james
=> #<Celluloid::Actor(JamesDean:0x1068) dead>
@@ -164,11 +196,11 @@
crash notifications from. In order to receive these events, we need to use the
trap_exit method to be notified of them. Let's look at how a hypothetical
Elizabeth Taylor object could be notified that James Dean has crashed:
class ElizabethTaylor
- include Celluloid::Actor
+ include Celluloid
trap_exit :actor_died
def actor_died(actor, reason)
puts "Oh no! #{actor.inspect} has died because of a #{reason.class}"
end
@@ -176,13 +208,13 @@
We've now used the trap_exit method to configure a callback which is invoked
whenever any linked actors crashed. Now we need to link Elizabeth to James so
James' crash notifications get sent to her:
- >> james = JamesDean.spawn
+ >> james = JamesDean.new
=> #<Celluloid::Actor(JamesDean:0x11b8)>
- >> elizabeth = ElizabethTaylor.spawn
+ >> elizabeth = ElizabethTaylor.new
=> #<Celluloid::Actor(ElizabethTaylor:0x11f0)>
>> elizabeth.link james
=> #<Celluloid::Actor(JamesDean:0x11b8)>
>> james.drive_little_bastard!
=> nil
@@ -195,42 +227,42 @@
her to take action (in this case, printing the error). But what would happen
if she weren't trapping exits? Let's break James apart into two separate
objects, one for James himself and one for Little Bastard, his car:
class PorscheSpider
- include Celluloid::Actor
+ include Celluloid
class CarInMyLaneError < StandardError; end
def drive_on_route_466
raise CarInMyLaneError, "head on collision :("
end
end
class JamesDean
- include Celluloid::Actor
+ include Celluloid
def initialize
- @little_bastard = PorscheSpider.spawn_link
+ @little_bastard = PorscheSpider.new_link
end
def drive_little_bastard
@little_bastard.drive_on_route_466
end
end
If you take a look in JamesDean#initialize, you'll notice that to create an
-instance of PorcheSpider, James is calling the spawn_link method.
+instance of PorcheSpider, James is calling the new_link method.
-This method works similarly to _spawn_, except it combines _spawn_ and _link_
+This method works similarly to _new_, except it combines _new_ and _link_
into a single call.
Now what happens if we repeat the same scenario with Elizabeth Taylor watching
for James Dean's crash?
- >> james = JamesDean.spawn
+ >> james = JamesDean.new
=> #<Celluloid::Actor(JamesDean:0x1108) @little_bastard=#<Celluloid::Actor(PorscheSpider:0x10ec)>>
- >> elizabeth = ElizabethTaylor.spawn
+ >> elizabeth = ElizabethTaylor.new
=> #<Celluloid::Actor(ElizabethTaylor:0x1144)>
>> elizabeth.link james
=> #<Celluloid::Actor(JamesDean:0x1108) @little_bastard=#<Celluloid::Actor(PorscheSpider:0x10ec)>>
>> james.drive_little_bastard!
=> nil
@@ -248,11 +280,11 @@
This allows you to factor your problem into several actors. If an error occurs
in any of them, it will kill off all actors used in a particular system. In
general, you'll probably want to have a supervisor start a single actor which
is in charge of a particular part of your system, and have that actor
-spawn_link to other actors which are part of the same system. If any error
+new_link to other actors which are part of the same system. If any error
occurs in any of these actors, all of them will be killed off and the entire
subsystem will be restarted by the supervisor in a clean state.
If, for any reason, you've linked to an actor and want to sever the link,
there's a corresponding _unlink_ method to remove links between actors.
@@ -261,11 +293,11 @@
--------
Celluloid lets you register actors so you can refer to them symbolically.
You can register Actors using Celluloid::Actor[]:
- >> james = JamesDean.spawn
+ >> james = JamesDean.new
=> #<Celluloid::Actor(JamesDean:0x80c27ce0)>
>> Celluloid::Actor[:james] = james
=> #<Celluloid::Actor(JamesDean:0x80c27ce0)>
>> Celluloid::Actor[:james]
=> #<Celluloid::Actor(JamesDean:0x80c27ce0)>
@@ -278,10 +310,45 @@
working, freshly-restarted version.
The main use of the registry is for interfacing with actors that are
automatically restarted by supervisors when they crash.
+Signaling
+---------
+
+Signaling is an advanced technique similar to condition variables in typical
+multithreaded programming. One method within a concurrent object can suspend
+itself waiting for a particular event, allowing other methods to run. Another
+method can then signal all methods waiting for a particular event, and even
+send them a value in the process:
+
+ class SignalingExample
+ include Celluloid
+ attr_reader :signaled
+
+ def initialize
+ @signaled = false
+ end
+
+ def wait_for_signal
+ value = wait :ponycopter
+ @signaled = true
+ value
+ end
+
+ def send_signal(value)
+ signal :ponycopter, value
+ end
+ end
+
+The wait_for_signal method in turn calls a method called "wait". Wait suspends
+the running method until another method of the same object calls the "signal"
+method with the same label.
+
+The send_signal method of this class does just that, signaling "ponycopter"
+with the given value. This value is returned from the original wait call.
+
Logging
-------
By default, Celluloid will log any errors and backtraces from any crashing
actors to STDOUT. However, if you wish you can use any logger which is
@@ -298,6 +365,6 @@
* If I like them I'll merge them and give you commit access to my repository
Copyright
---------
-Copyright (c) 2011 Tony Arcieri. See LICENSE.txt for further details.
+Copyright (c) 2011 Tony Arcieri. See LICENSE.txt for further details.
\ No newline at end of file