siftery-wisper 2.0.1

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +4 -0
  4. data/.travis.yml +22 -0
  5. data/CHANGELOG.md +129 -0
  6. data/CONTRIBUTING.md +61 -0
  7. data/Gemfile +13 -0
  8. data/README.md +373 -0
  9. data/Rakefile +10 -0
  10. data/bin/console +8 -0
  11. data/bin/setup +6 -0
  12. data/gem-public_cert.pem +21 -0
  13. data/lib/wisper.rb +65 -0
  14. data/lib/wisper/broadcasters/logger_broadcaster.rb +37 -0
  15. data/lib/wisper/broadcasters/send_broadcaster.rb +9 -0
  16. data/lib/wisper/configuration.rb +44 -0
  17. data/lib/wisper/global_listeners.rb +74 -0
  18. data/lib/wisper/publisher.rb +89 -0
  19. data/lib/wisper/registration/block.rb +11 -0
  20. data/lib/wisper/registration/object.rb +77 -0
  21. data/lib/wisper/registration/registration.rb +19 -0
  22. data/lib/wisper/temporary_listeners.rb +41 -0
  23. data/lib/wisper/value_objects/events.rb +61 -0
  24. data/lib/wisper/value_objects/prefix.rb +29 -0
  25. data/lib/wisper/version.rb +3 -0
  26. data/siftery-wisper.gemspec +31 -0
  27. data/spec/lib/global_listeners_spec.rb +82 -0
  28. data/spec/lib/integration_spec.rb +56 -0
  29. data/spec/lib/simple_example_spec.rb +21 -0
  30. data/spec/lib/temporary_global_listeners_spec.rb +67 -0
  31. data/spec/lib/wisper/broadcasters/logger_broadcaster_spec.rb +93 -0
  32. data/spec/lib/wisper/broadcasters/send_broadcaster_spec.rb +28 -0
  33. data/spec/lib/wisper/configuration/broadcasters_spec.rb +11 -0
  34. data/spec/lib/wisper/configuration_spec.rb +36 -0
  35. data/spec/lib/wisper/publisher_spec.rb +331 -0
  36. data/spec/lib/wisper/registrations/object_spec.rb +14 -0
  37. data/spec/lib/wisper/value_objects/events_spec.rb +107 -0
  38. data/spec/lib/wisper/value_objects/prefix_spec.rb +46 -0
  39. data/spec/lib/wisper_spec.rb +99 -0
  40. data/spec/spec_helper.rb +24 -0
  41. metadata +101 -0
