wisper 1.0.1 → 1.1.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.
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ tags
data/.travis.yml CHANGED
@@ -2,5 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - '1.9.2'
4
4
  - '1.9.3'
5
+ - '2.0.0'
5
6
  - jruby-19mode
7
+ - jruby-20mode
6
8
  - rbx-19mode
data/Gemfile CHANGED
@@ -2,8 +2,9 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'guard'
6
- gem 'guard-rspec'
7
- gem 'rb-fsevent', '~>0.9.1'
8
-
9
- gem 'flay'
5
+ group :development do
6
+ gem 'guard'
7
+ gem 'guard-rspec'
8
+ gem 'rb-fsevent', '~>0.9.1' if RUBY_PLATFORM =~ /darwin/
9
+ gem 'flay'
10
+ end
data/README.md CHANGED
@@ -32,7 +32,7 @@ listeners. Listeners are added, at runtime, to the publishing object.
32
32
 
33
33
  ```ruby
34
34
  class MyPublisher
35
- include Wisper
35
+ include Wisper::Publisher
36
36
 
37
37
  def do_something
38
38
  # ...
@@ -71,34 +71,25 @@ my_publisher.on(:done_something) do |publisher|
71
71
  end
72
72
  ```
73
73
 
74
- ### Asynchronous Publishing (Experimental)
74
+ ### Asynchronous Publishing
75
75
 
