From: Ethan Rowe <[email protected]>
Stubbed out the skeleton for new event classes/modules:
* Puppet::Events - global event subscriber/publisher
* Puppet::Events::Publisher - publisher base behavior
The tests should illustrate the intended behavior for these,
to be implemented in a subsequent step.
---
lib/puppet/events.rb | 53 +++++++++
lib/puppet/events/publisher.rb | 44 --------
spec/unit/events.rb | 239 ++++++++++++++++++++++++++++++++++++++++
spec/unit/events/publisher.rb | 105 ------------------
4 files changed, 292 insertions(+), 149 deletions(-)
create mode 100644 lib/puppet/events.rb
delete mode 100644 lib/puppet/events/publisher.rb
create mode 100644 spec/unit/events.rb
delete mode 100755 spec/unit/events/publisher.rb
diff --git a/lib/puppet/events.rb b/lib/puppet/events.rb
new file mode 100644
index 0000000..4d179ed
--- /dev/null
+++ b/lib/puppet/events.rb
@@ -0,0 +1,53 @@
+require 'puppet'
+
+module Puppet::Events
+ module Publisher
+ module ClassMethods
+ def private_publisher(flag = true)
+ @private_publisher = flag
+ end
+
+ def private_publisher?
+ ! ! @private_publisher
+ end
+
+ def create_subscriber_entry(subscriber, method = nil, &block)
+ end
+ end
+
+ def self.included(target_class)
+ target_class.extend ClassMethods
+ end
+
+ def subscriber_callbacks
+ end
+
+ def subscribe(subscriber, method = nil, &block)
+ end
+
+ def raise_event(event)
+ end
+
+ def unsubscribe(subscriber)
+ end
+
+ def private_publisher?
+ self.class.private_publisher?
+ end
+ end
+
+ class << self
+ include Publisher
+
+ # force this to always be private
+ def private_publisher?
+ true
+ end
+
+ def propagate_event(event, callbacks, options = {})
+ end
+
+ def notify_global_subscribers(event)
+ end
+ end
+end
diff --git a/lib/puppet/events/publisher.rb b/lib/puppet/events/publisher.rb
deleted file mode 100644
index 20ed1e3..0000000
--- a/lib/puppet/events/publisher.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require 'weakref'
-
-module Puppet
- module Events
- module Publisher
- def subscribe(consumer, *method, &callback)
- consumer = WeakRef.new(consumer)
- subscription = [consumer]
- if method.size > 0
- method = method.first
- subscription << Proc.new {|*e| consumer.__send__(method, *e)}
- end
-
- if block_given?
- raise 'subscribe() cannot specify both a method and a block!' if
subscription.size > 1
- subscription << callback
- end
-
- subscriptions << subscription
- self
- end
-
- def unsubscribe(consumer)
- subscriptions.reject! do |subscription|
- subscription[0] == consumer
- end
- self
- end
-
- def raise_event(event)
- subscriptions.each do |subscription|
- subscription[1].call(event) if subscription[0].weakref_alive?
- end
- self
- end
-
- def subscriptions
- @subscriptions ||= []
- end
-
- private :subscriptions
- end
- end
-end
diff --git a/spec/unit/events.rb b/spec/unit/events.rb
new file mode 100644
index 0000000..7bd7744
--- /dev/null
+++ b/spec/unit/events.rb
@@ -0,0 +1,239 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../spec_helper.rb'
+require 'puppet/events'
+
+def silent
+ flag = $VERBOSE
+ $VERBOSE = false
+ yield
+ $VERBOSE = flag
+end
+
+describe Puppet::Events do
+ before do
+ @target = Puppet::Events
+ end
+
+ it 'should implement the Puppet::Events::Publisher interface' do
+ @target.respond_to?(:subscribe).should be_true
+ @target.respond_to?(:unsubscribe).should be_true
+ @target.respond_to?(:raise_event).should be_true
+ @target.respond_to?(:private_publisher?).should be_true
+ end
+
+ it 'should be a private publisher' do
+ @target.private_publisher?.should be_true
+ end
+
+ describe 'propagate_event' do
+ before do
+ @event = stub 'event'
+ end
+
+ it 'should raise an exception if no event is provided' do
+ lambda { @target.propagate_event }.should raise_error {|e|
e.message ~ 'provide an event'}
+ end
+
+ it 'should pass event to each callback in sequence' do
+ listener = mock 'listener'
+ callback_seq = sequence 'callbacks'
+ listener.expects(:one).with(@event).in_sequence(callback_seq)
+ listener.expects(:two).with(@event).in_sequence(callback_seq)
+ listener.expects(:three).with(@event).in_sequence(callback_seq)
+
+ callbacks = [:one, :two, :three].collect do |sym|
+ Proc.new {|e| listener.send(sym, e)}
+ end
+
+ @target.propagate_event(@event, callbacks)
+ end
+
+ it 'should notify global subscribers by default' do
+ @target.expects(:notify_global_subscribers).with(@event)
+ @target.propagate_event @event, []
+ end
+
+ it 'should notify global subscribers if :no_global is false' do
+ @target.expects(:notify_global_subscribers).with(@event)
+ @target.propagate_event @event, [], :no_global => false
+ end
+
+ it 'should not notify global subscribers if :no_global is true' do
+ @target.expects(:notify_global_subscribers).never
+ @target.propagate_event @event, [], :no_global => true
+ end
+ end
+
+ describe 'notify_global_subscribers' do
+ it 'should invoke raise_event with the provided event' do
+ event = stub 'event'
+ @target.expects(:raise_event).with(event)
+ @target.notify_global_subscribers(event)
+ end
+ end
+end
+
+describe Puppet::Events::Publisher do
+ before :each do
+ @class = Class.new do
+ include Puppet::Events::Publisher
+ end
+
+ @publisher = @class.new
+ end
+
+ describe 'private publisher flag' do
+ it 'should be false by default' do
+ @publisher.private_publisher?.should be_false
+ end
+
+ it 'should be true if :private_publisher class method is invoked' do
+ @class.private_publisher
+ @publisher.private_publisher?.should be_true
+ end
+
+ it 'should be false if :private_publisher explicitly set at class
level' do
+ @class.private_publisher false
+ @publisher.private_publisher?.should be_false
+ end
+ end
+
+ describe 'raise_event' do
+ before do
+ @callbacks = [:a, :b, :c]
+ @event = stub 'event'
+ @publisher.stubs(:subscriber_callbacks).returns(@callbacks)
+ end
+
+ it 'should invoke Puppet::Events.propagate_event with subscriber
callbacks' do
+ Puppet::Events.expects(:propagate_event).with(@event, @callbacks)
+ @publisher.raise_event(@event)
+ end
+
+ it 'should provide propagate_event with :no_global flag if the
publisher is private' do
+ @publisher.stubs(:private_publisher?).returns(true)
+ Puppet::Events.expects(:propagate_events).with(@event, @callbacks,
{:no_global => true})
+ @publisher.raise_event(@event)
+ end
+ end
+
+ describe 'creating subscriber entry' do
+ before do
+ @subscriber = stub 'subscriber'
+ end
+
+ describe 'with a callback block' do
+ it 'should pass block through unaltered' do
+ block = Proc.new {|e| e}
+ entry = @publisher.class.create_subscriber_entry(@subscriber,
&block)
+ entry.size.should == 2
+ entry[1].should == block
+ end
+
+ it 'should have a weak reference to the subscriber' do
+ entry = @publisher.class.create_subscriber_entry(@subscriber)
{|e| e}
+ entry.size.should == 2
+ entry[0].should == @subscriber
+ entry[0].respond_to?(:weakref_alive?).should be_true
+ end
+ end
+
+ describe 'with a method name' do
+ it 'should create a block that invokes the method name on a weak
ref to the subscriber' do
+ class << @subscriber
+ attr_accessor :weakref, :last_received
+
+ def callback(e)
+ self.last_received = e
+ self.weakref = self.respond_to(:weakref_alive?)
+ end
+ end
+
+ entry = @publisher.class.create_subscriber_entry(@subscriber,
:callback)
+ entry.size.should == 2
+ event = stub 'event'
+ entry[1].call(event)
+ @publisher.last_received.should == event
+ @publisher.weakref.should be_true
+ end
+
+ it 'should have a weak reference to the subscriber' do
+ entry = @publisher.class.create_subscriber_entry(@subscriber,
:callback)
+ entry.size.should == 2
+ entry[0].should == @publisher
+ entry[0].respond_to(:weakref_alive?).should be_true
+ end
+ end
+ end
+
+ describe 'subscribe and unsubscribe' do
+ it 'should return publisher upon success' do
+ @consumer = Object.new
+
+ result = @publisher.subscribe(@consumer) do |event|
+ [self.object_id, event.object_id]
+ end
+ result.should == @publisher
+
+ @publisher.unsubscribe(@consumer).should == @publisher
+ end
+ end
+
+ describe 'subscribe' do
+ it 'should throw an exception if subscribe invoked with both symbol
and block' do
+ lambda { @publisher.subscribe(Object.new, :some_method) {|x|
x.object_id} }.should raise_error() {|error|
+ error.message.should =~ /specify both a method and a block/
+ }
+ end
+
+ it 'should get subscription entry via class.create_subscription_entry'
do
+ sub_a = stub 'sub_a'
+ sub_b = stub 'sub_b'
+ sub_seq = sequence 'sub_seq'
+ a_block = Proc.new {|e| e}
+ @class.expects(:create_subscription_entry).with(sub_a,
a_block).in_sequence(sub_seq)
+ @class.expects(:create_subscription_entry).with(sub_b,
:foo).in_sequence(sub_seq)
+ @publisher.subscribe(sub_a, &a_block)
+ @publisher.subscribe(sub_b, :foo)
+ end
+
+ it 'should put subscription entry for method in subscriber_callbacks'
do
+ subscriber = mock 'subscriber'
+ subscriber.expects(:foo)
+ @publisher.subscribe(subscriber, :foo)
+ @publisher.subscriber_callbacks.each {|cb| cb.call}
+ end
+
+ it 'should put subscription entry for block in subscriber_callbacks' do
+ subscriber = mock 'subscriber'
+ subscriber.expects(:in_block)
+ @publisher.subscribe(subscriber) { subscriber.in_block }
+ @publisher.subscriber_callbacks.each {|cb| cb.call}
+ end
+
+ it 'should put subscriber_callbacks in order of subscription' do
+ callbacks = (1..5).collect do |x|
+ p = Proc.new {|e| e}
+ @publisher.subscribe(p, &p)
+ p
+ end
+
+ @publisher.subscriber_callbacks.should == callbacks
+ end
+
+ it 'should remove subscriber_callbacks for subscribers who
unsubscribed' do
+ consumers = (1..3).collect {|x| Proc.new {|e| e} }
+ consumers.each {|c| @publisher.subscribe(c, &c) }
+ @publisher.unsubscribe(consumers[1])
+ @publisher.subscriber_callbacks.should == [0,2].collect {|c|
consumers[c]}
+ end
+
+ it 'should remove subscriber_callbacks for subscribers who passed on'
do
+ consumers = (1..3).collect {|x| Proc.new {|e| e} }
+ consumers.each {|c| @publisher.subscribe(c, &c) }
+ consumers.first.stubs(:weakref_alive?).returns(false)
+ @publisher.subscriber_callbacks.should == [1,2].collect {|c|
consumers[c]}
+ end
+ end
+end
diff --git a/spec/unit/events/publisher.rb b/spec/unit/events/publisher.rb
deleted file mode 100755
index 1c1b974..0000000
--- a/spec/unit/events/publisher.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/usr/bin/env ruby
-
-require File.dirname(__FILE__) + '/../../spec_helper'
-require 'puppet/events/publisher'
-
-describe Puppet::Events::Publisher do
- before :each do
- @class = Class.new do
- include Puppet::Events::Publisher
- end
-
- @publisher = @class.new
- end
-
- describe 'subscribe and unsubscribe' do
- it 'should return publisher upon success' do
- @consumer = Object.new
-
- result = @publisher.subscribe(@consumer) do |event|
- [self.object_id, event.object_id]
- end
- result.should == @publisher
-
- @publisher.unsubscribe(@consumer).should == @publisher
- end
-
- it 'should throw an exception if subscribe invoked with both symbol and
block' do
- lambda { @publisher.subscribe(Object.new, :some_method) {|x|
x.object_id} }.should raise_error() {|error|
- error.message.should =~ /specify both a method and a block/
- }
- end
- end
-
- describe 'raise_event' do
- it 'should pass event to a subscriber block when subscription performed
with block rather than method symbol' do
- consumer = Object.new
- stack = []
- @publisher.subscribe(consumer) do |event|
- stack << [consumer.object_id, event.object_id]
- end
- e = Object.new
- @publisher.raise_event(e)
-
- stack.size.should == 1
- stack.first.should == [consumer.object_id, e.object_id]
- end
-
- it 'should pass event to subscriber method when method specified' do
- consumer = Object.new
- stack = []
- class << consumer
- attr_accessor :stack
- def handle_event(event)
- stack << [event, self]
- end
- end
- consumer.stack = stack
- @publisher.subscribe(consumer, :handle_event)
- e = Object.new
- @publisher.raise_event(e)
- stack.size.should == 1
- stack.first.should == [e, consumer]
- end
-
- it 'should broadcast in order of subscription' do
- consumer = Object.new
- stack = []
- @publisher.subscribe(consumer) {|e| stack << :first}
- @publisher.subscribe(consumer) {|e| stack << :second}
- @publisher.subscribe(consumer) {|e| stack << :third}
- @publisher.raise_event(Object.new)
- stack.size.should == 3
- stack.should == [:first, :second, :third]
- end
-
- it 'should skip subscribers who have unsubscribed' do
- stack = []
- consumers = (1..3).collect {|x| Object.new}
- consumers.each {|c| @publisher.subscribe(c) {|e| stack << c.object_id} }
- @publisher.unsubscribe(consumers[1])
- @publisher.raise_event(Object.new)
- stack.should == [0,2].collect {|i| consumers[i].object_id}
- end
-
- it 'should gracefully handle subscribers that have passed on' do
- gc = GC.enable
- stack = []
- consumer_class = Class.new do
- attr_accessor :stack
- def handle_event(event)
- puts "Handling event!"
- stack << object_id
- end
- end
- consumers = (1..3).collect {|x| consumer_class.new}
- consumers.each {|c| c.stack = stack; @publisher.subscribe(c,
:handle_event)}
- consumers.delete_at(1)
- ObjectSpace.garbage_collect
- @publisher.raise_event(Object.new)
- stack.size.should == 2
- stack.should == consumers.collect {|c| c.object_id}
- GC.disable if gc
- end
- end
-end
--
1.6.3.3
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---