celluloid 0.18.0.pre → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +258 -39
  3. data/CONDUCT.md +13 -0
  4. data/CONTRIBUTING.md +39 -0
  5. data/README.md +54 -165
  6. data/REFACTOR.md +1 -0
  7. data/architecture.md +120 -0
  8. data/examples/basic_usage.rb +1 -1
  9. data/examples/configurations.rb +78 -0
  10. data/examples/futures.rb +1 -1
  11. data/examples/ring.rb +5 -4
  12. data/examples/simple_pmap.rb +1 -1
  13. data/examples/stack.rb +2 -2
  14. data/examples/supervisors_and_registry.rb +82 -0
  15. data/examples/timers.rb +2 -2
  16. data/lib/celluloid.rb +72 -47
  17. data/lib/celluloid/actor.rb +27 -17
  18. data/lib/celluloid/actor/system.rb +13 -29
  19. data/lib/celluloid/autostart.rb +5 -5
  20. data/lib/celluloid/call/async.rb +2 -0
  21. data/lib/celluloid/call/sync.rb +10 -3
  22. data/lib/celluloid/calls.rb +5 -12
  23. data/lib/celluloid/cell.rb +5 -9
  24. data/lib/celluloid/condition.rb +3 -3
  25. data/lib/celluloid/core_ext.rb +0 -2
  26. data/lib/celluloid/debug.rb +3 -0
  27. data/lib/celluloid/exceptions.rb +2 -2
  28. data/lib/celluloid/future.rb +7 -9
  29. data/lib/celluloid/group.rb +12 -8
  30. data/lib/celluloid/group/pool.rb +1 -3
  31. data/lib/celluloid/group/spawner.rb +2 -6
  32. data/lib/celluloid/internals/call_chain.rb +15 -0
  33. data/lib/celluloid/internals/cpu_counter.rb +62 -0
  34. data/lib/celluloid/internals/handlers.rb +42 -0
  35. data/lib/celluloid/internals/links.rb +38 -0
  36. data/lib/celluloid/internals/logger.rb +104 -0
  37. data/lib/celluloid/internals/method.rb +34 -0
  38. data/lib/celluloid/internals/properties.rb +32 -0
  39. data/lib/celluloid/internals/receivers.rb +64 -0
  40. data/lib/celluloid/internals/registry.rb +102 -0
  41. data/lib/celluloid/internals/responses.rb +46 -0
  42. data/lib/celluloid/internals/signals.rb +24 -0
  43. data/lib/celluloid/internals/stack.rb +74 -0
  44. data/lib/celluloid/internals/stack/dump.rb +12 -0
  45. data/lib/celluloid/internals/stack/states.rb +72 -0
  46. data/lib/celluloid/internals/stack/summary.rb +12 -0
  47. data/lib/celluloid/internals/task_set.rb +51 -0
  48. data/lib/celluloid/internals/thread_handle.rb +52 -0
  49. data/lib/celluloid/internals/uuid.rb +40 -0
  50. data/lib/celluloid/logging/incident.rb +21 -0
  51. data/lib/celluloid/logging/incident_logger.rb +147 -0
  52. data/lib/celluloid/logging/incident_reporter.rb +49 -0
  53. data/lib/celluloid/logging/log_event.rb +20 -0
  54. data/lib/celluloid/logging/ring_buffer.rb +64 -0
  55. data/lib/celluloid/mailbox.rb +22 -9
  56. data/lib/celluloid/mailbox/evented.rb +13 -5
  57. data/lib/celluloid/notifications.rb +95 -0
  58. data/lib/celluloid/pool.rb +6 -0
  59. data/lib/celluloid/probe.rb +81 -0
  60. data/lib/celluloid/proxy/abstract.rb +9 -9
  61. data/lib/celluloid/proxy/async.rb +1 -1
  62. data/lib/celluloid/proxy/block.rb +2 -2
  63. data/lib/celluloid/proxy/cell.rb +1 -1
  64. data/lib/celluloid/proxy/future.rb +2 -4
  65. data/lib/celluloid/proxy/sync.rb +1 -3
  66. data/lib/celluloid/rspec.rb +22 -33
  67. data/lib/celluloid/supervision.rb +17 -0
  68. data/lib/celluloid/supervision/configuration.rb +169 -0
  69. data/lib/celluloid/supervision/configuration/injections.rb +8 -0
  70. data/lib/celluloid/supervision/configuration/instance.rb +113 -0
  71. data/lib/celluloid/supervision/constants.rb +123 -0
  72. data/lib/celluloid/supervision/container.rb +144 -0
  73. data/lib/celluloid/supervision/container/behavior.rb +89 -0
  74. data/lib/celluloid/supervision/container/behavior/pool.rb +71 -0
  75. data/lib/celluloid/supervision/container/behavior/tree.rb +23 -0
  76. data/lib/celluloid/supervision/container/injections.rb +8 -0
  77. data/lib/celluloid/supervision/container/instance.rb +116 -0
  78. data/lib/celluloid/supervision/container/pool.rb +210 -0
  79. data/lib/celluloid/supervision/service.rb +27 -0
  80. data/lib/celluloid/supervision/supervise.rb +34 -0
  81. data/lib/celluloid/supervision/validation.rb +40 -0
  82. data/lib/celluloid/supervision/version.rb +5 -0
  83. data/lib/celluloid/system_events.rb +11 -6
  84. data/lib/celluloid/task.rb +25 -12
  85. data/lib/celluloid/task/fibered.rb +2 -0
  86. data/lib/celluloid/task/threaded.rb +3 -3
  87. data/lib/celluloid/test.rb +5 -2
  88. data/lib/celluloid/thread.rb +0 -2
  89. data/lib/celluloid/version.rb +1 -1
  90. data/spec/celluloid/block_spec.rb +29 -32
  91. data/spec/celluloid/calls_spec.rb +5 -15
  92. data/spec/celluloid/future_spec.rb +2 -2
  93. data/spec/celluloid/internals/cpu_counter_spec.rb +129 -0
  94. data/spec/celluloid/internals/links_spec.rb +43 -0
  95. data/spec/celluloid/internals/properties_spec.rb +40 -0
  96. data/spec/celluloid/internals/registry_spec.rb +62 -0
  97. data/spec/celluloid/internals/stack/dump_spec.rb +4 -0
  98. data/spec/celluloid/internals/stack/summary_spec.rb +4 -0
  99. data/spec/celluloid/internals/thread_handle_spec.rb +60 -0
  100. data/spec/celluloid/internals/uuid_spec.rb +9 -0
  101. data/spec/celluloid/logging/ring_buffer_spec.rb +36 -0
  102. data/spec/celluloid/mailbox/evented_spec.rb +11 -22
  103. data/spec/celluloid/misc/leak_spec.rb +3 -4
  104. data/spec/celluloid/notifications_spec.rb +140 -0
  105. data/spec/celluloid/probe_spec.rb +102 -0
  106. data/spec/celluloid/proxy_spec.rb +30 -30
  107. data/spec/celluloid/supervision/behavior_spec.rb +74 -0
  108. data/spec/celluloid/supervision/configuration_spec.rb +181 -0
  109. data/spec/celluloid/supervision/container_spec.rb +72 -0
  110. data/spec/celluloid/supervision/instance_spec.rb +13 -0
  111. data/spec/celluloid/supervision/root_spec.rb +28 -0
  112. data/spec/celluloid/supervision/supervisor_spec.rb +93 -0
  113. data/spec/celluloid/task/fibered_spec.rb +1 -3
  114. data/spec/celluloid/task/threaded_spec.rb +1 -3
  115. data/spec/shared/actor_examples.rb +58 -33
  116. data/spec/shared/group_examples.rb +2 -2
  117. data/spec/shared/mailbox_examples.rb +1 -1
  118. data/spec/shared/stack_examples.rb +87 -0
  119. data/spec/shared/task_examples.rb +2 -3
  120. data/spec/spec_helper.rb +2 -4
  121. data/spec/support/configure_rspec.rb +2 -3
  122. data/spec/support/coverage.rb +2 -4
  123. data/spec/support/crash_checking.rb +2 -2
  124. data/spec/support/examples/actor_class.rb +3 -8
  125. data/spec/support/examples/call_class.rb +2 -2
  126. data/spec/support/examples/container_class.rb +35 -0
  127. data/spec/support/examples/evented_mailbox_class.rb +1 -2
  128. data/spec/support/examples/stack_classes.rb +58 -0
  129. data/spec/support/examples/stack_methods.rb +23 -0
  130. data/spec/support/examples/subordinate_class.rb +19 -0
  131. data/spec/support/logging.rb +2 -34
  132. data/spec/support/loose_threads.rb +3 -16
  133. data/spec/support/reset_class_variables.rb +5 -1
  134. data/spec/support/stubbing.rb +1 -1
  135. metadata +91 -323
  136. data/culture/CONDUCT.md +0 -38
  137. data/culture/GSoC/1010-why_we_will_participate.md +0 -17
  138. data/culture/GSoC/1020-how_mentors_stay_engaged.md +0 -7
  139. data/culture/GSoC/1030-keeping_students_on_schedule.md +0 -9
  140. data/culture/GSoC/1040-getting_students_involved.md +0 -5
  141. data/culture/GSoC/1050-student_involvement_after.md +0 -5
  142. data/culture/GSoC/README.md +0 -16
  143. data/culture/Gemfile +0 -9
  144. data/culture/LICENSE.txt +0 -22
  145. data/culture/README.md +0 -22
  146. data/culture/Rakefile +0 -5
  147. data/culture/SYNC.md +0 -70
  148. data/culture/celluloid-culture.gemspec +0 -18
  149. data/culture/gems/README.md +0 -39
  150. data/culture/gems/dependencies.yml +0 -93
  151. data/culture/gems/loader.rb +0 -101
  152. data/culture/rubocop/README.md +0 -38
  153. data/culture/rubocop/lint.yml +0 -8
  154. data/culture/rubocop/metrics.yml +0 -15
  155. data/culture/rubocop/perf.yml +0 -0
  156. data/culture/rubocop/rubocop.yml +0 -5
  157. data/culture/rubocop/style.yml +0 -61
  158. data/culture/spec/gems_spec.rb +0 -2
  159. data/culture/spec/spec_helper.rb +0 -0
  160. data/culture/spec/sync_spec.rb +0 -2
  161. data/culture/sync.rb +0 -56
  162. data/culture/tasks/rspec.rake +0 -5
  163. data/culture/tasks/rubocop.rake +0 -2
  164. data/lib/celluloid/actor/manager.rb +0 -7
  165. data/lib/celluloid/backported.rb +0 -2
  166. data/lib/celluloid/current.rb +0 -2
  167. data/lib/celluloid/deprecate.rb +0 -34
  168. data/lib/celluloid/fiber.rb +0 -32
  169. data/lib/celluloid/managed.rb +0 -3
  170. data/lib/celluloid/notices.rb +0 -15
  171. data/spec/deprecate/actor_system_spec.rb +0 -72
  172. data/spec/deprecate/block_spec.rb +0 -52
  173. data/spec/deprecate/calls_spec.rb +0 -39
  174. data/spec/deprecate/evented_mailbox_spec.rb +0 -34
  175. data/spec/deprecate/future_spec.rb +0 -32
  176. data/spec/deprecate/internal_pool_spec.rb +0 -4
  177. data/spec/support/env.rb +0 -21
