wisper 1.5.0 → 1.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b7eced3bc4dc2f86f783081e2a3e9f592078deb4
4
- data.tar.gz: 3ce6be1cf3f79319bf8582c86eced10458c2e65f
3
+ metadata.gz: 9c7ce841a6cb6f5ea98bb85ff8c61b74ed007107
4
+ data.tar.gz: 57fd4d7de64134f55724e6bbf152b092312383e3
5
5
  SHA512:
6
- metadata.gz: c680af80578aa1d5170d4334ccda9545be5c0cbcea4ad105e82d5a4e0d5d4ab24a8f835ba1d5e4b9a7c8eb723ca7ff36c2ec0dfdc2da8fcf2e51347a95316280
7
- data.tar.gz: 5b6501efcf475b2a157e8975e6bf7c1ab83118b73313fd3ea7b8e2c5160bb93a301572f6aa30568b6d264c2fc4d779808dfd2b1ffc8395808274c7785712ecc6
6
+ metadata.gz: 37ab485a83aa173857edca6d8fd76911f4cda61bccf6fb3af5adb47e2503dd2a9fe182be75227d3f970d15e64933af16920319d2d47911e5e8d444d18df8ff55
7
+ data.tar.gz: 8cf79f4c35a792c87c97fc83fa6dcac78bfb60a70c3e80b6f052148b31a87618b4538b165ca71ef0a9b820f38bf3ec5f413055fbd0eaef36e0bd44d482ba694f
@@ -7,7 +7,7 @@ rvm:
7
7
  - '2.0.0'
8
8
  - '2.1.2'
9
9
  - jruby
10
- - rbx-2
10
+ #- rbx-2
11
11
  ### ALLOWED FAILURES ###
12
12
  # see how compatible we are with dev versions, but do not fail the build
13
13
  - ruby-head
@@ -1,4 +1,9 @@
1
- ## Unreleased (no breaking changes)
1
+ ## 1.6.0 (25 Oct 2014)
2
+
3
+ Authors: Kris Leech
4
+
5
+ * deprecate: add_listener, add_block_listener and respond_to
6
+ * internal: make method naming more consistent
2
7
 
3
8
  ## 1.5.0 (6th Oct 2014)
4
9
 
data/README.md CHANGED
@@ -1,17 +1,18 @@
1
1
  # Wisper
2
2
 
3
- Wisper is a Ruby library for decoupling and managing the dependencies of your
4
- Ruby objects using Pub/Sub.
3
+ *A micro library providing Ruby objects with Publish-Subscribe capabilities*
5
4
 