76
- There is support for publishing events asynchronously by passing the `async`
77
- option.
78
-
79
- ```ruby
80
- my_publisher.add_subscriber(MySubscriber.new, :async => true)
81
- ```
82
-
83
- This leans on Celluloid, which must be included in your Gemfile.
84
-
85
- The listener is transparently turned in to a Celluloid Actor.
86
-
87
- Please refer to [Celluloid](https://github.com/celluloid/celluloid/wiki)
88
- for more information, particually the
89
- [Gotchas](https://github.com/celluloid/celluloid/wiki/Gotchas).
76
+ Please refer to the [wisper-async](https://github.com/krisleech/wisper-async) gem.
90
77
 
91
78
  ### ActiveRecord
92
79
 
93
80
  ```ruby
94
- class Post < ActiveRecord::Base
95
- include Wisper
81
+ class Bid < ActiveRecord::Base
82
+ include Wisper::Publisher
96
83
 
97
- def create
98
- if save
99
- publish(:create_post_successful, self)
84
+ validates :amount, :presence => true
85
+
86
+ def commit(_attrs = nil)
87
+ assign_attributes(_attrs) if _attrs.present?
88
+ if valid?
89
+ save!
90
+ publish(:create_bid_successful, self)
100
91
  else
101
- publish(:create_post_failed, self)
92
+ publish(:create_bid_failed, self)
102
93
  end
103
94
  end
104
95
  end
@@ -107,22 +98,28 @@ end
107
98
  ### ActionController
108
99
 
109
100
  ```ruby
110
- class PostsController < ApplicationController
101
+ class BidsController < ApplicationController
102
+ def new
103
+ @bid = Bid.new
104
+ end
105
+
111
106
  def create
112
- @post = Post.new(params[:post])
107
+ @bid = Bid.new(params[:bid])
113
108
 
114
- @post.subscribe(PusherListener.new)
115
- @post.subscribe(ActivityListener.new)
116
- @post.subscribe(StatisticsListener.new)
109
+ @bid.subscribe(PusherListener.new)
110
+ @bid.subscribe(ActivityListener.new)
111
+ @bid.subscribe(StatisticsListener.new)
117
112
 
118
- @post.on(:create_post_successful) { |post| redirect_to post }
119
- @post.on(:create_post_failed) { |post| render :action => :new }
113
+ @bid.on(:create_bid_successful) { |bid| redirect_to bid }
114
+ @bid.on(:create_bid_failed) { |bid| render :action => :new }
120
115
 
121
- @post.create
116
+ @bid.commit
122
117
  end
123
118
  end
124
119
  ```
125
120
 
121
+ A full CRUD example is shown in the [Wiki](https://github.com/krisleech/wisper/wiki).
122
+
126
123
  ### Service/Use Case/Command objects
127
124
 
128
125
  A Service object is useful when an operation is complex, interacts with more
@@ -131,7 +128,7 @@ responsibility.
131
128
 
132
129
  ```ruby
133
130
  class PlayerJoiningTeam
134
- include Wisper
131
+ include Wisper::Publisher
135
132
 
136
133
  def execute(player, team)
137
134
  membership = Membership.new(player, team)
@@ -210,6 +207,24 @@ Wisper::GlobalListeners.add_listener(MyListener.new)
210
207
 
211
208
  In a Rails app you might want to add your global listeners in an initalizer.
212
209
 
210
+ Global listeners are threadsafe.
211
+
212
+ ## Temporary Global Listeners
213
+
214
+ You can also globally subscribe listeners for the duration of a block.
215
+
216
+ ```ruby
217
+ Wisper.with_listeners(MyListener.new, OtherListener.new) do
218
+ # do stuff
219
+ end
220
+ ```
221
+
222
+ Any events broadcast within the block by any publisher will be sent to the
223
+ listeners. This is useful if you have a child object which publishes an event
224
+ which is not bubbled down to a parent publisher.
225
+
226
+ Temporary Global Listeners are threadsafe.
227
+
213
228
  ## Subscribing to selected events
214
229
 
215
230
  By default a listener will get notified of all events it can respond to. You
@@ -311,7 +326,8 @@ See `spec/lib/rspec_extensions_spec.rb` for a runnable example.
311
326
 
312
327
  ## Compatibility
313
328
 
314
- Tested with 1.9.x on MRI, JRuby and Rubinius.
329
+ Tested with MRI 1.9.x, MRI 2.0.0, JRuby (1.9 and 2.0 mode) and Rubinius (1.9
330
+ mode).
315
331
  See the [build status](https://travis-ci.org/krisleech/wisper) for details.
316
332
 
317
333
  ## License
data/lib/wisper.rb CHANGED
@@ -1,50 +1,19 @@
1
- require "wisper/version"
2
- require "wisper/registration/registration"
3
- require "wisper/registration/object"
4
- require "wisper/registration/object/async_listener"
5
- require "wisper/registration/block"
1
+ require 'set'
2
+ require 'wisper/version'
3
+ require 'wisper/publisher'
4
+ require 'wisper/registration/registration'
5
+ require 'wisper/registration/object'
6
+ require 'wisper/registration/block'
6
7
  require 'wisper/global_listeners'
8
+ require 'wisper/temporary_listeners'
7
9
 
8
10
  module Wisper
9
- def listeners
10
- @listeners ||= Set.new
11
+ def self.included(base)
12
+ warn "[DEPRECATION] `include Wisper::Publisher` instead of `include Wisper`"
13
+ base.class_eval { include Wisper::Publisher }
11
14
  end
12
15
 
13
- def add_listener(listener, options = {})
14
- listeners << ObjectRegistration.new(listener, options)
15
- self
16
- end
17
-
18
- alias :subscribe :add_listener
19
-
20
- def add_block_listener(options = {}, &block)
21
- listeners << BlockRegistration.new(block, options)
22
- self
23
- end
24
-
25
- # sugar
26
- def respond_to(event, &block)
27
- add_block_listener({:on => event}, &block)
28
- end
29
-
30
- alias :on :respond_to
31
-
32
- private
33
-
34
- def all_listeners
35
- listeners.merge(GlobalListeners.listeners)
36
- end
37
-
38
- def broadcast(event, *args)
39
- all_listeners.each do | listener |
40
- listener.broadcast(clean_event(event), *args)
41
- end
42
- end
43
-
44
- alias :publish :broadcast
45
- alias :announce :broadcast
46
-
47
- def clean_event(event)
48
- event.to_s.gsub('-', '_')
16
+ def self.with_listeners(*args, &block)
17
+ TemporaryListeners.with(*args, &block)
49
18
  end
50
19
  end
@@ -3,26 +3,51 @@ require 'singleton'
3
3
  module Wisper
4
4
  class GlobalListeners
5
5
  include Singleton
6
+ attr_reader :mutex
7
+ private :mutex
6
8
 
7
9
  def initialize
8
- @listeners = Set.new
10
+ @registrations = Set.new
11
+ @mutex = Mutex.new
9
12
  end
10
13
 
11
14
  def add_listener(listener, options = {})
12
- listeners << ObjectRegistration.new(listener, options)
15
+ with_mutex { @registrations << ObjectRegistration.new(listener, options) }
13
16
  self
14
17
  end
15
18
 
19
+ def registrations
20
+ with_mutex { @registrations }
21
+ end
22
+
16
23
  def listeners
17
- @listeners
24
+ registrations.map(&:listener).freeze
25
+ end
26
+
27
+ def clear
28
+ with_mutex { @registrations.clear }
18
29
  end
19
30
 
20
31
  def self.add_listener(listener, options = {})
21
32
  instance.add_listener(listener, options)
22
33
  end
23
34
 
35
+ def self.registrations
36
+ instance.registrations
37
+ end
38
+
24
39
  def self.listeners
25
40
  instance.listeners
26
41
  end
42
+
43
+ def self.clear
44
+ instance.clear
45
+ end
46
+
47
+ private
48
+
49
+ def with_mutex
50
+ mutex.synchronize { yield }
51
+ end
27
52
  end
28
53
  end
@@ -0,0 +1,58 @@
1
+ module Wisper
2
+ module Publisher
3
+ def listeners
4
+ registrations.map(&:listener).freeze
5
+ end
6
+
7
+ def add_listener(listener, options = {})
8
+ local_registrations << ObjectRegistration.new(listener, options)
9
+ self
10
+ end
11
+
12
+ alias :subscribe :add_listener
13
+
14
+ def add_block_listener(options = {}, &block)
15
+ local_registrations << BlockRegistration.new(block, options)
16
+ self
17
+ end
18
+
19
+ # sugar
20
+ def respond_to(event, &block)
21
+ add_block_listener({:on => event}, &block)
22
+ end
23
+
24
+ alias :on :respond_to
25
+
26
+ private
27
+
28
+ def local_registrations
29
+ @local_registrations ||= Set.new
30
+ end
31
+
32
+ def global_registrations
33
+ GlobalListeners.registrations
34
+ end
35
+
36
+ def temporary_registrations
37
+ TemporaryListeners.registrations
38
+ end
39
+
40
+ def registrations
41
+ local_registrations + global_registrations + temporary_registrations
42
+ end
43
+
44
+ def broadcast(event, *args)
45
+ registrations.each do | registration |
46
+ registration.broadcast(clean_event(event), *args)
47
+ end
48
+ end
49
+
50
+ alias :publish :broadcast
51
+ alias :announce :broadcast
52
+
53
+ def clean_event(event)
54
+ event.to_s.gsub('-', '_')
55
+ end
56
+ end
57
+ end
58
+
@@ -1,27 +1,17 @@
1
- begin
2
- require 'celluloid/autostart'
3
- rescue LoadError
4
- # no-op
5
- end
6
-
7
1
  module Wisper
8
2
  class ObjectRegistration < Registration
9
- attr_reader :with, :async
3
+ attr_reader :with
10
4
 
11
5
  def initialize(listener, options)
12
6
  super(listener, options)
13
7
  @with = options[:with]
14
- @async = options.fetch(:async, false)
8
+ fail_on_async if options.has_key?(:async)
15
9
  end
16
10
 
17
11
  def broadcast(event, *args)
18
12
  method_to_call = map_event_to_method(event)
19
13
  if should_broadcast?(event) && listener.respond_to?(method_to_call)
20
- unless async
21
- listener.public_send(method_to_call, *args)
22
- else
23
- AsyncListener.new(listener, method_to_call).async.public_send(method_to_call, *args)
24
- end
14
+ listener.public_send(method_to_call, *args)
25
15
  end
26
16
  end
27
17
 
@@ -30,5 +20,9 @@ module Wisper
30
20
  def map_event_to_method(event)
31
21
  with || event
32
22
  end
23
+
24
+ def fail_on_async
25
+ raise 'The async feature has been moved to the wisper-async gem'
26
+ end
33
27
  end
34
28
  end
@@ -2,7 +2,7 @@
2
2
  # This is a proposal for integration as part of wisper core
3
3
  # for testing: https://github.com/krisleech/wisper/issues/1
4
4
  class TestWisperPublisher
5
- include Wisper
5
+ include Wisper::Publisher
6
6
  def initialize(*args); end
7
7
  end
8
8
 
@@ -0,0 +1,41 @@
1
+ module Wisper
2
+ class TemporaryListeners
3
+
4
+ def self.with(*listeners, &block)
5
+ options = listeners.last.is_a?(Hash) ? listeners.pop : {}
6
+ new.with(listeners, options, &block)
7
+ end
8
+
9
+ def self.registrations
10
+ new.registrations
11
+ end
12
+
13
+ def with(listeners, options, &block)
14
+ add_listeners(listeners, options)
15
+ yield
16
+ clear
17
+ end
18
+
19
+ def registrations
20
+ Thread.current[key] ||= Set.new
21
+ end
22
+
23
+ private
24
+
25
+ def clear
26
+ registrations.clear
27
+ end
28
+
29
+ def add_listeners(listeners, options)
30
+ listeners.each { |listener| add_listener(listener, options)}
31
+ end
32
+
33
+ def add_listener(listener, options)
34
+ registrations << ObjectRegistration.new(listener, options)
35
+ end
36
+
37
+ def key
38
+ '__wisper_temporary_listeners'
39
+ end
40
+ end
41
+ end
@@ -1,3 +1,3 @@
1
1
  module Wisper
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -1,34 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
- class MyService
4
- include Wisper
3
+ describe 'async option' do
4
+ let(:listener) { double('listener') }
5
+ let(:publisher) { publisher_class.new }
5
6
 
6
- def execute
7
- broadcast('success', self)
7
+ it 'it raises a deprecation exception' do
8
+ expect { publisher.add_listener(listener, :async => true) }.to raise_error
8
9
  end
9
10
  end
10
-
11
- # help me...
12
- $global = 'no'
13
-
14
- class MyListener
15
- def success(command)
16
- $global = 'yes'
17
- end
18
- end
19
-
20
- describe Wisper do
21
-
22
- it 'subscribes object to all published events' do
23
- listener = MyListener.new
24
-
25
- command = MyService.new
26
-
27
- command.add_listener(listener, :async => true)
28
-
29
- command.execute
30
- sleep(1) # seriously...
31
- $global.should == 'yes'
32
- end
33
- end
34
-
@@ -3,7 +3,9 @@ require 'spec_helper'
3
3
  describe Wisper::GlobalListeners do
4
4
  let(:global_listener) { double('listener') }
5
5
  let(:local_listener) { double('listener') }
6
- let(:publisher) { Object.new.extend(Wisper) }
6
+ let(:publisher) { publisher_class.new }
7
+
8
+ after(:each) { Wisper::GlobalListeners.clear }
7
9
 
8
10
  describe '.add_listener' do
9
11
  it 'adds given listener to every publisher' do
@@ -24,5 +26,35 @@ describe Wisper::GlobalListeners do
24
26
 
25
27
  publisher.send(:broadcast, :it_happened)
26
28
  end
29
+
30
+ it 'is threadsafe' do
31
+ num_threads = 100
32
+ (1..num_threads).to_a.map do
33
+ Thread.new do
34
+ Wisper::GlobalListeners.add_listener(Object.new)
35
+ sleep(rand) # a little chaos
36
+ end
37
+ end.each(&:join)
38
+
39
+ Wisper::GlobalListeners.listeners.size.should == num_threads
40
+ end
41
+ end
42
+
43
+ describe '.listeners' do
44
+ it 'returns collection of global listeners' do
45
+ Wisper::GlobalListeners.add_listener(global_listener)
46
+ Wisper::GlobalListeners.listeners.should == [global_listener]
47
+ end
48
+
49
+ it 'returns an immutable collection' do
50
+ Wisper::GlobalListeners.listeners.frozen?.should be_true
51
+ expect { Wisper::GlobalListeners.listeners << global_listener }.to raise_error(RuntimeError)
52
+ end
53
+ end
54
+
55
+ it '.clear clears all global listeners' do
56
+ Wisper::GlobalListeners.add_listener(global_listener)
57
+ Wisper::GlobalListeners.clear
58
+ Wisper::GlobalListeners.listeners.should be_empty
27
59
  end
28
60
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  # Example
4
4
  class MyCommand
5
- include Wisper
5
+ include Wisper::Publisher
6
6
 
7
7
  def execute(be_successful)
8
8
  if be_successful
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  class MyPublisher
4
- include Wisper
4
+ include Wisper::Publisher
5
5
 
6
6
  def do_something
7
7
  # ...
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wisper::TemporaryListeners do
4
+ let(:listener_1) { double('listener', :to_a => nil) } # [1]
5
+ let(:listener_2) { double('listener', :to_a => nil) }
6
+ let(:publisher) { Object.class_eval { include Wisper::Publisher } }
7
+
8
+ describe '.with' do
9
+ it 'globally subscribes listener for duration of given block' do
10
+
11
+ listener_1.should_receive(:success)
12
+ listener_1.should_not_receive(:failure)
13
+
14
+ Wisper::TemporaryListeners.with(listener_1) do
15
+ publisher.instance_eval { broadcast(:success) }
16
+ end
17
+
18
+ publisher.instance_eval { broadcast(:failure) }
19
+ end
20
+
21
+ it 'globally subscribes listeners for duration of given block' do
22
+
23
+ listener_1.should_receive(:success)
24
+ listener_1.should_not_receive(:failure)
25
+
26
+ listener_2.should_receive(:success)
27
+ listener_2.should_not_receive(:failure)
28
+
29
+ Wisper::TemporaryListeners.with(listener_1, listener_2) do
30
+ publisher.instance_eval { broadcast(:success) }
31
+ end
32
+
33
+ publisher.instance_eval { broadcast(:failure) }
34
+ end
35
+
36
+ it 'ensures registrations are thread local' do
37
+ num_threads = 20
38
+ (1..num_threads).to_a.map do
39
+ Thread.new do
40
+ Wisper::TemporaryListeners.registrations << Object.new
41
+ Wisper::TemporaryListeners.registrations.size.should == 1
42
+ end
43
+ end.each(&:join)
44
+
45
+ Wisper::TemporaryListeners.registrations.size.should == 0
46
+ end
47
+ end
48
+ end
49
+
50
+ # [1] stubbing `to_a` prevents `Double "listener" received unexpected message
51
+ # :to_a with (no args)` on MRI 1.9.2 when a double is passed to `Array()`.
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wisper::Publisher do
4
+ let(:listener) { double('listener') }
5
+ let(:publisher) { publisher_class.new }
6
+
7
+ describe '.add_listener' do
8
+ it 'subscribes given listener to all published events' do
9
+ listener.should_receive(:this_happened)
10
+ listener.should_receive(:so_did_this)
11
+
12
+ publisher.add_listener(listener)
13
+
14
+ publisher.send(:broadcast, 'this_happened')
15
+ publisher.send(:broadcast, 'so_did_this')
16
+ end
17
+
18
+ describe ':on argument' do
19
+ it 'subscribes given listener to a single event' do
20
+ listener.should_receive(:this_happened)
21
+ listener.stub(:so_did_this)
22
+ listener.should_not_receive(:so_did_this)
23
+
24
+ listener.respond_to?(:so_did_this).should be_true
25
+
26
+ publisher.add_listener(listener, :on => 'this_happened')
27
+
28
+ publisher.send(:broadcast, 'this_happened')
29
+ publisher.send(:broadcast, 'so_did_this')
30
+ end
31
+
32
+ it 'subscribes given listener to many events' do
33
+ listener.should_receive(:this_happened)
34
+ listener.should_receive(:and_this)
35
+ listener.stub(:so_did_this)
36
+ listener.should_not_receive(:so_did_this)
37
+
38
+ listener.respond_to?(:so_did_this).should be_true
39
+
40
+ publisher.add_listener(listener, :on => ['this_happened', 'and_this'])
41
+
42
+ publisher.send(:broadcast, 'this_happened')
43
+ publisher.send(:broadcast, 'so_did_this')
44
+ publisher.send(:broadcast, 'and_this')
45
+ end
46
+ end
47
+
48
+ describe ':with argument' do
49
+ it 'sets method to call listener with on event' do
50
+ listener.should_receive(:different_method).twice
51
+
52
+ publisher.add_listener(listener, :with => :different_method)
53
+
54
+ publisher.send(:broadcast, 'this_happened')
55
+ publisher.send(:broadcast, 'so_did_this')
56
+ end
57
+ end
58
+
59
+ it 'returns publisher so methods can be chained' do
60
+ publisher.add_listener(listener, :on => 'so_did_this').should == publisher
61
+ end
62
+
63
+ it 'is aliased to .subscribe' do
64
+ publisher.respond_to?(:subscribe).should be_true
65
+ end
66
+ end
67
+
68
+ describe '.add_block_listener' do
69
+ let(:insider) { double('insider') }
70
+
71
+ it 'subscribes given block to all events' do
72
+ insider.should_receive(:it_happened).twice
73
+
74
+ publisher.add_block_listener do
75
+ insider.it_happened
76
+ end
77
+
78
+ publisher.send(:broadcast, 'something_happened')
79
+ publisher.send(:broadcast, 'and_so_did_this')
80
+ end
81
+
82
+ describe ':on argument' do
83
+ it '.add_block_listener subscribes block to an event' do
84
+ insider.should_not_receive(:it_happened).once
85
+
86
+ publisher.add_block_listener(:on => 'something_happened') do
87
+ insider.it_happened
88
+ end
89
+
90
+ publisher.send(:broadcast, 'something_happened')
91
+ publisher.send(:broadcast, 'and_so_did_this')
92
+ end
93
+ end
94
+
95
+ it 'returns publisher so methods can be chained' do
96
+ publisher.add_block_listener(:on => 'this_thing_happened') do
97
+ end.should == publisher
98
+ end
99
+ end
100
+
101
+ describe '.on (alternative block syntax)' do
102
+ it 'subscribes given block to an event' do
103
+ insider = double('insider')
104
+ insider.should_receive(:it_happened)
105
+
106
+ publisher.on(:something_happened) do
107
+ insider.it_happened
108
+ end
109
+
110
+ publisher.send(:broadcast, 'something_happened')
111
+ end
112
+ end
113
+
114
+ describe '.broadcast' do
115
+ it 'does not publish events which cannot be responded to' do
116
+ listener.should_not_receive(:so_did_this)
117
+ listener.stub(:respond_to?, false)
118
+
119
+ publisher.add_listener(listener, :on => 'so_did_this')
120
+
121
+ publisher.send(:broadcast, 'so_did_this')
122
+ end
123
+
124
+ describe ':event argument' do
125
+ it 'is indifferent to string and symbol' do
126
+ listener.should_receive(:this_happened).twice
127
+
128
+ publisher.add_listener(listener)
129
+
130
+ publisher.send(:broadcast, 'this_happened')
131
+ publisher.send(:broadcast, :this_happened)
132
+ end
133
+
134
+ it 'is indifferent to dasherized and underscored strings' do
135
+ listener.should_receive(:this_happened).twice
136
+
137
+ publisher.add_listener(listener)
138
+
139
+ publisher.send(:broadcast, 'this_happened')
140
+ publisher.send(:broadcast, 'this-happened')
141
+ end
142
+ end
143
+ end
144
+
145
+ describe '.listeners' do
146
+ it 'returns an immutable collection' do
147
+ publisher.listeners.frozen?.should be_true
148
+ expect { publisher.listeners << listener }.to raise_error(RuntimeError)
149
+ end
150
+
151
+ it 'returns local listeners' do
152
+ publisher.add_listener(listener)
153
+ publisher.listeners.should == [listener]
154
+ publisher.listeners.size.should == 1
155
+ end
156
+ end
157
+ end
@@ -1,140 +1,33 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Wisper do
4
- let(:listener) { double('listener') }
5
- let(:publisher) { Object.new.extend(Wisper) }
6
4
 
7
- describe '.add_listener' do
8
- it 'subscribes given listener to all published events' do
9
- listener.should_receive(:this_happened)
10
- listener.should_receive(:so_did_this)
11
-
12
- publisher.add_listener(listener)
13
-
14
- publisher.send(:broadcast, 'this_happened')
15
- publisher.send(:broadcast, 'so_did_this')
16
- end
17
-
18
- describe ':on argument' do
19
- it 'subscribes given listener to a single event' do
20
- listener.should_receive(:this_happened)
21
- listener.stub(:so_did_this)
22
- listener.should_not_receive(:so_did_this)
23
-
24
- listener.respond_to?(:so_did_this).should be_true
25
-
26
- publisher.add_listener(listener, :on => 'this_happened')
27
-
28
- publisher.send(:broadcast, 'this_happened')
29
- publisher.send(:broadcast, 'so_did_this')
30
- end
31
-
32
- it 'subscribes given listener to many events' do
33
- listener.should_receive(:this_happened)
34
- listener.should_receive(:and_this)
35
- listener.stub(:so_did_this)
36
- listener.should_not_receive(:so_did_this)
37
-
38
- listener.respond_to?(:so_did_this).should be_true
39
-
40
- publisher.add_listener(listener, :on => ['this_happened', 'and_this'])
41
-
42
- publisher.send(:broadcast, 'this_happened')
43
- publisher.send(:broadcast, 'so_did_this')
44
- publisher.send(:broadcast, 'and_this')
45
- end
46
- end
47
-
48
- describe ':with argument' do
49
- it 'sets method to call listener with on event' do
50
- listener.should_receive(:different_method).twice
51
-
52
- publisher.add_listener(listener, :with => :different_method)
53
-
54
- publisher.send(:broadcast, 'this_happened')
55
- publisher.send(:broadcast, 'so_did_this')
56
- end
57
- end
58
-
59
- it 'returns publisher so methods can be chained' do
60
- publisher.add_listener(listener, :on => 'so_did_this').should == publisher
5
+ it 'includes Wisper::Publisher for backwards compatibility' do
6
+ silence_warnings do
7
+ publisher_class = Class.new { include Wisper }
8
+ publisher_class.ancestors.should include Wisper::Publisher
61
9
  end
62
10
  end
63
11
 
64
- describe '.add_block_listener' do
65
- let(:insider) { double('insider') }
66
-
67
- it 'subscribes given block to all events' do
68
- insider.should_receive(:it_happened).twice
69
-
70
- publisher.add_block_listener do
71
- insider.it_happened
72
- end
73
-
74
- publisher.send(:broadcast, 'something_happened')
75
- publisher.send(:broadcast, 'and_so_did_this')
76
- end
77
-
78
- describe ':on argument' do
79
- it '.add_block_listener subscribes block to an event' do
80
- insider.should_not_receive(:it_happened).once
12
+ it '.with_listeners subscribes listeners to all broadcast events for the duration of block' do
13
+ publisher = publisher_class.new
14
+ listener = double('listener')
81
15
 
82
- publisher.add_block_listener(:on => 'something_happened') do
83
- insider.it_happened
84
- end
16
+ listener.should_receive(:im_here)
17
+ listener.should_not_receive(:not_here)
85
18
 
86
- publisher.send(:broadcast, 'something_happened')
87
- publisher.send(:broadcast, 'and_so_did_this')
88
- end
19
+ Wisper.with_listeners(listener) do
20
+ publisher.send(:broadcast, 'im_here')
89
21
  end
90
22
 
91
- it 'returns publisher so methods can be chained' do
92
- publisher.add_block_listener(:on => 'this_thing_happened') do
93
- end.should == publisher
94
- end
95
- end
96
-
97
- describe '.on (alternative block syntax)' do
98
- it 'subscribes given block to an event' do
99
- insider = double('insider')
100
- insider.should_receive(:it_happened)
101
-
102
- publisher.on(:something_happened) do
103
- insider.it_happened
104
- end
105
-
106
- publisher.send(:broadcast, 'something_happened')
107
- end
23
+ publisher.send(:broadcast, 'not_here')
108
24
  end
25
+ end
109
26
 
110
- describe '.broadcast' do
111
- it 'does not publish events which cannot be responded to' do
112
- listener.should_not_receive(:so_did_this)
113
- listener.stub(:respond_to?, false)
114
-
115
- publisher.add_listener(listener, :on => 'so_did_this')
116
-
117
- publisher.send(:broadcast, 'so_did_this')
118
- end
119
-
120
- describe ':event argument' do
121
- it 'is indifferent to string and symbol' do
122
- listener.should_receive(:this_happened).twice
123
-
124
- publisher.add_listener(listener)
125
-
126
- publisher.send(:broadcast, 'this_happened')
127
- publisher.send(:broadcast, :this_happened)
128
- end
129
-
130
- it 'is indifferent to dasherized and underscored strings' do
131
- listener.should_receive(:this_happened).twice
132
-
133
- publisher.add_listener(listener)
134
-
135
- publisher.send(:broadcast, 'this_happened')
136
- publisher.send(:broadcast, 'this-happened')
137
- end
138
- end
139
- end
27
+ # prevents deprecation warning showing up in spec output
28
+ def silence_warnings
29
+ original_verbosity = $VERBOSE
30
+ $VERBOSE = nil
31
+ yield
32
+ $VERBOSE = original_verbosity
140
33
  end
data/spec/spec_helper.rb CHANGED
@@ -9,3 +9,8 @@ RSpec.configure do |config|
9
9
  config.filter_run :focus
10
10
  config.order = 'random'
11
11
  end
12
+
13
+ # returns an anonymous wispered class
14
+ def publisher_class
15
+ Class.new { include Wisper::Publisher }
16
+ end
data/wisper.gemspec CHANGED
@@ -20,5 +20,4 @@ Gem::Specification.new do |gem|
20
20
  gem.add_development_dependency 'rake'
21
21
  gem.add_development_dependency 'rspec'
22
22
  gem.add_development_dependency 'simplecov'
23
- gem.add_development_dependency 'celluloid'
24
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wisper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-02 00:00:00.000000000Z
12
+ date: 2013-06-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &2152451840 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2152451840
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rspec
27
- requirement: &2152451300 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0'
33
38
  type: :development
34
39
  prerelease: false
35
- version_requirements: *2152451300
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: simplecov
38
- requirement: &2152450760 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,18 +53,12 @@ dependencies:
43
53
  version: '0'
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *2152450760
47
- - !ruby/object:Gem::Dependency
48
- name: celluloid
49
- requirement: &2152450220 !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
50
57
  none: false
51
58
  requirements:
52
59
  - - ! '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
- type: :development
56
- prerelease: false
57
- version_requirements: *2152450220
58
62
  description: pub/sub for Ruby objects
59
63
  email:
60
64
  - kris.leech@gmail.com
@@ -71,17 +75,20 @@ files:
71
75
  - Rakefile
72
76
  - lib/wisper.rb
73
77
  - lib/wisper/global_listeners.rb
78
+ - lib/wisper/publisher.rb
74
79
  - lib/wisper/registration/block.rb
75
80
  - lib/wisper/registration/object.rb
76
- - lib/wisper/registration/object/async_listener.rb
77
81
  - lib/wisper/registration/registration.rb
78
82
  - lib/wisper/rspec/stub_wisper_publisher.rb
83
+ - lib/wisper/temporary_listeners.rb
79
84
  - lib/wisper/version.rb
80
85
  - spec/lib/async_spec.rb
81
86
  - spec/lib/global_subscribers_spec.rb
82
87
  - spec/lib/integration_spec.rb
83
88
  - spec/lib/rspec_extensions_spec.rb
84
89
  - spec/lib/simple_example_spec.rb
90
+ - spec/lib/temporary_global_listeners_spec.rb
91
+ - spec/lib/wisper/publisher_spec.rb
85
92
  - spec/lib/wisper_spec.rb
86
93
  - spec/spec_helper.rb
87
94
  - wisper.gemspec
@@ -105,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
112
  version: '0'
106
113
  requirements: []
107
114
  rubyforge_project:
108
- rubygems_version: 1.8.10
115
+ rubygems_version: 1.8.24
109
116
  signing_key:
110
117
  specification_version: 3
111
118
  summary: pub/sub for Ruby objects
@@ -115,6 +122,8 @@ test_files:
115
122
  - spec/lib/integration_spec.rb
116
123
  - spec/lib/rspec_extensions_spec.rb
117
124
  - spec/lib/simple_example_spec.rb
125
+ - spec/lib/temporary_global_listeners_spec.rb
126
+ - spec/lib/wisper/publisher_spec.rb
118
127
  - spec/lib/wisper_spec.rb
119
128
  - spec/spec_helper.rb
120
129
  has_rdoc:
@@ -1,23 +0,0 @@
1
- class AsyncListener
2
- include Celluloid if defined?(Celluloid)
3
-
4
- attr_reader :listener, :event_method
5
-
6
- def initialize(listener, event_method)
7
- @listener = listener
8
- @event_method = event_method.to_sym
9
- end
10
-
11
- def method_missing(method, *args, &block)
12
- if listener.respond_to?(method)
13
- if method == event_method
14
- listener.public_send(method, *args, &block)
15
- terminate
16
- else
17
- listener.public_send(method, *args, &block)
18
- end
19
- else
20
- super(method, *args, &block)
21
- end
22
- end
23
- end