@@ -0,0 +1,43 @@
1
+ RSpec.describe Celluloid::Internals::Links do
2
+ subject { Celluloid::Internals::Links.new }
3
+
4
+ let(:mailbox_mock) do
5
+ Class.new(Array) do
6
+ attr_reader :address
7
+ def initialize(address)
8
+ @address = address
9
+ end
10
+ end
11
+ end
12
+
13
+ let(:first_actor) do
14
+ Struct.new(:mailbox).new(mailbox_mock.new("foo123"))
15
+ end
16
+
17
+ let(:second_actor) do
18
+ Struct.new(:mailbox).new(mailbox_mock.new("bar456"))
19
+ end
20
+
21
+ it "is Enumerable" do
22
+ expect(subject).to be_an(Enumerable)
23
+ end
24
+
25
+ it "adds actors by their mailbox address" do
26
+ expect(subject.include?(first_actor)).to be_falsey
27
+ subject << first_actor
28
+ expect(subject.include?(first_actor)).to be_truthy
29
+ end
30
+
31
+ it "removes actors by their mailbox address" do
32
+ subject << first_actor
33
+ expect(subject.include?(first_actor)).to be_truthy
34
+ subject.delete first_actor
35
+ expect(subject.include?(first_actor)).to be_falsey
36
+ end
37
+
38
+ it "iterates over all actors" do
39
+ subject << first_actor
40
+ subject << second_actor
41
+ expect(subject.inject([]) { |all, a| all << a }).to eq([first_actor, second_actor])
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ RSpec.describe Celluloid::Internals::Properties do
2
+ let(:default_value) { 42 }
3
+ let(:changed_value) { 43 }
4
+
5
+ let(:example_class) do
6
+ Class.new do
7
+ extend Celluloid::Internals::Properties
8
+ property :baz, default: 42
9
+ end
10
+ end
11
+
12
+ let(:example_subclass) do
13
+ Class.new(example_class)
14
+ end
15
+
16
+ let(:example_subclass_subclass) do
17
+ Class.new(example_subclass)
18
+ end
19
+
20
+ it "adds properties to classes" do
21
+ expect(example_class.baz).to eq default_value
22
+ example_class.baz changed_value
23
+ expect(example_class.baz).to eq changed_value
24
+ end
25
+
26
+ it "allows properties to be inherited" do
27
+ expect(example_subclass.baz).to eq default_value
28
+ example_subclass.baz changed_value
29
+ expect(example_subclass.baz).to eq changed_value
30
+ expect(example_class.baz).to eq default_value
31
+ end
32
+
33
+ it "allows properties to be deeply inherited" do
34
+ expect(example_subclass_subclass.baz).to eq default_value
35
+ example_subclass_subclass.baz changed_value
36
+ expect(example_subclass_subclass.baz).to eq changed_value
37
+ expect(example_subclass.baz).to eq default_value
38
+ expect(example_class.baz).to eq default_value
39
+ end
40
+ end
@@ -0,0 +1,62 @@
1
+ RSpec.describe Celluloid::Internals::Registry, actor_system: :global do
2
+ class Marilyn
3
+ include Celluloid
4
+
5
+ def sing_for(person)
6
+ "o/~ Happy birthday, #{person}"
7
+ end
8
+ end
9
+
10
+ it "registers Actors" do
11
+ Celluloid::Actor[:marilyn] = Marilyn.new
12
+ expect(Celluloid::Actor[:marilyn].sing_for("Mr. President")).to eq("o/~ Happy birthday, Mr. President")
13
+ end
14
+
15
+ it "refuses to register non-Actors" do
16
+ expect do
17
+ Celluloid::Actor[:impostor] = Object.new
18
+ end.to raise_error TypeError
19
+ end
20
+
21
+ it "lists all registered actors" do
22
+ Celluloid::Actor[:marilyn] = Marilyn.new
23
+ expect(Celluloid::Actor.registered).to include :marilyn
24
+ end
25
+
26
+ it "knows its name once registered" do
27
+ Celluloid::Actor[:marilyn] = Marilyn.new
28
+ expect(Celluloid::Actor[:marilyn].registered_name).to eq(:marilyn)
29
+ end
30
+
31
+ describe :delete do
32
+ before do
33
+ Celluloid::Actor[:marilyn] ||= Marilyn.new
34
+ end
35
+
36
+ it "removes reference to actors' name from the registry" do
37
+ Celluloid::Actor.delete(:marilyn)
38
+ expect(Celluloid::Actor.registered).not_to include :marilyn
39
+ end
40
+
41
+ it "returns actor removed from the registry" do
42
+ rval = Celluloid::Actor.delete(:marilyn)
43
+ expect(rval).to be_kind_of(Marilyn)
44
+ end
45
+ end
46
+
47
+ describe :clear do
48
+ it "should return a hash of registered actors and remove them from the registry" do
49
+ Celluloid::Actor[:marilyn] ||= Marilyn.new
50
+ rval = Celluloid::Actor.clear_registry
51
+ begin
52
+ expect(rval).to be_kind_of(Hash)
53
+ expect(rval).to have_key(:marilyn)
54
+ expect(rval[:marilyn].wrapped_object).to be_instance_of(Marilyn)
55
+ expect(Celluloid::Actor.registered).to be_empty
56
+ ensure
57
+ # Repopulate the registry once we're done
58
+ rval.each { |key, actor| Celluloid::Actor[key] = actor }
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,4 @@
1
+ RSpec.describe Celluloid::Internals::Stack::Dump do
2
+ subject { actor_system.stack_dump }
3
+ it_behaves_like "a Celluloid Stack"
4
+ end
@@ -0,0 +1,4 @@
1
+ RSpec.describe Celluloid::Internals::Stack::Summary do
2
+ subject { actor_system.stack_summary }
3
+ it_behaves_like "a Celluloid Stack"
4
+ end
@@ -0,0 +1,60 @@
1
+ RSpec.describe Celluloid::Internals::ThreadHandle do
2
+ let(:actor_system) { Celluloid::Actor::System.new }
3
+ after { actor_system.shutdown }
4
+
5
+ context "given a living thread" do
6
+ let(:args) { [actor_system] }
7
+
8
+ before do
9
+ @thread = nil
10
+ @thread_info_queue = Queue.new
11
+ @handle = Celluloid::Internals::ThreadHandle.new(*args) do
12
+ @thread_info_queue << Thread.current
13
+ sleep
14
+ end
15
+ @thread = Timeout.timeout(2) { @thread_info_queue.pop }
16
+ end
17
+
18
+ it "knows the thread is alive" do
19
+ alive = @handle.alive?
20
+ if @thread
21
+ @thread.kill
22
+ @thread.join
23
+ else
24
+ STDERR.puts "NOTE: something failed - thread missing"
25
+ end
26
+ expect(alive).to be(true)
27
+ end
28
+
29
+ context "when a role is provided" do
30
+ let(:args) { [actor_system, :useful] }
31
+
32
+ it "can be retrieved from thread directly" do
33
+ role = @thread.role
34
+ if @thread
35
+ @thread.kill
36
+ @thread.join
37
+ else
38
+ STDERR.puts "NOTE: something failed - thread missing"
39
+ end
40
+ expect(role).to eq(:useful)
41
+ end
42
+ end
43
+ end
44
+
45
+ context "given a finished thread" do
46
+ before do
47
+ thread_info_queue = Queue.new
48
+ @handle = Celluloid::Internals::ThreadHandle.new(actor_system) do
49
+ thread_info_queue << Thread.current
50
+ end
51
+ thread = thread_info_queue.pop
52
+ thread.kill
53
+ Specs.sleep_and_wait_until { !thread.alive? }
54
+ end
55
+
56
+ it "knows the thread is no longer alive" do
57
+ expect(@handle).not_to be_alive
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,9 @@
1
+ RSpec.describe Celluloid::Internals::UUID do
2
+ U = Celluloid::Internals::UUID
3
+
4
+ it "generates unique IDs across the BLOCK_SIZE boundary" do
5
+ upper_bound = U::BLOCK_SIZE * 2 + 10
6
+ uuids = (1..upper_bound).map { U.generate }
7
+ expect(uuids.size).to eq(uuids.uniq.size)
8
+ end
9
+ end
@@ -0,0 +1,36 @@
1
+ RSpec.describe Celluloid::RingBuffer do
2
+ subject { Celluloid::RingBuffer.new(2) }
3
+
4
+ it { is_expected.to be_empty }
5
+ it { is_expected.not_to be_full }
6
+
7
+ it "should push and shift" do
8
+ subject.push("foo")
9
+ subject.push("foo2")
10
+ expect(subject.shift).to eq("foo")
11
+ expect(subject.shift).to eq("foo2")
12
+ end
13
+
14
+ it "should push past the end" do
15
+ subject.push("foo")
16
+ subject.push("foo2")
17
+ subject.push("foo3")
18
+ expect(subject).to be_full
19
+ end
20
+
21
+ it "should shift the most recent" do
22
+ (1..5).each { |i| subject.push(i) }
23
+ expect(subject.shift).to be 4
24
+ expect(subject.shift).to be 5
25
+ expect(subject.shift).to be_nil
26
+ end
27
+
28
+ it "should return nil when shifting empty" do
29
+ expect(subject).to be_empty
30
+ expect(subject.shift).to be_nil
31
+ end
32
+
33
+ it "should be thread-safe" do
34
+ # TODO: how to test?
35
+ end
36
+ end
@@ -2,36 +2,25 @@ RSpec.describe Celluloid::Mailbox::Evented do
2
2
  subject { TestEventedMailbox.new }