6
5
  [![Gem Version](https://badge.fury.io/rb/wisper.png)](http://badge.fury.io/rb/wisper)
7
6
  [![Code Climate](https://codeclimate.com/github/krisleech/wisper.png)](https://codeclimate.com/github/krisleech/wisper)
8
7
  [![Build Status](https://travis-ci.org/krisleech/wisper.png?branch=master)](https://travis-ci.org/krisleech/wisper)
9
8
  [![Coverage Status](https://coveralls.io/repos/krisleech/wisper/badge.png?branch=master)](https://coveralls.io/r/krisleech/wisper?branch=master)
10
9
 
11
- Wisper was extracted from a Rails codebase but is not dependant on Rails.
10
+ * Decouple core business logic from external concerns in Hexagonal style architectures
11
+ * Use as an alternative to ActiveRecord callbacks and Observers in Rails apps
12
+ * Connect objects based on context without permanence
13
+ * React to events synchronously or asynchronously
12
14
 
13
- It is commonly used as an alternative to ActiveRecord callbacks and Observers
14
- to reduce coupling between data and domain layers.
15
+ Note: Wisper was originally extracted from a Rails codebase but is not dependant on Rails.
15
16
 
16
17
  ## Installation
17
18
 
@@ -29,188 +30,131 @@ to subscribed listeners. Listeners subscribe, at runtime, to the publisher.
29
30
  ### Publishing
30
31
 
31
32
  ```ruby
32
- class MyPublisher
33
+ class CancelOrder
33
34
  include Wisper::Publisher
34
-
35
- def do_something
36
- # ...
37
- publish(:done_something)
35
+
36
+ def call(order_id)
37
+ order = Order.find_by_id(order_id)
38
+
39
+ # business logic...
40
+
41
+ if order.cancelled?
42
+ broadcast(:cancel_order_successful, order.id)
43
+ else
44
+ broadcast(:cancel_order_failed, order.id)
45
+ end
38
46
  end
39
47
  end
40
48
  ```
41
49
 
42
- When a publisher broadcasts an event it can pass any number of arguments which
43
- are to be passed on to the listeners.
50
+ When a publisher broadcasts an event it can include number of arguments.
44
51
 
45
- ```ruby
46
- publish(:done_something, 'hello', 'world')
47
- ```
52
+ The `broadcast` method is also aliased as `publish` and `announce`.
53
+
54
+ You can also include `Wisper.publisher` instead of `Wisper::Publisher`.
48
55
 
49
56
  ### Subscribing
50
57
 
51
- #### Listeners
58
+ #### Objects
52
59
 
53
- Any object can be a listener and only receives events it can respond to.
60
+ Any object can be subscribed as a listener.
54
61
 
55
62
  ```ruby
56
- my_publisher = MyPublisher.new
57
- my_publisher.subscribe(MyListener.new)
58
- ```
63
+ cancel_order = CancelOrder.new
59
64
 
60
- #### Blocks
65
+ cancel_order.subscribe(OrderNotifier.new)
61
66
 
62
- Blocks are subscribed to single events only.
67
+ cancel_order.call(order_id)
68
+ ```
69
+
70
+ The listener would need to implement a method for every event it wishes to receive.
63
71
 
64
72
  ```ruby
65
- my_publisher = MyPublisher.new
66
- my_publisher.on(:done_something) do |publisher|
67
- # ...
73
+ class OrderNotifier
74
+ def cancel_order_successful(order_id)
75
+ order = Order.find_by_id(order_id)
76
+
77
+ # notify someone ...
78
+ end
68
79
  end
69
80
  ```
70
81
 
71
- ### Asynchronous Publishing
82
+ #### Blocks
83
+
84
+ Blocks can be subscribed to single events and can be chained.
72
85
 
73
86
  ```ruby
74
- my_publisher.subscribe(MyListener.new, async: true)
75
- ```
87
+ cancel_order = CancelOrder.new
76
88
 
77
- Please refer to
78
- [wisper-celluloid](https://github.com/krisleech/wisper-celluloid) or
79
- [wisper-sidekiq](https://github.com/krisleech/wisper-sidekiq).
89
+ cancel_order.on(:cancel_order_successful) { |order_id| ... }
90
+ .on(:cancel_order_failed) { |order_id| ... }
91
+
92
+ cancel_order.call(order_id)
93
+ ```
80
94
 
81
- ### ActiveRecord
95
+ ### Handling Events Asynchronously
82
96
 
83
97
  ```ruby
84
- class Bid < ActiveRecord::Base
85
- include Wisper::Publisher
98
+ cancel_order.subscribe(OrderNotifier.new, async: true)
99
+ ```
86
100
 
87
- validates :amount, presence: true
101
+ Wisper has various adapters for asynchronous event handling, please refer to
102
+ [wisper-celluloid](https://github.com/krisleech/wisper-celluloid),
103
+ [wisper-sidekiq](https://github.com/krisleech/wisper-sidekiq) or
104
+ [wisper-activejob](https://github.com/krisleech/wisper-activejob).
88
105
 
89
- def commit(_attrs = nil)
90
- assign_attributes(_attrs) if _attrs.present?
91
- if valid?
92
- save!
93
- publish(:create_bid_successful, self)
94
- else
95
- publish(:create_bid_failed, self)
96
- end
97
- end
98
- end
99
- ```
106
+ Depending on the adapter used the listener may need to be a class instead of an object.
100
107
 
101
108
  ### ActionController
102
109
 
103
110
  ```ruby
104
- class BidsController < ApplicationController
105
- def new
106
- @bid = Bid.new
107
- end
108
-
111
+ class CancelOrderController < ApplicationController
112
+
109
113
  def create
110
- @bid = Bid.new(params[:bid])
114
+ cancel_order = CancelOrder.new
111
115
 
112
- @bid.subscribe(PusherListener.new)
113
- @bid.subscribe(ActivityListener.new)
114
- @bid.subscribe(StatisticsListener.new)
116
+ cancel_order.subscribe(OrderMailer, async: true)
117
+ cancel_order.subscribe(ActivityRecorder, async: true)
118
+ cancel_order.subscribe(StatisticsRecorder, async: true)
115
119
 
116
- @bid.on(:create_bid_successful) { |bid| redirect_to bid }
117
- @bid.on(:create_bid_failed) { |bid| render action: :new }
120
+ cancel_order.on(:cancel_order_successful) { |order_id| redirect_to order_path(order_id) }
121
+ cancel_order.on(:cancel_order_failed) { |order_id| render action: :new }
118
122
 
119
- @bid.commit
123
+ cancel_order.call(order_id)
120
124
  end
121
125
  end
122
126
  ```
123
127
 
124
- A full CRUD example is shown in the [Wiki](https://github.com/krisleech/wisper/wiki).
125
-
126
- ### Service/Use Case/Command objects
128
+ ### ActiveRecord
127
129
 
128
- A Service object is useful when an operation is complex, interacts with more
129
- than one model, accesses an external API or would burden a model with too much
130
- responsibility.
130
+ If you wish to publish directly from ActiveRecord models you can broadcast events from callbacks:
131
131
 
132
132
  ```ruby
133
- class PlayerJoiningTeam
133
+ class Order < ActiveRecord::Base
134
134
  include Wisper::Publisher
135
-
136
- attr_reader :player, :team
137
-
138
- def initialize(player, team)
139
- @player = player
140
- @team = team
141
- end
142
-
143
- def execute
144
- membership = Membership.new(player, team)
145
-
146
- if membership.valid?
147
- membership.save!
148
- email_player
149
- assign_first_mission
150
- publish(:player_joining_team_successful, player, team)
151
- else
152
- publish(:player_joining_team_failed, player, team)
153
- end
154
- end
135
+
136
+ after_commit :publish_creation_successful, on: :create
137
+ after_validation :publish_creation_failed, on: :create
155
138
 
156
139
  private
157
140
 
158
- def email_player
159
- # ...
141
+ def publish_creation_successful
142
+ broadcast(:order_creation_successful, self)
160
143
  end
161
144
 
162
- def assign_first_mission
163
- # ...
145
+ def publish_creation_failed
146
+ broadcast(:order_creation_failed, self) if errors.any?
164
147
  end
165
148
  end
166
149
  ```
167
150
 
168
- ### Example listeners
169
-
170
- These are typical app wide listeners which have a method for pretty much every
171
- event which is broadcast.
172
-
173
- ```ruby
174
- class PusherListener
175
- def create_thing_successful(thing)
176
- # ...
177
- end
178
- end
151
+ There are more examples in the [Wiki](https://github.com/krisleech/wisper/wiki).
179
152
 
180
- class ActivityListener
181
- def create_thing_successful(thing)
182
- # ...
183
- end
184
- end
153
+ ## Global Listeners
185
154
 
186
- class StatisticsListener
187
- def create_thing_successful(thing)
188
- # ...
189
- end
190
- end
155
+ Global listeners receive all broadcast events which they can respond to.
191
156
 
192
- class CacheListener
193
- def create_thing_successful(thing)
194
- # ...
195
- end
196
- end
197
-
198
- class IndexingListener
199
- def create_thing_successful(thing)
200
- # ...
201
- end
202
- end
203
- ```
204
-
205
- ## Global listeners
206
-
207
- If you become tired of adding the same listeners to _every_ publisher you can
208
- add listeners globally. They receive all broadcast events which they can respond
209
- to.
210
-
211
- Global listeners should be used with caution, the execution path becomes less
212
- obvious on reading the code and of course you are introducing global state and
213
- 'always on' behaviour. This may not desirable.
157
+ This is useful for cross cutting concerns such as recording statistics, indexing, caching and logging.
214
158
 
215
159
  ```ruby
216
160
  Wisper.subscribe(MyListener.new)
@@ -220,7 +164,7 @@ In a Rails app you might want to add your global listeners in an initalizer.
220
164
 
221
165
  Global listeners are threadsafe.
222
166
 
223
- ### Scoping to publisher class
167
+ ### Scoping by publisher class
224
168
 
225
169
  You might want to globally subscribe a listener to publishers with a certain
226
170
  class.
@@ -232,7 +176,7 @@ Wisper.subscribe(MyListener.new, scope: :MyPublisher)
232
176
  This will subscribe the listener to all instances of `MyPublisher` and its
233
177
  subclasses.
234
178
 
235
- Alternatively you can also do exactly the same with a publisher class:
179
+ Alternatively you can also do exactly the same with a publisher class itself:
236
180
 
237
181
  ```ruby
238
182
  MyPublisher.subscribe(MyListener.new)
@@ -249,8 +193,9 @@ end
249
193
  ```
250
194
 
251
195
  Any events broadcast within the block by any publisher will be sent to the
252
- listeners. This is useful if you have a child object which publishes an event
253
- which is not bubbled down to a parent publisher.
196
+ listeners.
197
+
198
+ This is useful for capturing events published by objects to which you do not have access in a given context.
254
199
 
255
200
  Temporary Global Listeners are threadsafe.
256
201
 
@@ -307,12 +252,8 @@ report_creator.subscribe(MailResponder.new, on: :create_report_failed,
307
252
  with: :failed)
308
253
  ```
309
254
 
310
- ## Chaining subscriptions
311
-
312
- ```ruby
313
- post.on(:success) { |post| redirect_to post }
314
- .on(:failure) { |post| render action: :edit, locals: { post: post } }
315
- ```
255
+ You could also alias the method within your listener, as such
256
+ `alias successful create_report_successful`.
316
257
 
317
258
  ## RSpec
318
259
 
@@ -346,18 +287,19 @@ publisher.execute
346
287
 
347
288
  ### Stubbing publishers
348
289
 
349
- Wisper comes with a method for stubbing event publishers so that you can create
350
- isolation tests that only care about reacting to events.
290
+ You can stub publishers and their events in unit (isolated) tests that only care about reacting to events.
351
291
 
352
292
  Given this piece of code:
353
293
 
354
294
  ```ruby
355
- class CodeThatReactsToEvents
356
- def do_something
295
+ class MyController
296
+ def create
357
297
  publisher = MyPublisher.new
298
+
358
299
  publisher.on(:some_event) do |variable|
359
300
  return "Hello with #{variable}!"
360
301
  end
302
+
361
303
  publisher.execute
362
304
  end
363
305
  end
@@ -368,24 +310,21 @@ You can test it like this:
368
310
  ```ruby
369
311
  require 'wisper/rspec/stub_wisper_publisher'
370
312
 
371
- describe CodeThatReactsToEvents do
313
+ describe MyController do
372
314
  context "on some_event" do
373
315
  before do
374
316
  stub_wisper_publisher("MyPublisher", :execute, :some_event, "foo")
375
317
  end
376
318
 
377
319
  it "renders" do
378
- response = CodeThatReactsToEvents.new.do_something
320
+ response = MyController.new.create
379
321
  expect(response).to eq "Hello with foo!"
380
322
  end
381
323
  end
382
324
  end
383
325
  ```
384
326
 
385
- This becomes important when testing, for example, Rails controllers in
386
- isolation from the business logic. This technique is used at the controller
387
- layer to isolate testing the controller from testing the encapsulated business
388
- logic.
327
+ This is useful when testing Rails controllers in isolation from the business logic.
389
328
 
390
329
  You can use any number of args to pass to the event:
391
330
 
@@ -395,6 +334,15 @@ stub_wisper_publisher("MyPublisher", :execute, :some_event, "foo1", "foo2", ...)
395
334
 
396
335
  See `spec/lib/rspec_extensions_spec.rb` for a runnable example.
397
336
 
337
+ ## Clearing Global Listeners
338
+
339
+ If you use global listeners in non-feature tests you _might_ want to clear them
340
+ in a hook to prevent global subscriptions persisting between tests.
341
+
342
+ ```ruby
343
+ after { Wisper.clear }
344
+ ```
345
+
398
346
  ## Compatibility
399
347
 
400
348
  Tested with MRI 1.9.x, MRI 2.0.0, JRuby (1.9 and 2.0 mode) and Rubinius (1.9
@@ -26,17 +26,36 @@ module Wisper
26
26
  self.subscribe(listener, options)
27
27
  end
28
28
 
29
+ # Examples:
30
+ #
31
+ # Wisper.subscribe(AuditRecorder.new)
32
+ #
33
+ # Wisper.subscribe(AuditRecorder.new, StatsRecorder.new)
34
+ #
35
+ # Wisper.subscribe(AuditRecorder.new, on: 'order_created')
36
+ #
37
+ # Wisper.subscribe(AuditRecorder.new, scope: 'MyPublisher')
38
+ #
39
+ # Wisper.subscribe(AuditRecorder.new, StatsRecorder.new) do
40
+ # # ..
41
+ # end
42
+ #
29
43
  def self.subscribe(*args, &block)
30
44
  if block_given?
31
- TemporaryListeners.with(*args, &block)
45
+ TemporaryListeners.subscribe(*args, &block)
32
46
  else
33
- options = args.last.is_a?(Hash) ? args.pop : {}
34
- args.each do |listener|
35
- GlobalListeners.add(listener, options)
36
- end
47
+ GlobalListeners.subscribe(*args)
37
48
  end
38
49
  end
39
50
 
51
+ def self.publisher
52
+ Publisher
53
+ end
54
+
55
+ def self.clear
56
+ GlobalListeners.clear
57
+ end
58
+
40
59
  def self.configure
41
60
  yield(configuration)
42
61
  end
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+
1
3
  module Wisper
2
4
  class Configuration
3
5
  attr_reader :broadcasters
@@ -1,18 +1,24 @@
1
1
  require 'singleton'
2
2
 
3
+ # Handles global subscriptions
4
+
3
5
  module Wisper
4
6
  class GlobalListeners
5
7
  include Singleton
6
- attr_reader :mutex
7
- private :mutex
8
8
 
9
9
  def initialize
10
10
  @registrations = Set.new
11
11
  @mutex = Mutex.new
12
12
  end
13
13
 
14
- def add(listener, options = {})
15
- with_mutex { @registrations << ObjectRegistration.new(listener, options) }
14
+ def subscribe(*listeners)
15
+ options = listeners.last.is_a?(Hash) ? listeners.pop : {}
16
+
17
+ with_mutex do
18
+ listeners.each do |listener|
19
+ @registrations << ObjectRegistration.new(listener, options)
20
+ end
21
+ end
16
22
  self
17
23
  end
18
24
 
@@ -28,8 +34,8 @@ module Wisper
28
34
  with_mutex { @registrations.clear }
29
35
  end
30
36
 
31
- def self.add(listener, options = {})
32
- instance.add(listener, options)
37
+ def self.subscribe(*listeners)
38
+ instance.subscribe(*listeners)
33
39
  end
34
40
 
35
41
  def self.registrations
@@ -44,16 +50,15 @@ module Wisper
44
50
  instance.clear
45
51
  end
46
52
 
47
- # remain backwards compatible
48
- def self.add_listener(listener, options = {})
49
- warn "[DEPRECATION] use `add` instead of `add_listener`"
50
- add(listener, options)
53
+ def self.add_listener(listener, options = {}) # deprecated
54
+ warn "[DEPRECATION] use `subscribe` instead of `add_listener`"
55
+ subscribe(listener, options)
51
56
  end
52
57
 
53
58
  private
54
59
 
55
60
  def with_mutex
56
- mutex.synchronize { yield }
61
+ @mutex.synchronize { yield }
57
62
  end
58
63
  end
59
64
  end
@@ -4,31 +4,42 @@ module Wisper
4
4
  registrations.map(&:listener).freeze
5
5
  end
6
6
 
7
- def add_listener(listener, options = {})
7
+ def subscribe(listener, options = {})
8
8
  local_registrations << ObjectRegistration.new(listener, options)
9
9
  self
10
10
  end
11
11
 
12
- alias :subscribe :add_listener
12
+ def on(*events, &block)
13
+ raise ArgumentError, 'must give at least one event' if events.empty?
14
+ local_registrations << BlockRegistration.new(block, on: events)
15
+ self
16
+ end
13
17
 
14
18
  def add_block_listener(options = {}, &block)
19
+ warn "[DEPRECATED] use `on` instead of `add_block_listener`"
15
20
  local_registrations << BlockRegistration.new(block, options)
16
21
  self
17
22
  end
18
23
 
19
- # sugar
20
- def respond_to(*events, &block)
21
- add_block_listener({:on => events}, &block)
24
+ def add_listener(listener, options = {})
25
+ warn "[DEPRECATED] use `subscribe` instead of `add_listener`"
26
+ subscribe(listener, options)
22
27
  end
23
28
 
24
- alias :on :respond_to
29
+ def respond_to(*events, &block)
30
+ warn '[DEPRECATED] use `on` instead of `respond_to`'
31
+ on(*events, &block)
32
+ end
25
33
 
26
34
  module ClassMethods
27
- def add_listener(listener, options = {})
28
- GlobalListeners.add(listener, options.merge(:scope => self))
35
+ def subscribe(listener, options = {})
36
+ GlobalListeners.subscribe(listener, options.merge(:scope => self))
29
37
  end
30
38
 
31
- alias :subscribe :add_listener
39
+ def add_listener(listener, options = {})
40
+ warn "[DEPRECATED] use `subscribe` instead of `add_listener`"
41
+ subscribe(listener, options)
42
+ end
32
43
  end
33
44
 
34
45
  private
@@ -1,22 +1,25 @@
1
+ # Handles temporary global subscriptions
2
+
1
3
  module Wisper
2
4
  class TemporaryListeners
3
5
 
4
- def self.with(*listeners, &block)
5
- options = listeners.last.is_a?(Hash) ? listeners.pop : {}
6
- new.with(listeners, options, &block)
6
+ def self.subscribe(*listeners, &block)
7
+ new.subscribe(*listeners, &block)
7
8
  end
8
9
 
9
10
  def self.registrations
10
11
  new.registrations
11
12
  end
12
13
 
13
- def with(listeners, options, &block)
14
+ def subscribe(*listeners, &block)
15
+ options = listeners.last.is_a?(Hash) ? listeners.pop : {}
14
16
  begin
15
- add_listeners(listeners, options)
17
+ listeners.each { |listener| registrations << ObjectRegistration.new(listener, options) }
16
18
  yield
17
19
  ensure
18
20
  clear
19
21
  end
22
+ self
20
23
  end
21
24
 
22
25
  def registrations
@@ -29,14 +32,6 @@ module Wisper
29
32
  registrations.clear
30
33
  end
31
34
 
32
- def add_listeners(listeners, options)
33
- listeners.each { |listener| add_listener(listener, options)}
34
- end
35
-
36
- def add_listener(listener, options)
37
- registrations << ObjectRegistration.new(listener, options)
38
- end
39
-
40
35
  def key
41
36
  '__wisper_temporary_listeners'
42
37
  end
@@ -1,3 +1,3 @@
1
1
  module Wisper
2
- VERSION = "1.5.0"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -3,15 +3,15 @@ describe Wisper::GlobalListeners do
3
3
  let(:local_listener) { double('listener') }
4
4
  let(:publisher) { publisher_class.new }
5
5
 
6
- describe '.add' do
6
+ describe '.subscribe' do
7
7
  it 'adds given listener to every publisher' do
8
- Wisper::GlobalListeners.add(global_listener)
8
+ Wisper::GlobalListeners.subscribe(global_listener)
9
9
  expect(global_listener).to receive(:it_happened)
10
10
  publisher.send(:broadcast, :it_happened)
11
11
  end
12
12
 
13
13
  it 'works with options' do
14
- Wisper::GlobalListeners.add(global_listener, :on => :it_happened,
14
+ Wisper::GlobalListeners.subscribe(global_listener, :on => :it_happened,
15
15
  :with => :woot)
16
16
  expect(global_listener).to receive(:woot).once
17
17
  expect(global_listener).not_to receive(:it_happened_again)
@@ -21,10 +21,10 @@ describe Wisper::GlobalListeners do
21
21
 
22
22
  it 'works along side local listeners' do
23
23
  # global listener
24
- Wisper::GlobalListeners.add(global_listener)
24
+ Wisper::GlobalListeners.subscribe(global_listener)
25
25
 
26
26
  # local listener
27
- publisher.add_listener(local_listener)
27
+ publisher.subscribe(local_listener)
28
28
 
29
29
  expect(global_listener).to receive(:it_happened)
30
30
  expect(local_listener).to receive(:it_happened)
@@ -37,7 +37,7 @@ describe Wisper::GlobalListeners do
37
37
  publisher_2 = publisher_class.new
38
38
  publisher_3 = publisher_class.new
39
39
 
40
- Wisper::GlobalListeners.add(global_listener, :scope => [publisher_1.class,
40
+ Wisper::GlobalListeners.subscribe(global_listener, :scope => [publisher_1.class,
41
41
  publisher_2.class])
42
42
 
43
43
  expect(global_listener).to receive(:it_happened_1).once
@@ -53,7 +53,7 @@ describe Wisper::GlobalListeners do
53
53
  num_threads = 100
54
54
  (1..num_threads).to_a.map do
55
55
  Thread.new do
56
- Wisper::GlobalListeners.add(Object.new)
56
+ Wisper::GlobalListeners.subscribe(Object.new)
57
57
  sleep(rand) # a little chaos
58
58
  end
59
59
  end.each(&:join)
@@ -64,7 +64,7 @@ describe Wisper::GlobalListeners do
64
64
 
65
65
  describe '.listeners' do
66
66
  it 'returns collection of global listeners' do
67
- Wisper::GlobalListeners.add(global_listener)
67
+ Wisper::GlobalListeners.subscribe(global_listener)
68
68
  expect(Wisper::GlobalListeners.listeners).to eq [global_listener]
69
69
  end
70
70
 
@@ -75,17 +75,16 @@ describe Wisper::GlobalListeners do
75
75
  end
76
76
 
77
77
  it '.clear clears all global listeners' do
78
- Wisper::GlobalListeners.add(global_listener)
78
+ Wisper::GlobalListeners.subscribe(global_listener)
79
79
  Wisper::GlobalListeners.clear
80
80
  expect(Wisper::GlobalListeners.listeners).to be_empty
81
81
  end
82
82
 
83
83
  describe 'backwards compatibility' do
84
- it '.add_listener adds a listener' do
84
+ it '.add_listener is aliased to .add' do
85
85
  silence_warnings do
86
+ expect(Wisper::GlobalListeners).to receive(:subscribe)
86
87
  Wisper::GlobalListeners.add_listener(global_listener)
87
- expect(global_listener).to receive(:it_happened)
88
- publisher.send(:broadcast, :it_happened)
89
88
  end
90
89
  end
91
90
  end
@@ -19,20 +19,7 @@ describe Wisper do
19
19
 
20
20
  command = MyCommand.new
21
21
 
22
- command.add_listener(listener)
23
-
24
- command.execute(true)
25
- end
26
-
27
- it 'subscribes block to all published events' do
28
- insider = double('Insider')
29
- expect(insider).to receive(:render).with('hello')
30
-
31
- command = MyCommand.new
32
-
33
- command.add_block_listener do |message|
34
- insider.render(message)
35
- end
22
+ command.subscribe(listener)
36
23
 
37
24
  command.execute(true)
38
25
  end
@@ -45,8 +32,8 @@ describe Wisper do
45
32
 
46
33
  command = MyCommand.new
47
34
 
48
- command.add_listener(listener_1, :on => :success, :with => :happy_days)
49
- command.add_listener(listener_2, :on => :failure, :with => :sad_days)
35
+ command.subscribe(listener_1, :on => :success, :with => :happy_days)
36
+ command.subscribe(listener_2, :on => :failure, :with => :sad_days)
50
37
 
51
38
  command.execute(true)
52
39
  command.execute(false)
@@ -15,7 +15,7 @@ describe 'simple publishing' do
15
15
  expect(listener).to receive(:bar).with instance_of MyPublisher
16
16
 
17
17
  my_publisher = MyPublisher.new
18
- my_publisher.add_listener(listener)
18
+ my_publisher.subscribe(listener)
19
19
  my_publisher.do_something
20
20
  end
21
21
  end
@@ -3,13 +3,13 @@ describe Wisper::TemporaryListeners do
3
3
  let(:listener_2) { double('listener', :to_a => nil) }
4
4
  let(:publisher) { Object.class_eval { include Wisper::Publisher } }
5
5
 
6
- describe '.with' do
6
+ describe '.subscribe' do
7
7
  it 'globally subscribes listener for duration of given block' do
8
8
 
9
9
  expect(listener_1).to receive(:success)
10
10
  expect(listener_1).to_not receive(:failure)
11
11
 
12
- Wisper::TemporaryListeners.with(listener_1) do
12
+ Wisper::TemporaryListeners.subscribe(listener_1) do
13
13
  publisher.instance_eval { broadcast(:success) }
14
14
  end
15
15
 
@@ -24,7 +24,7 @@ describe Wisper::TemporaryListeners do
24
24
  expect(listener_2).to receive(:success)
25
25
  expect(listener_2).to_not receive(:failure)
26
26
 
27
- Wisper::TemporaryListeners.with(listener_1, listener_2) do
27
+ Wisper::TemporaryListeners.subscribe(listener_1, listener_2) do
28
28
  publisher.instance_eval { broadcast(:success) }
29
29
  end
30
30
 
@@ -45,7 +45,7 @@ describe Wisper::TemporaryListeners do
45
45
 
46
46
  it 'ensures registrations are cleared after exception raised in block' do
47
47
  begin
48
- Wisper::TemporaryListeners.with(listener_1) do
48
+ Wisper::TemporaryListeners.subscribe(listener_1) do
49
49
  raise StandardError
50
50
  end
51
51
  rescue StandardError
@@ -54,6 +54,10 @@ describe Wisper::TemporaryListeners do
54
54
  expect(Wisper::TemporaryListeners.registrations.size).to eql 0
55
55
  end
56
56
  end
57
+
58
+ it 'returns self so methods can be chained' do
59
+ expect(Wisper::TemporaryListeners.subscribe {}).to be_an_instance_of(Wisper::TemporaryListeners)
60
+ end
57
61
  end
58
62
 
59
63
  # [1] stubbing `to_a` prevents `Double "listener" received unexpected message
@@ -2,12 +2,12 @@ describe Wisper::Publisher do
2
2
  let(:listener) { double('listener') }
3
3
  let(:publisher) { publisher_class.new }
4
4
 
5
- describe '.add_listener' do
5
+ describe '.subscribe' do
6
6
  it 'subscribes given listener to all published events' do
7
7
  expect(listener).to receive(:this_happened)
8
8
  expect(listener).to receive(:so_did_this)
9
9
 
10
- publisher.add_listener(listener)
10
+ publisher.subscribe(listener)
11
11
 
12
12
  publisher.send(:broadcast, 'this_happened')
13
13
  publisher.send(:broadcast, 'so_did_this')
@@ -21,7 +21,7 @@ describe Wisper::Publisher do
21
21
 
22
22
  expect(listener).to respond_to(:so_did_this)
23
23
 
24
- publisher.add_listener(listener, :on => 'this_happened')
24
+ publisher.subscribe(listener, :on => 'this_happened')
25
25
 
26
26
  publisher.send(:broadcast, 'this_happened')
27
27
  publisher.send(:broadcast, 'so_did_this')
@@ -35,7 +35,7 @@ describe Wisper::Publisher do
35
35
 
36
36
  expect(listener).to respond_to(:so_did_this)
37
37
 
38
- publisher.add_listener(listener, :on => ['this_happened', 'and_this'])
38
+ publisher.subscribe(listener, :on => ['this_happened', 'and_this'])
39
39
 
40
40
  publisher.send(:broadcast, 'this_happened')
41
41
  publisher.send(:broadcast, 'so_did_this')
@@ -47,7 +47,7 @@ describe Wisper::Publisher do
47
47
  it 'sets method to call listener with on event' do
48
48
  expect(listener).to receive(:different_method).twice
49
49
 
50
- publisher.add_listener(listener, :with => :different_method)
50
+ publisher.subscribe(listener, :with => :different_method)
51
51
 
52
52
  publisher.send(:broadcast, 'this_happened')
53
53
  publisher.send(:broadcast, 'so_did_this')
@@ -59,7 +59,7 @@ describe Wisper::Publisher do
59
59
  expect(listener).to receive(:after_it_happened)
60
60
  expect(listener).not_to receive(:it_happened)
61
61
 
62
- publisher.add_listener(listener, :prefix => :after)
62
+ publisher.subscribe(listener, :prefix => :after)
63
63
 
64
64
  publisher.send(:broadcast, 'it_happened')
65
65
  end
@@ -68,7 +68,7 @@ describe Wisper::Publisher do
68
68
  expect(listener).to receive(:on_it_happened)
69
69
  expect(listener).not_to receive(:it_happened)
70
70
 
71
- publisher.add_listener(listener, :prefix => true)
71
+ publisher.subscribe(listener, :prefix => true)
72
72
 
73
73
  publisher.send(:broadcast, 'it_happened')
74
74
  end
@@ -83,16 +83,16 @@ describe Wisper::Publisher do
83
83
  it 'scopes listener to given class' do
84
84
  expect(listener_1).to receive(:it_happended)
85
85
  expect(listener_2).not_to receive(:it_happended)
86
- publisher.add_listener(listener_1, :scope => publisher.class)
87
- publisher.add_listener(listener_2, :scope => Class.new)
86
+ publisher.subscribe(listener_1, :scope => publisher.class)
87
+ publisher.subscribe(listener_2, :scope => Class.new)
88
88
  publisher.send(:broadcast, 'it_happended')
89
89
  end
90
90
 
91
91
  it 'scopes listener to given class string' do
92
92
  expect(listener_1).to receive(:it_happended)
93
93
  expect(listener_2).not_to receive(:it_happended)
94
- publisher.add_listener(listener_1, :scope => publisher.class.to_s)
95
- publisher.add_listener(listener_2, :scope => Class.new.to_s)
94
+ publisher.subscribe(listener_1, :scope => publisher.class.to_s)
95
+ publisher.subscribe(listener_2, :scope => Class.new.to_s)
96
96
  publisher.send(:broadcast, 'it_happended')
97
97
  end
98
98
 
@@ -105,7 +105,7 @@ describe Wisper::Publisher do
105
105
 
106
106
  publisher = publisher_sub_klass.new
107
107
 
108
- publisher.add_listener(listener, :scope => publisher_super_klass)
108
+ publisher.subscribe(listener, :scope => publisher_super_klass)
109
109
  publisher.send(:broadcast, 'it_happended')
110
110
  end
111
111
  end
@@ -171,7 +171,7 @@ describe Wisper::Publisher do
171
171
  end
172
172
 
173
173
  it 'returns publisher so methods can be chained' do
174
- expect(publisher.add_listener(listener, :on => 'so_did_this')).to \
174
+ expect(publisher.subscribe(listener, :on => 'so_did_this')).to \
175
175
  eq publisher
176
176
  end
177
177
 
@@ -180,14 +180,61 @@ describe Wisper::Publisher do
180
180
  end
181
181
  end
182
182
 
183
+ describe '.on' do
184
+ let(:insider) { double('insider') }
185
+
186
+ it 'subscribes block to given event' do
187
+ expect(insider).to receive(:yes).once
188
+
189
+ publisher.on(:something_happened) do
190
+ insider.yes
191
+ end
192
+
193
+ publisher.send(:broadcast, :something_happened)
194
+ publisher.send(:broadcast, :and_so_did_this)
195
+ end
196
+
197
+ it 'subscribes block to given events' do
198
+ expect(insider).to receive(:yes).twice
199
+
200
+ publisher.on(:something_happened, :and_so_did_this) do
201
+ insider.yes
202
+ end
203
+
204
+ publisher.send(:broadcast, :something_happened)
205
+ publisher.send(:broadcast, :and_so_did_this)
206
+ end
207
+
208
+ it 'raise an error if no events given' do
209
+ expect { publisher.on() {} }.to raise_error(ArgumentError)
210
+ end
211
+
212
+ it 'returns publisher so methods can be chained' do
213
+ expect(publisher.on(:foo) {}).to eq publisher
214
+ end
215
+ end
216
+
217
+ # @deprecated
218
+ describe '.add_listener' do
219
+ it 'is aliased to .subscribe' do
220
+ expect(publisher).to receive(:subscribe)
221
+ silence_warnings do
222
+ publisher.add_listener(listener)
223
+ end
224
+ end
225
+ end
226
+
227
+ # @deprecated
183
228
  describe '.add_block_listener' do
184
229
  let(:insider) { double('insider') }
185
230
 
186
231
  it 'subscribes given block to all events' do
187
232
  expect(insider).to receive(:it_happened).twice
188
233
 
189
- publisher.add_block_listener do
190
- insider.it_happened
234
+ silence_warnings do
235
+ publisher.add_block_listener do
236
+ insider.it_happened
237
+ end
191
238
  end
192
239
 
193
240
  publisher.send(:broadcast, 'something_happened')
@@ -198,8 +245,10 @@ describe Wisper::Publisher do
198
245
  it '.add_block_listener subscribes block to an event' do
199
246
  expect(insider).not_to receive(:it_happened).once
200
247
 
201
- publisher.add_block_listener(:on => 'something_happened') do
202
- insider.it_happened
248
+ silence_warnings do
249
+ publisher.add_block_listener(:on => 'something_happened') do
250
+ insider.it_happened
251
+ end
203
252
  end
204
253
 
205
254
  publisher.send(:broadcast, 'something_happened')
@@ -209,9 +258,11 @@ describe Wisper::Publisher do
209
258
  it '.add_block_listener subscribes block to all listed events' do
210
259
  expect(insider).to receive(:it_happened).twice
211
260
 
212
- publisher.add_block_listener(
213
- :on => ['something_happened', 'and_so_did_this']) do
214
- insider.it_happened
261
+ silence_warnings do
262
+ publisher.add_block_listener(
263
+ :on => ['something_happened', 'and_so_did_this']) do
264
+ insider.it_happened
265
+ end
215
266
  end
216
267
 
217
268
  publisher.send(:broadcast, 'something_happened')
@@ -221,34 +272,20 @@ describe Wisper::Publisher do
221
272
  end
222
273
 
223
274
  it 'returns publisher so methods can be chained' do
224
- expect(publisher.add_block_listener(:on => 'this_thing_happened') do
225
- end).to eq publisher
226
- end
227
- end
228
-
229
- describe '.on (alternative block syntax)' do
230
- let(:insider) { double('insider') }
231
-
232
- it 'subscribes given block to an event' do
233
- expect(insider).to receive(:it_happened)
234
-
235
- publisher.on(:something_happened) do
236
- insider.it_happened
275
+ silence_warnings do
276
+ expect(publisher.add_block_listener(:on => 'this_thing_happened') do
277
+ end).to eq publisher
237
278
  end
238
-
239
- publisher.send(:broadcast, 'something_happened')
240
279
  end
280
+ end
241
281
 
242
- it 'subscribes given block to multiple events' do
243
- expect(insider).to receive(:it_happened).twice
244
-
245
- publisher.on(:something_happened, :and_so_did_this) do
246
- insider.it_happened
282
+ # @deprecated
283
+ describe '.respond_to (alternative block syntax)' do
284
+ it 'delegates to .on' do
285
+ expect(publisher).to receive(:on).with(:foobar)
286
+ silence_warnings do
287
+ publisher.respond_to(:foobar) { }
247
288
  end
248
-
249
- publisher.send(:broadcast, 'something_happened')
250
- publisher.send(:broadcast, 'and_so_did_this')
251
- publisher.send(:broadcast, 'but_not_this')
252
289
  end
253
290
  end
254
291
 
@@ -257,7 +294,7 @@ describe Wisper::Publisher do
257
294
  expect(listener).not_to receive(:so_did_this)
258
295
  allow(listener).to receive(:respond_to?).and_return(false)
259
296
 
260
- publisher.add_listener(listener, :on => 'so_did_this')
297
+ publisher.subscribe(listener, :on => 'so_did_this')
261
298
 
262
299
  publisher.send(:broadcast, 'so_did_this')
263
300
  end
@@ -266,7 +303,7 @@ describe Wisper::Publisher do
266
303
  it 'is indifferent to string and symbol' do
267
304
  expect(listener).to receive(:this_happened).twice
268
305
 
269
- publisher.add_listener(listener)
306
+ publisher.subscribe(listener)
270
307
 
271
308
  publisher.send(:broadcast, 'this_happened')
272
309
  publisher.send(:broadcast, :this_happened)
@@ -275,7 +312,7 @@ describe Wisper::Publisher do
275
312
  it 'is indifferent to dasherized and underscored strings' do
276
313
  expect(listener).to receive(:this_happened).twice
277
314
 
278
- publisher.add_listener(listener)
315
+ publisher.subscribe(listener)
279
316
 
280
317
  publisher.send(:broadcast, 'this_happened')
281
318
  publisher.send(:broadcast, 'this-happened')
@@ -290,25 +327,30 @@ describe Wisper::Publisher do
290
327
  end
291
328
 
292
329
  it 'returns local listeners' do
293
- publisher.add_listener(listener)
330
+ publisher.subscribe(listener)
294
331
  expect(publisher.listeners).to eq [listener]
295
332
  expect(publisher.listeners.size).to eq 1
296
333
  end
297
334
  end
298
335
 
299
- describe '#add_listener' do
336
+ describe '#subscribe' do
300
337
  let(:publisher_klass_1) { publisher_class }
301
338
  let(:publisher_klass_2) { publisher_class }
302
339
 
303
- it 'subscribes listeners to all instances of publisher' do
304
- publisher_klass_1.add_listener(listener)
340
+ it 'subscribes listener to all instances of publisher' do
341
+ publisher_klass_1.subscribe(listener)
305
342
  expect(listener).to receive(:it_happened).once
306
343
  publisher_klass_1.new.send(:broadcast, 'it_happened')
307
344
  publisher_klass_2.new.send(:broadcast, 'it_happened')
308
345
  end
346
+ end
309
347
 
348
+ describe '#add_listener' do # deprecated
310
349
  it 'is aliased to #subscribe' do
311
- expect(publisher_klass_1).to respond_to(:subscribe)
350
+ expect(publisher).to receive(:subscribe)
351
+ silence_warnings do
352
+ publisher.add_listener(listener)
353
+ end
312
354
  end
313
355
  end
314
356
  end
@@ -78,6 +78,16 @@ describe Wisper do
78
78
  end
79
79
  end
80
80
 
81
+ it '.publisher returns the Publisher module' do
82
+ expect(Wisper.publisher).to eq Wisper::Publisher
83
+ end
84
+
85
+ it '.clear clears all global listeners' do
86
+ 10.times { Wisper.subscribe(double) }
87
+ Wisper.clear
88
+ expect(Wisper::GlobalListeners.listeners).to be_empty
89
+ end
90
+
81
91
  it '.configuration returns configuration' do
82
92
  expect(Wisper.configuration).to be_an_instance_of(Wisper::Configuration)
83
93
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wisper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kris Leech
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-06 00:00:00.000000000 Z
11
+ date: 2014-10-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: pub/sub for Ruby objects
14
14
  email: