wisper 1.5.0 → 1.6.0

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