3
3
  it_behaves_like "a Celluloid Mailbox"
4
4
 
5
- # NOTE: this example seems too exotic to "have to" succeed on RBX, though I
6
- # don't know why it hangs on subject.receive and the timeout never occurs
7
- #
8
- # Other than
9
- #
10
- # Links:
11
- # https://github.com/celluloid/celluloid-io/pull/98
12
- # https://github.com/celluloid/celluloid-io/issues/56
13
- #
14
- unless RUBY_ENGINE == "rbx"
15
- it "recovers from timeout exceeded to process mailbox message" do
16
- timeout_interval = Specs::TIMER_QUANTUM + 0.1
17
- started_at = Time.now
18
- expect do
19
- Kernel.send(:timeout, timeout_interval) do
20
- subject.receive { false }
21
- end
22
- end.to raise_exception(Timeout::Error)
5
+ it "recovers from timeout exceeded to process mailbox message" do
6
+ timeout_interval = Specs::TIMER_QUANTUM + 0.1
7
+ started_at = Time.now
8
+ expect do
9
+ ::Timeout.timeout(timeout_interval) do
10
+ subject.receive { false }
11
+ end
12
+ end.to raise_exception(Timeout::Error)
23
13
 
24
- expect(Time.now - started_at).to be_within(Specs::TIMER_QUANTUM).of timeout_interval
25
- end
14
+ expect(Time.now - started_at).to be_within(Specs::TIMER_QUANTUM).of timeout_interval
26
15
  end
