wisper 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -1
- data/Gemfile +2 -0
- data/README.md +31 -0
- data/lib/wisper/publisher.rb +13 -1
- data/lib/wisper/registration/block.rb +1 -1
- data/lib/wisper/registration/object.rb +26 -5
- data/lib/wisper/rspec/stub_wisper_publisher.rb +1 -1
- data/lib/wisper/version.rb +1 -1
- data/spec/lib/global_subscribers_spec.rb +18 -1
- data/spec/lib/wisper/publisher_spec.rb +79 -4
- data/spec/spec_helper.rb +9 -1
- data/wisper.gemspec +1 -1
- metadata +6 -6
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -212,6 +212,24 @@ In a Rails app you might want to add your global listeners in an initalizer.
|
|
212
212
|
|
213
213
|
Global listeners are threadsafe.
|
214
214
|
|
215
|
+
### Scoping to publisher class
|
216
|
+
|
217
|
+
You might want to globally subscribe a listener to publishers with a certain
|
218
|
+
class.
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
Wisper.add_listener(MyListener.new, :scope => :MyPublisher)
|
222
|
+
```
|
223
|
+
|
224
|
+
This will subscribe the listener to all instances of `MyPublisher` and its
|
225
|
+
subclasses.
|
226
|
+
|
227
|
+
Alternatively you can also do exactly the same with a publisher class:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
MyPublisher.add_listener(MyListener.new)
|
231
|
+
```
|
232
|
+
|
215
233
|
## Temporary Global Listeners
|
216
234
|
|
217
235
|
You can also globally subscribe listeners for the duration of a block.
|
@@ -238,6 +256,19 @@ of events to `:on`.
|
|
238
256
|
post_creater.subscribe(PusherListener.new, :on => :create_post_successful)
|
239
257
|
```
|
240
258
|
|
259
|
+
## Prefixing broadcast events
|
260
|
+
|
261
|
+
If you would prefer listeners to receive events with a prefix, for example
|
262
|
+
`on`, you can do so by passing a string or symbol to `:prefix`.
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
post_creater.subscribe(PusherListener.new, :prefix => :on)
|
266
|
+
```
|
267
|
+
|
268
|
+
If `post_creater` where to broadcast the event `post_created` the subscribed
|
269
|
+
listeners would receive `on_post_created`. You can also pass `true` which will
|
270
|
+
use the default prefix, "on".
|
271
|
+
|
241
272
|
## Mapping an event to a different method
|
242
273
|
|
243
274
|
By default the method called on the subscriber is the same as the event
|
data/lib/wisper/publisher.rb
CHANGED
@@ -23,6 +23,14 @@ module Wisper
|
|
23
23
|
|
24
24
|
alias :on :respond_to
|
25
25
|
|
26
|
+
module ClassMethods
|
27
|
+
def add_listener(listener, options = {})
|
28
|
+
GlobalListeners.add(listener, options.merge(:scope => self))
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :subscribe :add_listener
|
32
|
+
end
|
33
|
+
|
26
34
|
private
|
27
35
|
|
28
36
|
def local_registrations
|
@@ -43,7 +51,7 @@ module Wisper
|
|
43
51
|
|
44
52
|
def broadcast(event, *args)
|
45
53
|
registrations.each do | registration |
|
46
|
-
registration.broadcast(clean_event(event), *args)
|
54
|
+
registration.broadcast(clean_event(event), self, *args)
|
47
55
|
end
|
48
56
|
end
|
49
57
|
|
@@ -53,6 +61,10 @@ module Wisper
|
|
53
61
|
def clean_event(event)
|
54
62
|
event.to_s.gsub('-', '_')
|
55
63
|
end
|
64
|
+
|
65
|
+
def self.included(base)
|
66
|
+
base.extend(ClassMethods)
|
67
|
+
end
|
56
68
|
end
|
57
69
|
end
|
58
70
|
|
@@ -1,24 +1,45 @@
|
|
1
1
|
module Wisper
|
2
2
|
class ObjectRegistration < Registration
|
3
|
-
attr_reader :with
|
3
|
+
attr_reader :with, :prefix, :allowed_classes
|
4
4
|
|
5
5
|
def initialize(listener, options)
|
6
6
|
super(listener, options)
|
7
|
-
@with
|
7
|
+
@with = options[:with]
|
8
|
+
@prefix = stringify_prefix(options[:prefix])
|
9
|
+
@allowed_classes = Array(options[:scope]).map(&:to_s).to_set
|
8
10
|
fail_on_async if options.has_key?(:async)
|
9
11
|
end
|
10
12
|
|
11
|
-
def broadcast(event, *args)
|
13
|
+
def broadcast(event, publisher, *args)
|
12
14
|
method_to_call = map_event_to_method(event)
|
13
|
-
if should_broadcast?(event) && listener.respond_to?(method_to_call)
|
15
|
+
if should_broadcast?(event) && listener.respond_to?(method_to_call) && publisher_in_scope?(publisher)
|
14
16
|
listener.public_send(method_to_call, *args)
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
20
|
private
|
19
21
|
|
22
|
+
def publisher_in_scope?(publisher)
|
23
|
+
allowed_classes.empty? || publisher.class.ancestors.any? { |ancestor| allowed_classes.include?(ancestor.to_s) }
|
24
|
+
end
|
25
|
+
|
20
26
|
def map_event_to_method(event)
|
21
|
-
with || event
|
27
|
+
prefix + (with || event).to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def stringify_prefix(_prefix)
|
31
|
+
case _prefix
|
32
|
+
when nil
|
33
|
+
''
|
34
|
+
when true
|
35
|
+
default_prefix + '_'
|
36
|
+
else
|
37
|
+
_prefix.to_s + '_'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def default_prefix
|
42
|
+
'on'
|
22
43
|
end
|
23
44
|
|
24
45
|
def fail_on_async
|
@@ -8,7 +8,7 @@ end
|
|
8
8
|
|
9
9
|
def stub_wisper_publisher(clazz, called_method, event_to_publish, *published_event_args)
|
10
10
|
stub_const(clazz, Class.new(TestWisperPublisher) do
|
11
|
-
define_method(called_method) do
|
11
|
+
define_method(called_method) do |*args|
|
12
12
|
publish(event_to_publish, *published_event_args)
|
13
13
|
end
|
14
14
|
end)
|
data/lib/wisper/version.rb
CHANGED
@@ -34,6 +34,23 @@ describe Wisper::GlobalListeners do
|
|
34
34
|
publisher.send(:broadcast, :it_happened)
|
35
35
|
end
|
36
36
|
|
37
|
+
it 'can be scoped to classes' do
|
38
|
+
publisher_1 = publisher_class.new
|
39
|
+
publisher_2 = publisher_class.new
|
40
|
+
publisher_3 = publisher_class.new
|
41
|
+
|
42
|
+
Wisper::GlobalListeners.add(global_listener, :scope => [publisher_1.class,
|
43
|
+
publisher_2.class])
|
44
|
+
|
45
|
+
global_listener.should_receive(:it_happened_1).once
|
46
|
+
global_listener.should_receive(:it_happened_2).once
|
47
|
+
global_listener.should_not_receive(:it_happened_3)
|
48
|
+
|
49
|
+
publisher_1.send(:broadcast, :it_happened_1)
|
50
|
+
publisher_2.send(:broadcast, :it_happened_2)
|
51
|
+
publisher_3.send(:broadcast, :it_happened_3)
|
52
|
+
end
|
53
|
+
|
37
54
|
it 'is threadsafe' do
|
38
55
|
num_threads = 100
|
39
56
|
(1..num_threads).to_a.map do
|
@@ -54,7 +71,7 @@ describe Wisper::GlobalListeners do
|
|
54
71
|
end
|
55
72
|
|
56
73
|
it 'returns an immutable collection' do
|
57
|
-
Wisper::GlobalListeners.listeners.
|
74
|
+
Wisper::GlobalListeners.listeners.should be_frozen
|
58
75
|
expect { Wisper::GlobalListeners.listeners << global_listener }.to raise_error(RuntimeError)
|
59
76
|
end
|
60
77
|
end
|
@@ -21,7 +21,7 @@ describe Wisper::Publisher do
|
|
21
21
|
listener.stub(:so_did_this)
|
22
22
|
listener.should_not_receive(:so_did_this)
|
23
23
|
|
24
|
-
listener.respond_to
|
24
|
+
listener.should respond_to(:so_did_this)
|
25
25
|
|
26
26
|
publisher.add_listener(listener, :on => 'this_happened')
|
27
27
|
|
@@ -35,7 +35,7 @@ describe Wisper::Publisher do
|
|
35
35
|
listener.stub(:so_did_this)
|
36
36
|
listener.should_not_receive(:so_did_this)
|
37
37
|
|
38
|
-
listener.respond_to
|
38
|
+
listener.should respond_to(:so_did_this)
|
39
39
|
|
40
40
|
publisher.add_listener(listener, :on => ['this_happened', 'and_this'])
|
41
41
|
|
@@ -56,12 +56,71 @@ describe Wisper::Publisher do
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
describe ':prefix argument' do
|
60
|
+
it 'prefixes broadcast events with given symbol' do
|
61
|
+
listener.should_receive(:after_it_happened)
|
62
|
+
listener.should_not_receive(:it_happened)
|
63
|
+
|
64
|
+
publisher.add_listener(listener, :prefix => :after)
|
65
|
+
|
66
|
+
publisher.send(:broadcast, 'it_happened')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'prefixes broadcast events with "on" when given true' do
|
70
|
+
listener.should_receive(:on_it_happened)
|
71
|
+
listener.should_not_receive(:it_happened)
|
72
|
+
|
73
|
+
publisher.add_listener(listener, :prefix => true)
|
74
|
+
|
75
|
+
publisher.send(:broadcast, 'it_happened')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# NOTE: these are not realistic use cases, since you would only ever use
|
80
|
+
# `scope` when globally subscribing.
|
81
|
+
describe ':scope argument' do
|
82
|
+
let(:listener_1) {double('Listener') }
|
83
|
+
let(:listener_2) {double('Listener') }
|
84
|
+
|
85
|
+
before do
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'scopes listener to given class' do
|
89
|
+
listener_1.should_receive(:it_happended)
|
90
|
+
listener_2.should_not_receive(:it_happended)
|
91
|
+
publisher.add_listener(listener_1, :scope => publisher.class)
|
92
|
+
publisher.add_listener(listener_2, :scope => Class.new)
|
93
|
+
publisher.send(:broadcast, 'it_happended')
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'scopes listener to given class string' do
|
97
|
+
listener_1.should_receive(:it_happended)
|
98
|
+
listener_2.should_not_receive(:it_happended)
|
99
|
+
publisher.add_listener(listener_1, :scope => publisher.class.to_s)
|
100
|
+
publisher.add_listener(listener_2, :scope => Class.new.to_s)
|
101
|
+
publisher.send(:broadcast, 'it_happended')
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'includes all subclasses of given class' do
|
105
|
+
publisher_super_klass = publisher_class
|
106
|
+
publisher_sub_klass = Class.new(publisher_super_klass)
|
107
|
+
|
108
|
+
listener = double('Listener')
|
109
|
+
listener.should_receive(:it_happended).once
|
110
|
+
|
111
|
+
publisher = publisher_sub_klass.new
|
112
|
+
|
113
|
+
publisher.add_listener(listener, :scope => publisher_super_klass)
|
114
|
+
publisher.send(:broadcast, 'it_happended')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
59
118
|
it 'returns publisher so methods can be chained' do
|
60
119
|
publisher.add_listener(listener, :on => 'so_did_this').should == publisher
|
61
120
|
end
|
62
121
|
|
63
122
|
it 'is aliased to .subscribe' do
|
64
|
-
publisher.respond_to
|
123
|
+
publisher.should respond_to(:subscribe)
|
65
124
|
end
|
66
125
|
end
|
67
126
|
|
@@ -170,7 +229,7 @@ describe Wisper::Publisher do
|
|
170
229
|
|
171
230
|
describe '.listeners' do
|
172
231
|
it 'returns an immutable collection' do
|
173
|
-
publisher.listeners.
|
232
|
+
publisher.listeners.should be_frozen
|
174
233
|
expect { publisher.listeners << listener }.to raise_error(RuntimeError)
|
175
234
|
end
|
176
235
|
|
@@ -180,4 +239,20 @@ describe Wisper::Publisher do
|
|
180
239
|
publisher.listeners.size.should == 1
|
181
240
|
end
|
182
241
|
end
|
242
|
+
|
243
|
+
describe '#add_listener' do
|
244
|
+
let(:publisher_klass_1) { publisher_class }
|
245
|
+
let(:publisher_klass_2) { publisher_class }
|
246
|
+
|
247
|
+
it 'subscribes listeners to all instances of publisher' do
|
248
|
+
publisher_klass_1.add_listener(listener)
|
249
|
+
listener.should_receive(:it_happened).once
|
250
|
+
publisher_klass_1.new.send(:broadcast, 'it_happened')
|
251
|
+
publisher_klass_2.new.send(:broadcast, 'it_happened')
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'is aliased to #subscribe' do
|
255
|
+
publisher_klass_1.should respond_to(:subscribe)
|
256
|
+
end
|
257
|
+
end
|
183
258
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -7,11 +7,19 @@ end
|
|
7
7
|
require 'wisper'
|
8
8
|
|
9
9
|
RSpec.configure do |config|
|
10
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
11
10
|
config.run_all_when_everything_filtered = true
|
12
11
|
config.filter_run :focus
|
13
12
|
config.order = 'random'
|
14
13
|
config.after(:each) { Wisper::GlobalListeners.clear }
|
14
|
+
|
15
|
+
# Support both Rspec2 should and Rspec3 expect syntax
|
16
|
+
config.expect_with :rspec do |c|
|
17
|
+
c.syntax = [:should, :expect]
|
18
|
+
end
|
19
|
+
|
20
|
+
config.mock_with :rspec do |c|
|
21
|
+
c.syntax = [:should, :expect]
|
22
|
+
end
|
15
23
|
end
|
16
24
|
|
17
25
|
# returns an anonymous wispered class
|
data/wisper.gemspec
CHANGED
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.
|
4
|
+
version: 1.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,24 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-01-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 3.0.0.beta1
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 3.0.0.beta1
|
30
30
|
description: pub/sub for Ruby objects
|
31
31
|
email:
|
32
32
|
- kris.leech@gmail.com
|