wisper 1.2.1 → 1.3.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/.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
|