27
16
 
28
17
  it "discard messages when reactor wakeup fails" do
29
- expect(Celluloid::Internals::Logger).to receive(:crash).with('reactor crashed', RuntimeError)
18
+ expect(Celluloid::Internals::Logger).to receive(:crash).with("reactor crashed", RuntimeError)
30
19
  expect(Celluloid.logger).to receive(:debug).with("Discarded message (mailbox is dead): first")
31
20
 
32
21
  bad_reactor = Class.new do
33
22
  def wakeup
34
- fail
23
+ raise
35
24
  end
36
25
  end
37
26
  mailbox = Celluloid::Mailbox::Evented.new(bad_reactor)
@@ -13,11 +13,10 @@ RSpec.describe "Leaks", actor_system: :global, leaktest: true,
13
13
  []
14
14
  end
15
15
 
16
- def terminate
17
- end
16
+ def terminate; end
18
17
  end
19
18
 
20
- def wait_for_release(weak, _what, count=1000)
19
+ def wait_for_release(weak, _what, count = 1000)
21
20
  trash = []
22
21
  count.times do |step|
23
22
  GC.start
@@ -43,7 +42,7 @@ RSpec.describe "Leaks", actor_system: :global, leaktest: true,
43
42
  end
44
43
 
45
44
  context "celluloid actor" do