@@ -0,0 +1,28 @@
1
+ module Wisper
2
+ module Broadcasters
3
+ describe SendBroadcaster do
4
+ let(:listener) { double('listener') }
5
+ let(:event) { 'thing_created' }
6
+
7
+ describe '#broadcast' do
8
+ context 'without arguments' do
9
+ let(:args) { [] }
10
+
11
+ it 'sends event to listener without any arguments' do
12
+ expect(listener).to receive(event).with(no_args())
13
+ subject.broadcast(listener, anything, event, args)
14
+ end
15
+ end
16
+
17
+ context 'with arguments' do
18
+ let(:args) { [1,2,3] }
19
+
20
+ it 'sends event to listener with arguments' do
21
+ expect(listener).to receive(event).with(*args)
22
+ subject.broadcast(listener, anything, event, args)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ module Wisper
2
+ describe Configuration::Broadcasters do
3
+ describe 'broadcasters' do
4
+ describe '#to_h' do
5
+ it 'returns a Hash' do
6
+ expect(subject.to_h).to be_instance_of(Hash)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ module Wisper
2
+ describe Configuration do
3
+ describe 'broadcasters' do
4
+ let(:broadcaster) { double }
5
+ let(:key) { :default }
6
+
7
+ it '#broadcasters returns empty collection' do
8
+ expect(subject.broadcasters).to be_empty
9
+ end
10
+
11
+ describe '#broadcaster' do
12
+ it 'adds given broadcaster' do
13
+ subject.broadcaster(key, broadcaster)
14
+ expect(subject.broadcasters).to include key
15
+ expect(subject.broadcasters[key]).to eql broadcaster
16
+ end
17
+
18
+ it 'returns the configuration' do
19
+ expect(subject.broadcaster(key, broadcaster)).to eq subject
20
+ end
21
+ end
22
+ end
23
+
24
+ describe '#default_prefix=' do
25
+ let(:prefix_class) { ValueObjects::Prefix }
26
+ let(:default_value) { double }
27
+
28
+ before { allow(prefix_class).to receive(:default=) }
29
+
30
+ it 'sets the default value for prefixes' do
31
+ expect(prefix_class).to receive(:default=).with(default_value)
32
+ subject.default_prefix = default_value
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,331 @@
1
+ describe Wisper::Publisher do
2
+ let(:listener) { double('listener') }
3
+ let(:publisher) { publisher_class.new }
4
+
5
+ describe '.subscribe' do
6
+ it 'subscribes given listener to all published events' do
7
+ expect(listener).to receive(:this_happened)
8
+ expect(listener).to receive(:so_did_this)
9
+
10
+ publisher.subscribe(listener)
11
+
12
+ publisher.send(:broadcast, 'this_happened')
13
+ publisher.send(:broadcast, 'so_did_this')
14
+ end
15
+
16
+ describe ':on argument' do
17
+ before do
18
+ allow(listener).to receive(:something_a_happened)
19
+ allow(listener).to receive(:and_this)
20
+ allow(listener).to receive(:so_did_this)
21
+ end
22
+
23
+ describe 'given a string' do
24
+ it 'subscribes listener to an event' do
25
+ expect(listener).to receive(:this_happened)
26
+ expect(listener).not_to receive(:so_did_this)
27
+
28
+ publisher.subscribe(listener, on: 'this_happened')
29
+
30
+ publisher.send(:broadcast, 'this_happened')
31
+ publisher.send(:broadcast, 'so_did_this')
32
+ end
33
+ end
34
+
35
+ describe 'given a symbol' do
36
+ it 'subscribes listener to an event' do
37
+ expect(listener).to receive(:this_happened)
38
+ expect(listener).not_to receive(:so_did_this)
39
+
40
+ publisher.subscribe(listener, on: :this_happened)
41
+
42
+ publisher.send(:broadcast, 'this_happened')
43
+ publisher.send(:broadcast, 'so_did_this')
44
+ end
45
+ end
46
+
47
+ describe 'given an array' do
48
+ it 'subscribes listener to events' do
49
+ expect(listener).to receive(:this_happened)
50
+ expect(listener).to receive(:and_this)
51
+ expect(listener).not_to receive(:so_did_this)
52
+
53
+ publisher.subscribe(listener, on: ['this_happened', 'and_this'])
54
+
55
+ publisher.send(:broadcast, 'this_happened')
56
+ publisher.send(:broadcast, 'so_did_this')
57
+ publisher.send(:broadcast, 'and_this')
58
+ end
59
+ end
60
+
61
+ describe 'given a regex' do
62
+ it 'subscribes listener to matching events' do
63
+ expect(listener).to receive(:something_a_happened)
64
+ expect(listener).not_to receive(:so_did_this)
65
+
66
+ publisher.subscribe(listener, on: /something_._happened/)
67
+
68
+ publisher.send(:broadcast, 'something_a_happened')
69
+ publisher.send(:broadcast, 'so_did_this')
70
+ end
71
+ end
72
+
73
+ describe 'given an unsupported argument' do
74
+ it 'raises an error' do
75
+ publisher.subscribe(listener, on: Object.new)
76
+ expect { publisher.send(:broadcast, 'something_a_happened') }.to raise_error(ArgumentError)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe ':with argument' do
82
+ it 'sets method to call listener with on event' do
83
+ expect(listener).to receive(:different_method).twice
84
+
85
+ publisher.subscribe(listener, :with => :different_method)
86
+
87
+ publisher.send(:broadcast, 'this_happened')
88
+ publisher.send(:broadcast, 'so_did_this')
89
+ end
90
+ end
91
+
92
+ describe ':prefix argument' do
93
+ it 'prefixes broadcast events with given symbol' do
94
+ expect(listener).to receive(:after_it_happened)
95
+ expect(listener).not_to receive(:it_happened)
96
+
97
+ publisher.subscribe(listener, :prefix => :after)
98
+
99
+ publisher.send(:broadcast, 'it_happened')
100
+ end
101
+
102
+ it 'prefixes broadcast events with "on" when given true' do
103
+ expect(listener).to receive(:on_it_happened)
104
+ expect(listener).not_to receive(:it_happened)
105
+
106
+ publisher.subscribe(listener, :prefix => true)
107
+
108
+ publisher.send(:broadcast, 'it_happened')
109
+ end
110
+ end
111
+
112
+ describe ':scope argument' do
113
+ let(:listener_1) { double('Listener') }
114
+ let(:listener_2) { double('Listener') }
115
+
116
+ it 'scopes listener to given class' do
117
+ expect(listener_1).to receive(:it_happended)
118
+ expect(listener_2).not_to receive(:it_happended)
119
+ publisher.subscribe(listener_1, :scope => publisher.class)
120
+ publisher.subscribe(listener_2, :scope => Class.new)
121
+ publisher.send(:broadcast, 'it_happended')
122
+ end
123
+
124
+ it 'scopes listener to given class string' do
125
+ expect(listener_1).to receive(:it_happended)
126
+ expect(listener_2).not_to receive(:it_happended)
127
+ publisher.subscribe(listener_1, :scope => publisher.class.to_s)
128
+ publisher.subscribe(listener_2, :scope => Class.new.to_s)
129
+ publisher.send(:broadcast, 'it_happended')
130
+ end
131
+
132
+ it 'includes all subclasses of given class' do
133
+ publisher_super_klass = publisher_class
134
+ publisher_sub_klass = Class.new(publisher_super_klass)
135
+
136
+ listener = double('Listener')
137
+ expect(listener).to receive(:it_happended).once
138
+
139
+ publisher = publisher_sub_klass.new
140
+
141
+ publisher.subscribe(listener, :scope => publisher_super_klass)
142
+ publisher.send(:broadcast, 'it_happended')
143
+ end
144
+ end
145
+
146
+ describe ':broadcaster argument'do
147
+ let(:broadcaster) { double('broadcaster') }
148
+ let(:listener) { double('listener') }
149
+ let(:event_name) { 'it_happened' }
150
+
151
+ before do
152
+ Wisper.configuration.broadcasters.clear
153
+ allow(listener).to receive(event_name)
154
+ allow(broadcaster).to receive(:broadcast)
155
+ end
156
+
157
+ after { Wisper.setup } # restore default configuration
158
+
159
+ it 'given an object which responds_to broadcast it uses object' do
160
+ publisher.subscribe(listener, broadcaster: broadcaster)
161
+ expect(broadcaster).to receive('broadcast')
162
+ publisher.send(:broadcast, event_name)
163
+ end
164
+
165
+ it 'given a key it uses a configured broadcaster' do
166
+ Wisper.configure { |c| c.broadcaster(:foobar, broadcaster) }
167
+ publisher.subscribe(listener, broadcaster: :foobar)
168
+ expect(broadcaster).to receive('broadcast')
169
+ publisher.send(:broadcast, event_name)
170
+ end
171
+
172
+ it 'given an unknown key it raises error' do
173
+ expect { publisher.subscribe(listener, broadcaster: :foobar) }.to raise_error(KeyError, /broadcaster not found/)
174
+ end
175
+
176
+ it 'given nothing it uses the default broadcaster' do
177
+ Wisper.configure { |c| c.broadcaster(:default, broadcaster) }
178
+ publisher.subscribe(listener)
179
+ expect(broadcaster).to receive('broadcast')
180
+ publisher.send(:broadcast, event_name)
181
+ end
182
+
183
+ describe 'async alias' do
184
+ it 'given an object which responds_to broadcast it uses object' do
185
+ publisher.subscribe(listener, async: broadcaster)
186
+ expect(broadcaster).to receive('broadcast')
187
+ publisher.send(:broadcast, event_name)
188
+ end
189
+
190
+ it 'given true it uses configured async broadcaster' do
191
+ Wisper.configure { |c| c.broadcaster(:async, broadcaster) }
192
+ publisher.subscribe(listener, async: true)
193
+ expect(broadcaster).to receive('broadcast')
194
+ publisher.send(:broadcast, event_name)
195
+ end
196
+
197
+ it 'given false it uses configured default broadcaster' do
198
+ Wisper.configure { |c| c.broadcaster(:default, broadcaster) }
199
+ publisher.subscribe(listener, async: false)
200
+ expect(broadcaster).to receive('broadcast')
201
+ publisher.send(:broadcast, event_name)
202
+ end
203
+ end
204
+
205
+ describe 'callable broadcasters' do
206
+ let(:broadcaster) { Struct.new(:options) }
207
+ let(:async_options) { { queue: 'custom', retry: false } }
208
+
209
+ before do
210
+ Wisper.configure { |c| c.broadcaster(:async, Proc.new { |options| broadcaster.new(options) }) }
211
+ end
212
+
213
+ it 'initializes broadcaster with configured options during subscribe action' do
214
+ expect(broadcaster).to receive(:new).with(async_options)
215
+ publisher.subscribe(listener, async: async_options)
216
+ end
217
+
218
+ it 'invokes configured broadcaster action during event broadcast' do
219
+ publisher.subscribe(listener, async: async_options)
220
+ expect_any_instance_of(broadcaster).to receive('broadcast')
221
+ publisher.send(:broadcast, event_name)
222
+ end
223
+ end
224
+ end
225
+
226
+ it 'returns publisher so methods can be chained' do
227
+ expect(publisher.subscribe(listener, :on => 'so_did_this')).to \
228
+ eq publisher
229
+ end
230
+
231
+ it 'is aliased to .subscribe' do
232
+ expect(publisher).to respond_to(:subscribe)
233
+ end
234
+
235
+ it 'raises a helpful error if trying to pass a block' do
236
+ invalid = ->{
237
+ publisher.subscribe(:success) do
238
+ puts
239
+ end
240
+ }
241
+ expect{ invalid.call }.to raise_error(ArgumentError)
242
+ end
243
+ end
244
+
245
+ describe '.on' do
246
+ it 'returns publisher so methods can be chained' do
247
+ expect(publisher.on('this_thing_happened') {}).to eq publisher
248
+ end
249
+
250
+ it 'raise an error if no events given' do
251
+ expect { publisher.on() {} }.to raise_error(ArgumentError)
252
+ end
253
+
254
+ it 'raises an error of no block given' do
255
+ expect { publisher.on(:something) }.to raise_error(ArgumentError)
256
+ end
257
+
258
+ it 'returns publisher so methods can be chained' do
259
+ expect(publisher.on(:foo) {}).to eq publisher
260
+ end
261
+ end
262
+
263
+ describe '.broadcast' do
264
+
265
+ it 'does not publish events which cannot be responded to' do
266
+ expect(listener).not_to receive(:so_did_this)
267
+ allow(listener).to receive(:respond_to?).and_return(false)
268
+
269
+ publisher.subscribe(listener, :on => 'so_did_this')
270
+
271
+ publisher.send(:broadcast, 'so_did_this')
272
+ end
273
+
274
+ describe ':event argument' do
275
+ it 'is indifferent to string and symbol' do
276
+ expect(listener).to receive(:this_happened).twice
277
+
278
+ publisher.subscribe(listener)
279
+
280
+ publisher.send(:broadcast, 'this_happened')
281
+ publisher.send(:broadcast, :this_happened)
282
+ end
283
+
284
+ it 'is indifferent to dasherized and underscored strings' do
285
+ expect(listener).to receive(:this_happened).twice
286
+
287
+ publisher.subscribe(listener)
288
+
289
+ publisher.send(:broadcast, 'this_happened')
290
+ publisher.send(:broadcast, 'this-happened')
291
+ end
292
+ end
293
+
294
+ it 'returns publisher' do
295
+ expect(publisher.send(:broadcast, :foo)).to eq publisher
296
+ end
297
+
298
+ it 'is not public' do
299
+ expect(publisher).not_to respond_to(:broadcast)
300
+ end
301
+
302
+ it 'is alised as .publish' do
303
+ expect(publisher.method(:broadcast)).to eq publisher.method(:publish)
304
+ end
305
+ end
306
+
307
+ describe '.listeners' do
308
+ it 'returns an immutable collection' do
309
+ expect(publisher.listeners).to be_frozen
310
+ expect { publisher.listeners << listener }.to raise_error(RuntimeError)
311
+ end
312
+
313
+ it 'returns local listeners' do
314
+ publisher.subscribe(listener)
315
+ expect(publisher.listeners).to eq [listener]
316
+ expect(publisher.listeners.size).to eq 1
317
+ end
318
+ end
319
+
320
+ describe '#subscribe' do
321
+ let(:publisher_klass_1) { publisher_class }
322
+ let(:publisher_klass_2) { publisher_class }
323
+
324
+ it 'subscribes listener to all instances of publisher' do
325
+ publisher_klass_1.subscribe(listener)
326
+ expect(listener).to receive(:it_happened).once
327
+ publisher_klass_1.new.send(:broadcast, 'it_happened')
328
+ publisher_klass_2.new.send(:broadcast, 'it_happened')
329
+ end
330
+ end
331
+ end
@@ -0,0 +1,14 @@
1
+ describe Wisper::ObjectRegistration do
2
+
3
+ describe 'broadcaster' do
4
+ it 'defaults to SendBroadcaster' do
5
+ subject = Wisper::ObjectRegistration.new(double('listener'), {})
6
+ expect(subject.broadcaster).to be_instance_of(Wisper::Broadcasters::SendBroadcaster)
7
+ end
8
+
9
+ it 'default is lazily evaluated' do
10
+ expect(Wisper::Broadcasters::SendBroadcaster).to_not receive :new
11
+ Wisper::ObjectRegistration.new(double('listener'), broadcaster: double('DifferentBroadcaster').as_null_object)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,107 @@
1
+ describe Wisper::ValueObjects::Events do
2
+ context 'nil' do
3
+ subject { described_class.new nil }
4
+
5
+ describe '#include?' do
6
+ it 'returns true' do
7
+ expect(subject.include? 'foo').to be_truthy
8
+ expect(subject.include? :bar).to be_truthy
9
+ end
10
+ end
11
+ end
12
+
13
+ context '"foo"' do
14
+ let(:foo) { Class.new(String).new 'foo' }
15
+ subject { described_class.new foo }
16
+
17
+ describe '#include?' do
18
+ it 'returns true for "foo"' do
19
+ expect(subject.include? 'foo').to be_truthy
20
+ end
21
+
22
+ it 'returns true for :foo' do
23
+ expect(subject.include? :foo).to be_truthy
24
+ end
25
+
26
+ it 'returns false otherwise' do
27
+ expect(subject.include? 'bar').to be_falsey
28
+ expect(subject.include? :bar).to be_falsey
29
+ end
30
+ end
31
+ end
32
+
33
+ context ':foo' do
34
+ subject { described_class.new :foo }
35
+
36
+ describe '#include?' do
37
+ it 'returns true for "foo"' do
38
+ expect(subject.include? 'foo').to be_truthy
39
+ end
40
+
41
+ it 'returns true for :foo' do
42
+ expect(subject.include? :foo).to be_truthy
43
+ end
44
+
45
+ it 'returns false otherwise' do
46
+ expect(subject.include? 'bar').to be_falsey
47
+ expect(subject.include? :bar).to be_falsey
48
+ end
49
+ end
50
+ end
51
+
52
+ context '[:foo, "bar"]' do
53
+ subject { described_class.new [:foo, 'bar'] }
54
+
55
+ describe '#include?' do
56
+ it 'returns true for "foo"' do
57
+ expect(subject.include? 'foo').to be_truthy
58
+ end
59
+
60
+ it 'returns true for :foo' do
61
+ expect(subject.include? :foo).to be_truthy
62
+ end
63
+
64
+ it 'returns true for "bar"' do
65
+ expect(subject.include? 'bar').to be_truthy
66
+ end
67
+
68
+ it 'returns true for :bar' do
69
+ expect(subject.include? :bar).to be_truthy
70
+ end
71
+
72
+ it 'returns false otherwise' do
73
+ expect(subject.include? 'baz').to be_falsey
74
+ expect(subject.include? :baz).to be_falsey
75
+ end
76
+ end
77
+ end
78
+
79
+ context 'by /foo/' do
80
+ subject { described_class.new(/foo/) }
81
+
82
+ describe '#include?' do
83
+ it 'returns true for "foo"' do
84
+ expect(subject.include? 'foo').to be_truthy
85
+ end
86
+
87
+ it 'returns true for :foo' do
88
+ expect(subject.include? :foo).to be_truthy
89
+ end
90
+
91
+ it 'returns false otherwise' do
92
+ expect(subject.include? 'bar').to be_falsey
93
+ expect(subject.include? :bar).to be_falsey
94
+ end
95
+ end
96
+ end
97
+
98
+ context 'another class' do
99
+ subject { described_class.new Object.new }
100
+
101
+ describe '#include?' do
102
+ it 'raises ArgumentError' do
103
+ expect { subject.include? 'foo' }.to raise_error(ArgumentError)
104
+ end
105
+ end
106
+ end
107
+ end