celluloid 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ require 'autotest/rspec2'
2
+
3
+ Autotest.add_hook :initialize do |autotest|
4
+ autotest.add_exception '.git'
5
+ autotest.clear_mappings
6
+
7
+ # Map specs to themselves
8
+ autotest.add_mapping(%r<^spec/.*_spec.rb$>) { |file, _| file }
9
+
10
+ # Map libraries to specs
11
+ autotest.add_mapping(%r<lib/celluloid/(.*).rb$>) do |_, m|
12
+ ["spec/#{m[1]}_spec.rb"]
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --backtrace
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in celluloid.gemspec
4
+ gemspec
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ celluloid (0.0.1)
5
+ celluloid
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.1.2)
11
+ git (1.2.5)
12
+ jeweler (1.5.2)
13
+ bundler (~> 1.0.0)
14
+ git (>= 1.2.5)
15
+ rake
16
+ rake (0.8.7)
17
+ rspec (2.5.0)
18
+ rspec-core (~> 2.5.0)
19
+ rspec-expectations (~> 2.5.0)
20
+ rspec-mocks (~> 2.5.0)
21
+ rspec-core (2.5.1)
22
+ rspec-expectations (2.5.0)
23
+ diff-lcs (~> 1.1.2)
24
+ rspec-mocks (2.5.0)
25
+
26
+ PLATFORMS
27
+ java
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ bundler (~> 1.0.0)
32
+ celluloid!
33
+ jeweler (~> 1.5.2)
34
+ rspec (>= 2.3.0)
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Tony Arcieri
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,286 @@
1
+ Celluloid
2
+ =========
3
+
4
+ > "I thought of objects being like biological cells and/or individual
5
+ > computers on a network, only able to communicate with messages"
6
+ > _--Alan Kay, creator of Smalltalk, on the meaning of "object oriented programming"_
7
+
8
+ Celluloid is a concurrent object framework for Ruby inspired by Erlang and the
9
+ Actor model. Celluloid gives you thread-backed objects that run concurrently,
10
+ providing the simplicity of Ruby objects for the most common use cases, but
11
+ also the ability to call methods _asynchronously_, allowing the receiver to do
12
+ things in the background while the caller carries on with its business.
13
+ These concurrent objects are called "actors". Actors are somewhere in between
14
+ the kind of object you're typically used to working with and a network service.
15
+
16
+ Usage
17
+ -----
18
+
19
+ To use Celluloid, define a normal Ruby class that includes Celluloid::Actor
20
+
21
+ class Sheen
22
+ include Celluloid::Actor
23
+
24
+ def initialize(name)
25
+ @name = name
26
+ end
27
+
28
+ def set_status(status)
29
+ @status = status
30
+ end
31
+
32
+ def report
33
+ "#{@name} is #{@status}"
34
+ end
35
+ end
36
+
37
+ If you call Sheen.new, you'll wind up with a plain old Ruby object. To
38
+ create a concurrent object instead of a regular one, use Sheen.spawn:
39
+
40
+ >> charlie = Sheen.spawn "Charlie Sheen"
41
+ => #<Celluloid::Actor(Sheen:0x00000100a312d0) @name="Charlie Sheen">
42
+ >> charlie.set_status "winning!"
43
+ => "winning!"
44
+ >> charlie.report
45
+ => "Charlie Sheen is winning!"
46
+ >> charlie.set_status! "asynchronously winning!"
47
+ => nil
48
+ >> charlie.report
49
+ => "Charlie Sheen is asynchronously winning!"
50
+
51
+ Calling spawn creates a concurrent object running inside its own thread. You
52
+ can call methods on this concurrent object just like you would any other Ruby
53
+ object. The Sheen#set_status method works exactly like you'd expect, returning
54
+ the last expression evaluated.
55
+
56
+ However, Celluloid's secret sauce kicks in when you call banged predicate
57
+ methods (i.e. methods ending in !). Even though the Sheen class has no
58
+ set_status! method, you can still call it. Why is this? Because bang methods
59
+ have a special meaning in Celluloid. (Note: this also means you can't define
60
+ bang methods on Celluloid classes and expect them to be callable from other
61
+ objects)
62
+
63
+ Adding a bang to the end of a method instructs Celluloid that you would like
64
+ for the given method to be called _asynchronously_. This means that rather
65
+ than the caller waiting for a response, the caller sends a message to the
66
+ concurrent object that you'd like the given method invoked, and then the
67
+ caller proceeds without waiting for a response. The concurrent object
68
+ receiving the message will then process the method call in the background.
69
+
70
+ Adding a bang to a method name is a convention in Ruby used to indicate that
71
+ the method is in some way "dangerous", and in Celluloid this is no exception.
72
+ You have no guarantees that just because you made an asynchronous call it was
73
+ ever actually invoked. Asynchronous calls will never raise an exception, even
74
+ if an exception occurs when the receiver is processing it. Worse, unhandled
75
+ exceptions will crash the receiver, and making an asynchronous call to a
76
+ crashed object will not raise an error.
77
+
78
+ However, you can still handle errors created by asynchronous calls using
79
+ two features of Celluloid called _supervisors_ and _linking_.
80
+
81
+ Supervisors
82
+ -----------
83
+
84
+ You may be familiar with tools like Monit or God which keep an eye on your
85
+ applications and restart them when they crash. Celluloid supervisors work in
86
+ a similar fashion, except instead of monitoring applications, they monitor
87
+ individual actors and restart them when they crash. Crashes occur whenever
88
+ an unhandled exception is raised anywhere within an actor.
89
+
90
+ To supervise an actor, start it with the _supervise_ method. Using the Sheen
91
+ class from the example above:
92
+
93
+ >> supervisor = Sheen.supervise "Charlie Sheen"
94
+ => #<Celluloid::Supervisor(Sheen) "Charlie Sheen">
95
+
96
+ This created a new Celluloid::Supervisor actor, and also created a new Sheen
97
+ actor, giving its initialize method the argument "Charlie Sheen". The
98
+ _supervise_ method has the same method signature as _spawn_. However, rather
99
+ than returning the newly created actor, _supervise_ returns the supervisor.
100
+ To retrieve the actor that the supervisor is currently using, use the
101
+ Celluloid::Supervisor#actor method:
102
+
103
+ >> supervisor = Sheen.supervise "Charlie Sheen"
104
+ => #<Celluloid::Supervisor(Sheen) "Charlie Sheen">
105
+ >> charlie = supervisor.actor
106
+ => #<Celluloid::Actor(Sheen:0x00000100a312d0)>
107
+
108
+ Supervisors can also automatically put actors into the actor _registry_ using
109
+ the supervise_as method:
110
+
111
+ >> Sheen.supervise_as :charlie, "Charlie Sheen"
112
+ => #<Celluloid::Supervisor(Sheen) "Charlie Sheen">
113
+ >> charlie = Celluloid::Actor[:charlie]
114
+ => #<Celluloid::Actor(Sheen:0x00000100a312d0)>
115
+
116
+ In this case, the supervisor will ensure that an actor of the Sheen class,
117
+ created using the given arguments, is aways available by calling
118
+ Celluloid::Actor[:charlie]. The first argument to supervise_as is the name
119
+ you'd like the newly created actor to be registered under. The remaining
120
+ arguments are passed to initialize just like you called _new_ or _spawn_.
121
+
122
+ See the "Registry" section below for more information on the actor registry
123
+
124
+ Linking
125
+ -------
126
+
127
+ Whenever any unhandled exceptions occur in any of the methods of an actor,
128
+ that actor crashes and dies. Let's start with an example:
129
+
130
+ class JamesDean
131
+ include Celluloid::Actor
132
+ class CarInMyLaneError < StandardError; end
133
+
134
+ def drive_little_bastard
135
+ raise CarInMyLaneError, "that guy's gotta stop. he'll see us"
136
+ end
137
+ end
138
+
139
+ Now, let's have James drive Little Bastard and see what happens:
140
+
141
+ >> james = JamesDean.spawn
142
+ => #<Celluloid::Actor(JamesDean:0x1068)>
143
+ >> james.drive_little_bastard!
144
+ => nil
145
+ >> james
146
+ => #<Celluloid::Actor(JamesDean:0x1068) dead>
147
+
148
+ When we told james asynchronously to drive Little Bastard, it killed him! If
149
+ we were Elizabeth Taylor, co-star in James' latest film at the time of his
150
+ death, we'd certainly want to know when he died. So how can we do that?
151
+
152
+ Actors can _link_ to other actors they're interested in and want to receive
153
+ crash notifications from. In order to receive these events, we need to use the
154
+ trap_exit method to be notified of them. Let's look at how a hypothetical
155
+ Elizabeth Taylor object could be notified that James Dean has crashed:
156
+
157
+ class ElizabethTaylor
158
+ include Celluloid::Actor
159
+ trap_exit :actor_died
160
+
161
+ def actor_died(actor, reason)
162
+ puts "Oh no! #{actor.inspect} has died because of a #{reason.class}"
163
+ end
164
+ end
165
+
166
+ We've now used the trap_exit method to configure a callback which is invoked
167
+ whenever any linked actors crashed. Now we need to link Elizabeth to James so
168
+ James' crash notifications get sent to her:
169
+
170
+ >> james = JamesDean.spawn
171
+ => #<Celluloid::Actor(JamesDean:0x11b8)>
172
+ >> elizabeth = ElizabethTaylor.spawn
173
+ => #<Celluloid::Actor(ElizabethTaylor:0x11f0)>
174
+ >> elizabeth.link james
175
+ => #<Celluloid::Actor(JamesDean:0x11b8)>
176
+ >> james.drive_little_bastard!
177
+ => nil
178
+ Oh no! #<Celluloid::Actor(JamesDean:0x11b8) dead> has died because of a JamesDean::CarInMyLaneError
179
+
180
+ Elizabeth called the _link_ method to receive crash events from James. Because
181
+ Elizabeth was linked to James, when James crashed, James' exit message was
182
+ sent to her. Because Elizabeth was trapping the exit messages she received
183
+ using the trap_exit method, the callback she specified was invoked, allowing
184
+ her to take action (in this case, printing the error). But what would happen
185
+ if she weren't trapping exits? Let's break James apart into two separate
186
+ objects, one for James himself and one for Little Bastard, his car:
187
+
188
+ class PorscheSpider
189
+ include Celluloid::Actor
190
+ class CarInMyLaneError < StandardError; end
191
+
192
+ def drive_on_route_466
193
+ raise CarInMyLaneError, "head on collision :("
194
+ end
195
+ end
196
+
197
+ class JamesDean
198
+ include Celluloid::Actor
199
+
200
+ def initialize
201
+ @little_bastard = PorscheSpider.spawn_link
202
+ end
203
+
204
+ def drive_little_bastard
205
+ @little_bastard.drive_on_route_466
206
+ end
207
+ end
208
+
209
+ If you take a look in JamesDean#initialize, you'll notice that to create an
210
+ instance of PorcheSpider, James is calling the spawn_link method.
211
+
212
+ This method works similarly to _spawn_, except it combines _spawn_ and _link_
213
+ into a single call.
214
+
215
+ Now what happens if we repeat the same scenario with Elizabeth Taylor watching
216
+ for James Dean's crash?
217
+
218
+ >> james = JamesDean.spawn
219
+ => #<Celluloid::Actor(JamesDean:0x1108) @little_bastard=#<Celluloid::Actor(PorscheSpider:0x10ec)>>
220
+ >> elizabeth = ElizabethTaylor.spawn
221
+ => #<Celluloid::Actor(ElizabethTaylor:0x1144)>
222
+ >> elizabeth.link james
223
+ => #<Celluloid::Actor(JamesDean:0x1108) @little_bastard=#<Celluloid::Actor(PorscheSpider:0x10ec)>>
224
+ >> james.drive_little_bastard!
225
+ => nil
226
+ Oh no! #<Celluloid::Actor(JamesDean:0x1108) dead> has died because of a PorscheSpider::CarInMyLaneError
227
+
228
+ When Little Bastard crashed, it killed James as well. Little Bastard killed
229
+ James, and because Elizabeth was trapping James' exit events, she received the
230
+ notification of James' death.
231
+
232
+ Actors that are linked together propagate their error messages to all other
233
+ actors that they're linked to. Unless those actors are trapping exit events,
234
+ those actors too will die, like James did in this case. If you have many,
235
+ many actors linked together in a large object graph, killing one will kill them
236
+ all unless they are trapping exits.
237
+
238
+ This allows you to factor your problem into several actors. If an error occurs
239
+ in any of them, it will kill off all actors used in a particular system. In
240
+ general, you'll probably want to have a supervisor start a single actor which
241
+ is in charge of a particular part of your system, and have that actor
242
+ spawn_link to other actors which are part of the same system. If any error
243
+ occurs in any of these actors, all of them will be killed off and the entire
244
+ subsystem will be restarted by the supervisor in a clean state.
245
+
246
+ If, for any reason, you've linked to an actor and want to sever the link,
247
+ there's a corresponding _unlink_ method to remove links between actors.
248
+
249
+ Registry
250
+ --------
251
+
252
+ Celluloid lets you register actors so you can refer to them symbolically.
253
+ You can register Actors using Celluloid::Actor[]:
254
+
255
+ >> james = JamesDean.spawn
256
+ => #<Celluloid::Actor(JamesDean:0x80c27ce0)>
257
+ >> Celluloid::Actor[:james] = james
258
+ => #<Celluloid::Actor(JamesDean:0x80c27ce0)>
259
+ >> Celluloid::Actor[:james]
260
+ => #<Celluloid::Actor(JamesDean:0x80c27ce0)>
261
+
262
+ The Celluloid::Actor constant acts as a hash, allowing you to register actors
263
+ under the name of your choosing, and access actors by name rather than
264
+ reference. This is important because actors may crash. If you're attempting to
265
+ reference an actor explicitly by storing it in a variable, you may be holding
266
+ onto a reference to a crashed copy of that actor, rather than talking to a
267
+ working, freshly-restarted version.
268
+
269
+ The main use of the registry is for interfacing with actors that are
270
+ automatically restarted by supervisors when they crash.
271
+
272
+ Contributing to Celluloid
273
+ -------------------------
274
+
275
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
276
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
277
+ * Fork the project
278
+ * Start a feature/bugfix branch
279
+ * Commit and push until you are happy with your contribution
280
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
281
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
282
+
283
+ Copyright
284
+ ---------
285
+
286
+ Copyright (c) 2011 Tony Arcieri. See LICENSE.txt for further details.
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "celluloid"
16
+ gem.homepage = "http://github.com/tarcieri/celluloid"
17
+ gem.license = "MIT"
18
+ gem.summary = "Ruby concurrency made easy! Or at least, easier..."
19
+ gem.description = "Celluloid is a concurrent object framework inspired by the Actor Model"
20
+ gem.email = "tony@medioh.com"
21
+ gem.authors = ["Tony Arcieri"]
22
+
23
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
24
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
25
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
26
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rspec/core'
31
+ require 'rspec/core/rake_task'
32
+ RSpec::Core::RakeTask.new(:spec) do |spec|
33
+ spec.pattern = FileList['spec/**/*_spec.rb']
34
+ end
35
+
36
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
37
+ spec.pattern = 'spec/**/*_spec.rb'
38
+ spec.rcov = true
39
+ end
40
+
41
+ task :default => :spec
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "celluloid #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,87 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{celluloid}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tony Arcieri"]
12
+ s.date = %q{2011-05-11}
13
+ s.description = %q{Celluloid is a concurrent object framework inspired by the Actor Model}
14
+ s.email = %q{tony@medioh.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".autotest",
21
+ ".document",
22
+ ".rspec",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.md",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "celluloid.gemspec",
30
+ "lib/celluloid.rb",
31
+ "lib/celluloid/actor.rb",
32
+ "lib/celluloid/actor_proxy.rb",
33
+ "lib/celluloid/calls.rb",
34
+ "lib/celluloid/core_ext.rb",
35
+ "lib/celluloid/events.rb",
36
+ "lib/celluloid/future.rb",
37
+ "lib/celluloid/linking.rb",
38
+ "lib/celluloid/mailbox.rb",
39
+ "lib/celluloid/registry.rb",
40
+ "lib/celluloid/responses.rb",
41
+ "lib/celluloid/supervisor.rb",
42
+ "lib/celluloid/waker.rb",
43
+ "spec/actor_spec.rb",
44
+ "spec/future_spec.rb",
45
+ "spec/mailbox_spec.rb",
46
+ "spec/registry_spec.rb",
47
+ "spec/spec_helper.rb",
48
+ "spec/supervisor_spec.rb",
49
+ "spec/waker_spec.rb"
50
+ ]
51
+ s.homepage = %q{http://github.com/tarcieri/celluloid}
52
+ s.licenses = ["MIT"]
53
+ s.require_paths = ["lib"]
54
+ s.rubygems_version = %q{1.6.2}
55
+ s.summary = %q{Ruby concurrency made easy! Or at least, easier...}
56
+ s.test_files = [
57
+ "spec/actor_spec.rb",
58
+ "spec/future_spec.rb",
59
+ "spec/mailbox_spec.rb",
60
+ "spec/registry_spec.rb",
61
+ "spec/spec_helper.rb",
62
+ "spec/supervisor_spec.rb",
63
+ "spec/waker_spec.rb"
64
+ ]
65
+
66
+ if s.respond_to? :specification_version then
67
+ s.specification_version = 3
68
+
69
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
70
+ s.add_runtime_dependency(%q<celluloid>, [">= 0"])
71
+ s.add_development_dependency(%q<rspec>, [">= 2.3.0"])
72
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
73
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
74
+ else
75
+ s.add_dependency(%q<celluloid>, [">= 0"])
76
+ s.add_dependency(%q<rspec>, [">= 2.3.0"])
77
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
78
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
79
+ end
80
+ else
81
+ s.add_dependency(%q<celluloid>, [">= 0"])
82
+ s.add_dependency(%q<rspec>, [">= 2.3.0"])
83
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
84
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
85
+ end
86
+ end
87
+