wisper 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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