46
- it "is properly destroyed upon termination" do
45
+ xit "is properly destroyed upon termination" do
47
46
  actor_life("actor") do |actor|
48
47
  WeakRef.new actor
49
48
  end
@@ -0,0 +1,140 @@
1
+ RSpec.describe Celluloid::Notifications, actor_system: :global do
2
+ class Admirer
3
+ include Celluloid
4
+ include Celluloid::Notifications
5
+
6
+ attr_reader :mourning
7
+ attr_reader :mourning_count
8
+
9
+ def someone_died(_topic, name)
10
+ @mourning = name
11
+ @mourning_count ||= 0
12
+ @mourning_count += 1
13
+ end
14
+ end
15
+
16
+ class President
17
+ include Celluloid
18
+ include Celluloid::Notifications
19
+
20
+ def die(topic = "death")
21
+ publish(topic, "Mr. President")
22
+ end
23
+ end
24
+
25
+ it "notifies relevant subscribers" do
26
+ marilyn = Admirer.new
27
+ jackie = Admirer.new
28
+
29
+ marilyn.subscribe("death", :someone_died)
30
+ jackie.subscribe("alive", :someone_died)
31
+
32
+ president = President.new
33
+
34
+ president.die
35
+ expect(marilyn.mourning).to eq("Mr. President")
36
+ expect(jackie.mourning).not_to eq("Mr. President")
37
+ end
38
+
39
+ it "allows multiple subscriptions from the same actor" do
40
+ marilyn = Admirer.new
41
+
42
+ marilyn.subscribe("death", :someone_died)
43
+ marilyn.subscribe("death", :someone_died)
44
+
45
+ president = President.new
46
+
47
+ president.die
48
+ expect(marilyn.mourning_count).to be(2)
49
+ end
50
+
51
+ it "notifies subscribers" do
52
+ marilyn = Admirer.new
53
+ jackie = Admirer.new
54
+
55
+ marilyn.subscribe("death", :someone_died)
56
+ jackie.subscribe("death", :someone_died)
57
+
58
+ president = President.new
59
+
60
+ president.die
61
+ expect(marilyn.mourning).to eq("Mr. President")
62
+ expect(jackie.mourning).to eq("Mr. President")
63
+ end
64
+
65
+ it "publishes even if there are no subscribers" do
66
+ president = President.new
67
+ president.die
68
+ end
69
+
70
+ it "allows symbol subscriptions" do
71
+ marilyn = Admirer.new
72
+ jackie = Admirer.new
73
+
74
+ marilyn.subscribe(:death, :someone_died)
75
+ jackie.subscribe("death", :someone_died)
76
+
77
+ president = President.new
78
+ president.die(:death)
79
+ expect(marilyn.mourning).to eq("Mr. President")
80
+ expect(jackie.mourning).to eq("Mr. President")
81
+ end
82
+
83
+ it "allows regex subscriptions" do
84
+ marilyn = Admirer.new
85
+
86
+ marilyn.subscribe(/(death|assassination)/, :someone_died)
87
+
88
+ president = President.new
89
+ president.die
90
+ expect(marilyn.mourning).to eq("Mr. President")
91
+ end
92
+
93
+ it "matches symbols against regex subscriptions" do
94
+ marilyn = Admirer.new
95
+
96
+ marilyn.subscribe(/(death|assassination)/, :someone_died)
97
+
98
+ president = President.new
99
+ president.die(:assassination)
100
+ expect(marilyn.mourning).to eq("Mr. President")
101
+ end
102
+
103
+ it "allows unsubscribing" do
104
+ marilyn = Admirer.new
105
+
106
+ subscription = marilyn.subscribe("death", :someone_died)
107
+ marilyn.unsubscribe(subscription)
108
+
109
+ president = President.new
110
+ president.die
111
+ expect(marilyn.mourning).to be_nil
112
+ end
113
+
114
+ it "prunes dead subscriptions" do
115
+ marilyn = Admirer.new
116
+ jackie = Admirer.new
117
+
118
+ marilyn.subscribe("death", :someone_died)
119
+ jackie.subscribe("death", :someone_died)
120
+
121
+ listeners = Celluloid::Notifications.notifier.listeners_for("death").size
122
+ marilyn.terminate
123
+ after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size
124
+
125
+ expect(after_listeners).to eq(listeners - 1)
126
+ end
127
+
128
+ it "prunes multiple subscriptions from a dead actor" do
129
+ marilyn = Admirer.new
130
+
131
+ marilyn.subscribe("death", :someone_died)
132
+ marilyn.subscribe("death", :someone_died)
133
+
134
+ listeners = Celluloid::Notifications.notifier.listeners_for("death").size
135
+ marilyn.terminate
136
+ after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size
137
+
138
+ expect(after_listeners).to eq(listeners - 2)
139
+ end
140
+ end