wisper 2.0.0 → 3.0.0.rc1

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
- SHA1:
3
- metadata.gz: 62cd6c2948137446de8f7f515bcbebd11c14092d
4
- data.tar.gz: dfbc3b2d28c9023edc9f8308a6c406887c20efed
2
+ SHA256:
3
+ metadata.gz: 051745d94baf4c2ba328de627fde2f5bea42ed5081e2c830520b5ee33bef9c28
4
+ data.tar.gz: b069da8aa786e36d2fe0f51f90030295a9a918f5b8733341515e5c93dca92d7b
5
5
  SHA512:
6
- metadata.gz: d82be662081f118b83ce5f01f87a0760be1bbc9d711163fc76ac303db689ba307ec453afb9edfb374fa1b23e92ab6a684a2c3857580d3f6a29195c75c8d6f754
7
- data.tar.gz: 9395fbd3e0d2b925d31dc06682aa573ad66b0f4ee980533b1671e497ce7f2b8b82019840c7f8eb15c7f52b7bdae73934592ae0e5634729b4ad99f2c068bcae3e
6
+ metadata.gz: 4c84a5d089a16c2588a3cf2d1c83100ff5bc0a7db191c63e47251a4c7e3c8214ddca68a6f2a06e4b155077f88fb3f78e94537c4fe4e0fd68c5ff72cad929a66c
7
+ data.tar.gz: 628455d61676ea7618979695ef6a5babe5654db083162f3c5db9aa5dd833b4ff351b4e27f23730b19b7fb07fb4c440fc51857147657657bde941b37465164a91
checksums.yaml.gz.sig ADDED
@@ -0,0 +1,3 @@
1
+ ��1��eA �%Sz�c�W�E7�^���G����S�؁`�|���M��B�9s7! ���\e
2
+ ;t�b��������0�t��?Ň{ �Ƹ
3
+ �)��W+t_G��7ƞ�&���f�N*;���؟��/ �Ѳu�8h�g���
@@ -0,0 +1,17 @@
1
+ name: Run unit tests
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ os: [ubuntu-latest]
9
+ ruby: ["2.7", "3.0", "3.1", "3.2", "jruby"]
10
+ runs-on: ${{ matrix.os }}
11
+ steps:
12
+ - uses: actions/checkout@v3
13
+ - uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: ${{ matrix.ruby }}
16
+ bundler-cache: true
17
+ - run: bundle exec rspec spec
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  .bundle
5
5
  .config
6
6
  .yardoc
7
+ .tool-versions
7
8
  Gemfile.lock
8
9
  InstalledFiles
9
10
  _yardoc
data/CHANGELOG.md CHANGED
@@ -1,10 +1,41 @@
1
1
  ## HEAD (unreleased)
2
2
 
3
- ## 2.0.0 (7th Mar 2017)
3
+ ## 3.0.0.rc1 (6th July 2023)
4
+
5
+ Authors: Keith Bennett, doits, jstoks, merringtion, thirunjuguna
6
+
7
+ * fix: exclude bin directory from gem #181
8
+ * Adds: Support for Ruby 3.0 keyword arguments
9
+ * Removes: Support for Ruby 2.6 and lower [breaking change]
10
+
11
+ ## 2.0.1 (29th Aug 2019)
4
12
 
5
- Authors: Sergey Mostovoy
13
+ Authors: David Wilkie, hosseintoussi, Maxim Polunin, Tristan
14
+ Harmer, Bartosz Żurkowski, Kris Leech
15
+
16
+ * fix: support nested temporary global listeners
17
+ * docs: add threadsafe notes to README for global and temporary listeners
18
+ * docs: fix spelling mistakes in README
19
+ * fix: safely get signing key in gemspec when HOME is not set
20
+ * adds: add console to aid experiments and REPL driven development
21
+ * adds: Latest Ruby to CI
22
+
23
+ ## 2.0.0 (7th Mar 2017)
6
24
 
7
- * fix: logger raises exception if hash is passed as an argument to a listener
25
+ Authors: Sergey Mostovoy, Andrew Kozin, Kyle Tolle, Martin, Rob Miller, Mike
26
+ Dalto, orthographic-pedant, Drew Ulmer, Mikey Hogarth, Attila Domokos, Josh
27
+ Miltz, Pascal Betz, Vasily Kolesnikov, Julien Letessier, Kris Leech
28
+
29
+ * Fix: logger raises exception if hash is passed as an argument to a listener #133, #136
30
+ * Fix: deprecation warnings #120
31
+ * Doc improvements: #106, #111, #116, #122, #128, #130, #147, #149, #150, #151
32
+ * Adds: Allow configuration of default prefix when using `prefix: true`. #105
33
+ * Adds: Allow unsubscribing of global listeners #118
34
+ * Adds: Helpful error message when passing a block to `#subscribe` of `#on` #125
35
+ * Adds: raise an error message when `#on` is not passed a block #146
36
+ * Adds: Support for JRuby 9.x #148
37
+ * Adds: Support for MRI 2.4.0 #155
38
+ * Refactor specs #126, #131
8
39
 
9
40
  ## 2.0.0.rc1 (17 Dec 2014)
10
41
 
data/CONTRIBUTING.md CHANGED
@@ -28,10 +28,6 @@ appropriate as an extension to Wisper.
28
28
 
29
29
  We also have a [Gitter channel](https://gitter.im/krisleech/wisper) if you wish to discuss your ideas.
30
30
 
31
- ## Backlog / Roadmap
32
-
33
- The backlog for Wisper and related gems is on [Waffle](https://waffle.io/krisleech/wisper).
34
-
35
31
  ## Questions
36
32
 
37
33
  Try the [Wiki](https://github.com/krisleech/wisper/wiki) first, the examples
@@ -43,7 +39,7 @@ Feel free to ping me the URL on [Twitter](https://twitter.com/krisleech).
43
39
 
44
40
  ## Pull requests
45
41
 
46
- * Fork the project, create a new branch from `v1` or `master`.
42
+ * Fork the project, create a new branch `master`.
47
43
  * Squash commits which are related.
48
44
  * Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
49
45
  * Documentation only changes should have `[skip ci]` in the commit message
@@ -58,4 +54,4 @@ not backwards compatible.
58
54
  The `v1` branch is a long lived branch and you should
59
55
  branch from this if you wish to fix an issue in version `~> 1.0`.
60
56
 
61
- The `master` branch will target the next version of Wisper.
57
+ Otherwise branch from `master` branch.
data/Gemfile CHANGED
@@ -2,8 +2,8 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rake', '~> 10.3.0'
6
- gem 'rspec', '~> 3.4.0'
5
+ gem 'rake'
6
+ gem 'rspec'
7
7
  gem 'coveralls', require: false
8
8
 
9
9
  group :extras do
data/README.md CHANGED
@@ -4,22 +4,27 @@
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/wisper.svg)](http://badge.fury.io/rb/wisper)
6
6
  [![Code Climate](https://codeclimate.com/github/krisleech/wisper.svg)](https://codeclimate.com/github/krisleech/wisper)
7
- [![Build Status](https://travis-ci.org/krisleech/wisper.svg?branch=master)](https://travis-ci.org/krisleech/wisper)
7
+ [![Build Status](https://github.com/krisleech/wisper/actions/workflows/test.yml/badge.svg)](https://github.com/krisleech/wisper/actions)
8
8
  [![Coverage Status](https://coveralls.io/repos/krisleech/wisper/badge.svg?branch=master)](https://coveralls.io/r/krisleech/wisper?branch=master)
9
9
 
10
10
  * Decouple core business logic from external concerns in Hexagonal style architectures
11
11
  * Use as an alternative to ActiveRecord callbacks and Observers in Rails apps
12
12
  * Connect objects based on context without permanence
13
- * React to events synchronously or asynchronously
13
+ * Publish events synchronously or asynchronously
14
14
 
15
15
  Note: Wisper was originally extracted from a Rails codebase but is not dependant on Rails.
16
16
 
17
+ Please also see the [Wiki](https://github.com/krisleech/wisper/wiki) for more additional information and articles.
18
+
19
+ **For greenfield applications you might also be interested in
20
+ [WisperNext](https://gitlab.com/kris.leech/wisper_next) and [Ma](https://gitlab.com/kris.leech/ma).**
21
+
17
22
  ## Installation
18
23
 
19
24
  Add this line to your application's Gemfile:
20
25
 
21
26
  ```ruby
22
- gem 'wisper', '2.0.0'
27
+ gem 'wisper', '~3.0'
23
28
  ```
24
29
 
25
30
  ## Usage
@@ -74,7 +79,7 @@ class OrderNotifier
74
79
  def cancel_order_successful(order_id)
75
80
  order = Order.find_by_id(order_id)
76
81
 
77
- # notify someone ...
82
+ # notify someone ...
78
83
  end
79
84
  end
80
85
  ```
@@ -118,8 +123,9 @@ cancel_order.subscribe(OrderNotifier.new, async: true)
118
123
  Wisper has various adapters for asynchronous event handling, please refer to
119
124
  [wisper-celluloid](https://github.com/krisleech/wisper-celluloid),
120
125
  [wisper-sidekiq](https://github.com/krisleech/wisper-sidekiq),
121
- [wisper-activejob](https://github.com/krisleech/wisper-activejob), or
122
- [wisper-que](https://github.com/joevandyk/wisper-que).
126
+ [wisper-activejob](https://github.com/krisleech/wisper-activejob),
127
+ [wisper-que](https://github.com/joevandyk/wisper-que) or
128
+ [wisper-resque](https://github.com/bzurkowski/wisper-resque).
123
129
 
124
130
  Depending on the adapter used the listener may need to be a class instead of an object. In this situation, every method corresponding to events should be declared as a class method, too. For example:
125
131
 
@@ -131,7 +137,7 @@ class OrderNotifier
131
137
  def self.cancel_order_successful(order_id)
132
138
  order = Order.find_by_id(order_id)
133
139
 
134
- # notify someone ...
140
+ # notify someone ...
135
141
  end
136
142
  end
137
143
  ```
@@ -191,9 +197,17 @@ This is useful for cross cutting concerns such as recording statistics, indexing
191
197
  Wisper.subscribe(MyListener.new)
192
198
  ```
193
199
 
194
- In a Rails app you might want to add your global listeners in an initalizer.
200
+ In a Rails app you might want to add your global listeners in an initializer like:
201
+
202
+ ```ruby
203
+ # config/initializers/listeners.rb
204
+ Rails.application.reloader.to_prepare do
205
+ Wisper.subscribe(MyListener.new)
206
+ end
207
+ ```
208
+
209
+ Global listeners are threadsafe. Subscribers will receive events published on all threads.
195
210
 
196
- Global listeners are threadsafe.
197
211
 
198
212
  ### Scoping by publisher class
199
213
 
@@ -231,7 +245,7 @@ listeners.
231
245
 
232
246
  This is useful for capturing events published by objects to which you do not have access in a given context.
233
247
 
234
- Temporary Global Listeners are threadsafe.
248
+ Temporary Global Listeners are threadsafe. Subscribers will receive events published on the same thread.
235
249
 
236
250
  ## Subscribing to selected events
237
251
 
@@ -291,7 +305,7 @@ You could also alias the method within your listener, as such
291
305
 
292
306
  ## Testing
293
307
 
294
- Testing matchers and stubs are in seperate gems.
308
+ Testing matchers and stubs are in separate gems.
295
309
 
296
310
  * [wisper-rspec](https://github.com/krisleech/wisper-rspec)
297
311
  * [wisper-minitest](https://github.com/digitalcuisine/wisper-minitest)
@@ -315,9 +329,7 @@ Got a specific question, try the
315
329
 
316
330
  ## Compatibility
317
331
 
318
- Tested with MRI 2.x, JRuby and Rubinius.
319
-
320
- See the [build status](https://travis-ci.org/krisleech/wisper) for details.
332
+ See the [build status](https://github.com/krisleech/wisper/actions) for details.
321
333
 
322
334
  ## Running Specs
323
335
 
@@ -339,6 +351,7 @@ Please read the [Contributing Guidelines](https://github.com/krisleech/wisper/bl
339
351
 
340
352
  * gem releases are [signed](http://guides.rubygems.org/security/) ([public key](https://github.com/krisleech/wisper/blob/master/gem-public_cert.pem))
341
353
  * commits are GPG signed ([public key](https://pgp.mit.edu/pks/lookup?op=get&search=0x3ABC74851F7CCC88))
354
+ * My [Keybase.io profile](https://keybase.io/krisleech)
342
355
 
343
356
  ## License
344
357
 
data/gem-public_cert.pem CHANGED
@@ -1,21 +1,19 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDAprcmlz
3
- LmxlZWNoMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZFgNj
4
- b20wHhcNMTQxMTA1MTEwMzQ4WhcNMTUxMTA1MTEwMzQ4WjBBMRMwEQYDVQQDDApr
5
- cmlzLmxlZWNoMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
6
- FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtW0/UtrFK/tSm
7
- uq5HnCUkZAQjnSaZ/h1Tby9s30CDJjDUdizPRdLQCplLDAMHFsAfTyD0Mc+Ez8o9
8
- CdTh8EZ4TSf+nokL+SUprpdR6qm6OWU03Ntd+bDCP0+rdqCX82g3N3mnvjR9aD3a
9
- +hd9Fhp0WuEyqTNjQ7IlopeUPDW7eYfSwI4bjfRHxsDR1GuZ3j0npxCAgAIN41WH
10
- MSTTZhdo0vKEiKZEtMMnT6w6fG/c3XIhVVPGnqy5+IZqBL0SYC+WJL3vC6yUBgqB
11
- nrpA/q+b3M69W+q+TkGv0qOnrxln0O7J2pykjoGIxUhhRkiGEldEhy9dxQWubffr
12
- hVJ4F0wLAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
13
- BBSzq+x8mwj0ldvNkvjOl44OJG354jAfBgNVHREEGDAWgRRrcmlzLmxlZWNoQGdt
14
- YWlsLmNvbTAfBgNVHRIEGDAWgRRrcmlzLmxlZWNoQGdtYWlsLmNvbTANBgkqhkiG
15
- 9w0BAQUFAAOCAQEAF5M2Md3DcNCrQrDRDLaIzHaMM+RTgfbpgmZ6tU0iEowES18g
16
- QWQgrAbFuvQRPETJ2gbL5AC35fEqN80nU+3GhgW/bDYhII5D3PGLMorxhFw1JYLI
17
- 0Fd7MCE0sImc2rPybYUdpZ6TxvqgPKp+8CzM8vBUrdYd+dSHXit1piViWBcZcJb+
18
- EL5Ze4IodjkCPAeHvu2MQieieViLyfB4eG1syvfkxvAXCjFHeQoIFP16vVtcljdF
19
- k5cHH/4SGeMuGrSLRsqVvltxVV3AbQAfH8WUos2brjYHsoH5tVPrJ7UcFhzP95oU
20
- pEfFMW42smiNTOXpzG6JoIpA11szEHFT5ZS+UQ==
2
+ MIIC/jCCAeagAwIBAgIBAjANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBprcmlz
3
+ LmxlZWNoL0RDPWdtYWlsL0RDPWNvbTAeFw0yMzA3MDYxNTI0MDdaFw0yNDA3MDUx
4
+ NTI0MDdaMCUxIzAhBgNVBAMMGmtyaXMubGVlY2gvREM9Z21haWwvREM9Y29tMIIB
5
+ IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8gg/iZE6RFkl6GjisJJf9wQl
6
+ 5Tog+mrGqtylNpe9QKeyQS1fKLNR3Cqmv6sT3f3GlU8hhG+802MXmlJo7VyENs+s
7
+ HdMy85fBnYwhS/szhTzBqduXw43RAAGqB0VaHAoHdufTZBkmFSXET5c0/jUqQrPL
8
+ Zxsm2i0NV8cVpvrZlQcsW4Kf97DG1ZFNncS0IfCDqnluh21qMSgVAuiKHGoVKCYG
9
+ Igey486VuuJ8j6axwW3ii8HoBOxjF8Ka/rJ/t4sB9Ql/p6RHR4RhxP5xIS9geCpI
10
+ dsPGnCSleTW1EnXGHLdOdJpVmJXueYJEQJ1Rh/rR+9PeqAT3RA0D/dxSGIVtrQID
11
+ AQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUyupX2gb3
12
+ +isBRwf4/fhgLnh02sYwDQYJKoZIhvcNAQELBQADggEBABdaC++ZW0a2N7oLen4+
13
+ i/4JKdVqa2+BKCQtJdaFsejjGURcudXaY5wqEhgP4ADQ8ANOFF0dCDPPsEn6qMhf
14
+ cNEbRQhMexbe5xQTRVey+20i6vvnJ/kwqnftxAm28Jc3QMMsPk+iM2HZiOsBzICj
15
+ b7k2mPBUf3M6aj8i2fRo6H79PsAN3y7AyU9/KhgkcwHQhr+k7kFpnXKMNlw02ctg
16
+ nzy6xfYdDTzcw47twNBWr53COJTJMgdmlVupROoNjQsJTvuSfUiuy5JFGqS0fijB
17
+ tiGTXMVCk/fOTj04HvFZi/P11KUmQ6HsevziemEgQOnG2lCp+BkyNN0Y4XVW/mno
18
+ x7Y=
21
19
  -----END CERTIFICATE-----
@@ -8,9 +8,9 @@ module Wisper
8
8
  @broadcaster = broadcaster
9
9
  end
10
10
 
11
- def broadcast(listener, publisher, event, args)
12
- @logger.info("[WISPER] #{name(publisher)} published #{event} to #{name(listener)} with #{args_info(args)}")
13
- @broadcaster.broadcast(listener, publisher, event, args)
11
+ def broadcast(listener, publisher, event, *args, **kwargs)
12
+ @logger.info("[WISPER] #{name(publisher)} published #{event} to #{name(listener)} with #{args_info(args)} and #{kwargs_info(kwargs)}")
13
+ @broadcaster.broadcast(listener, publisher, event, *args, **kwargs)
14
14
  end
15
15
 
16
16
  private
@@ -32,6 +32,10 @@ module Wisper
32
32
  arg_string
33
33
  end.join(', ')
34
34
  end
35
+
36
+ def kwargs_info(kwargs)
37
+ kwargs.empty? ? 'no keyword arguments' : "keyword arguments #{kwargs.inspect}"
38
+ end
35
39
  end
36
40
  end
37
41
  end
@@ -1,8 +1,8 @@
1
1
  module Wisper
2
2
  module Broadcasters
3
3
  class SendBroadcaster
4
- def broadcast(listener, publisher, event, args)
5
- listener.public_send(event, *args)
4
+ def broadcast(listener, publisher, event, *args, **kwargs)
5
+ listener.public_send(event, *args, **kwargs)
6
6
  end
7
7
  end
8
8
  end
@@ -13,12 +13,10 @@ module Wisper
13
13
  @mutex = Mutex.new
14
14
  end
15
15
 
16
- def subscribe(*listeners)
17
- options = listeners.last.is_a?(Hash) ? listeners.pop : {}
18
-
16
+ def subscribe(*listeners, **options)
19
17
  with_mutex do
20
18
  listeners.each do |listener|
21
- @registrations << ObjectRegistration.new(listener, options)
19
+ @registrations << ObjectRegistration.new(listener, **options)
22
20
  end
23
21
  end
24
22
  self
@@ -45,8 +43,8 @@ module Wisper
45
43
  with_mutex { @registrations.clear }
46
44
  end
47
45
 
48
- def self.subscribe(*listeners)
49
- instance.subscribe(*listeners)
46
+ def self.subscribe(*listeners, **options)
47
+ instance.subscribe(*listeners, **options)
50
48
  end
51
49
 
52
50
  def self.unsubscribe(*listeners)
@@ -10,9 +10,9 @@ module Wisper
10
10
  # my_publisher.subscribe(MyListener.new)
11
11
  #
12
12
  # @return [self]
13
- def subscribe(listener, options = {})
13
+ def subscribe(listener, **options)
14
14
  raise ArgumentError, "#{__method__} does not take a block, did you mean to call #on instead?" if block_given?
15
- local_registrations << ObjectRegistration.new(listener, options)
15
+ local_registrations << ObjectRegistration.new(listener, **options)
16
16
  self
17
17
  end
18
18
 
@@ -38,9 +38,9 @@ module Wisper
38
38
  # end
39
39
  #
40
40
  # @return [self]
41
- def broadcast(event, *args)
41
+ def broadcast(event, *args, **kwargs)
42
42
  registrations.each do | registration |
43
- registration.broadcast(clean_event(event), self, *args)
43
+ registration.broadcast(clean_event(event), self, *args, **kwargs)
44
44
  end
45
45
  self
46
46
  end
@@ -55,8 +55,8 @@ module Wisper
55
55
  # @example
56
56
  # MyPublisher.subscribe(MyListener.new)
57
57
  #
58
- def subscribe(listener, options = {})
59
- GlobalListeners.subscribe(listener, options.merge(:scope => self))
58
+ def subscribe(listener, **options)
59
+ GlobalListeners.subscribe(listener, **options.merge(:scope => self))
60
60
  end
61
61
  end
62
62
 
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Wisper
4
4
  class BlockRegistration < Registration
5
- def broadcast(event, publisher, *args)
5
+ def broadcast(event, publisher, *args, **kwargs)
6
6
  if should_broadcast?(event)
7
- listener.call(*args)
7
+ listener.call(*args, **kwargs)
8
8
  end
9
9
  end
10
10
  end
@@ -4,18 +4,18 @@ module Wisper
4
4
  class ObjectRegistration < Registration
5
5
  attr_reader :with, :prefix, :allowed_classes, :broadcaster
6
6
 
7
- def initialize(listener, options)
8
- super(listener, options)
7
+ def initialize(listener, **options)
8
+ super(listener, **options)
9
9
  @with = options[:with]
10
10
  @prefix = ValueObjects::Prefix.new options[:prefix]
11
11
  @allowed_classes = Array(options[:scope]).map(&:to_s).to_set
12
12
  @broadcaster = map_broadcaster(options[:async] || options[:broadcaster])
13
13
  end
14
14
 
15
- def broadcast(event, publisher, *args)
15
+ def broadcast(event, publisher, *args, **kwargs)
16
16
  method_to_call = map_event_to_method(event)
17
17
  if should_broadcast?(event) && listener.respond_to?(method_to_call) && publisher_in_scope?(publisher)
18
- broadcaster.broadcast(listener, publisher, method_to_call, args)
18
+ broadcaster.broadcast(listener, publisher, method_to_call, *args, **kwargs)
19
19
  end
20
20
  end
21
21
 
@@ -4,7 +4,7 @@ module Wisper
4
4
  class Registration
5
5
  attr_reader :on, :listener
6
6
 
7
- def initialize(listener, options)
7
+ def initialize(listener, **options)
8
8
  @listener = listener
9
9
  @on = ValueObjects::Events.new options[:on]
10
10
  end
@@ -4,22 +4,22 @@
4
4
 
5
5
  module Wisper
6
6
  class TemporaryListeners
7
-
8
- def self.subscribe(*listeners, &block)
9
- new.subscribe(*listeners, &block)
7
+ def self.subscribe(*listeners, **options, &block)
8
+ new.subscribe(*listeners, **options, &block)
10
9
  end
11
10
 
12
11
  def self.registrations
13
12
  new.registrations
14
13
  end
15
14
 
16
- def subscribe(*listeners, &block)
17
- options = listeners.last.is_a?(Hash) ? listeners.pop : {}
15
+ def subscribe(*listeners, **options, &_block)
16
+ new_registrations = build_registrations(*listeners, **options)
17
+
18
18
  begin
19
- listeners.each { |listener| registrations << ObjectRegistration.new(listener, options) }
19
+ registrations.merge new_registrations
20
20
  yield
21
21
  ensure
22
- clear
22
+ registrations.subtract new_registrations
23
23
  end
24
24
  self
25
25
  end
@@ -30,8 +30,8 @@ module Wisper
30
30
 
31
31
  private
32
32
 
33
- def clear
34
- registrations.clear
33
+ def build_registrations(*listeners, **options)
34
+ listeners.map { |listener| ObjectRegistration.new(listener, **options) }
35
35
  end
36
36
 
37
37
  def key
@@ -1,3 +1,3 @@
1
1
  module Wisper
2
- VERSION = "2.0.0"
2
+ VERSION = "3.0.0.rc1"
3
3
  end
data/lib/wisper.rb CHANGED
@@ -27,11 +27,11 @@ module Wisper
27
27
  # # ..
28
28
  # end
29
29
  #
30
- def self.subscribe(*args, &block)
30
+ def self.subscribe(*args, **kwargs, &block)
31
31
  if block_given?
32
- TemporaryListeners.subscribe(*args, &block)
32
+ TemporaryListeners.subscribe(*args, **kwargs, &block)
33
33
  else
34
- GlobalListeners.subscribe(*args)
34
+ GlobalListeners.subscribe(*args, **kwargs)
35
35
  end
36
36
  end
37
37
 
@@ -15,7 +15,7 @@ describe Wisper do
15
15
 
16
16
  it 'subscribes object to all published events' do
17
17
  listener = double('listener')
18
- expect(listener).to receive(:success).with('hello')
18
+ expect(listener).to receive(:success).with('hello', **{})
19
19
 
20
20
  command = MyCommand.new
21
21
 
@@ -27,8 +27,8 @@ describe Wisper do
27
27
  it 'maps events to different methods' do
28
28
  listener_1 = double('listener')
29
29
  listener_2 = double('listener')
30
- expect(listener_1).to receive(:happy_days).with('hello')
31
- expect(listener_2).to receive(:sad_days).with('world')
30
+ expect(listener_1).to receive(:happy_days).with('hello', **{})
31
+ expect(listener_2).to receive(:sad_days).with('world', **{})
32
32
 
33
33
  command = MyCommand.new
34
34
 
@@ -11,8 +11,8 @@ end
11
11
  describe 'simple publishing' do
12
12
  it 'subscribes listener to events' do
13
13
  listener = double('listener')
14
- expect(listener).to receive(:foo).with instance_of MyPublisher
15
- expect(listener).to receive(:bar).with instance_of MyPublisher
14
+ expect(listener).to receive(:foo).with((instance_of MyPublisher), **{})
15
+ expect(listener).to receive(:bar).with((instance_of MyPublisher), **{})
16
16
 
17
17
  my_publisher = MyPublisher.new
18
18
  my_publisher.subscribe(listener)
@@ -32,6 +32,42 @@ describe Wisper::TemporaryListeners do
32
32
  publisher.instance_eval { broadcast(:failure) }
33
33
  end
34
34
 
35
+ it 'globally subscribes listeners for duration of nested block' do
36
+
37
+ expect(listener_1).to receive(:success)
38
+ expect(listener_1).to receive(:failure)
39
+
40
+ expect(listener_2).to receive(:success)
41
+ expect(listener_2).to_not receive(:failure)
42
+
43
+ Wisper::TemporaryListeners.subscribe(listener_1) do
44
+ Wisper::TemporaryListeners.subscribe(listener_2) do
45
+ publisher.instance_eval { broadcast(:success) }
46
+ end
47
+ publisher.instance_eval { broadcast(:failure) }
48
+ end
49
+ end
50
+
51
+ it 'clears registrations for the block which exits' do
52
+
53
+ # listener_1 is subscribed twice hence it's supposed to get the message twice
54
+ expect(listener_1).to receive(:success).twice
55
+ expect(listener_1).to receive(:failure)
56
+ expect(listener_1).to_not receive(:ignored)
57
+
58
+ expect(listener_2).to receive(:success)
59
+ expect(listener_2).to_not receive(:failure)
60
+ expect(listener_2).to_not receive(:ignored)
61
+
62
+ Wisper::TemporaryListeners.subscribe(listener_1) do
63
+ Wisper::TemporaryListeners.subscribe(listener_1, listener_2) do
64
+ publisher.instance_eval { broadcast(:success) }
65
+ end
66
+ publisher.instance_eval { broadcast(:failure) }
67
+ end
68
+ publisher.instance_eval { broadcast(:ignored) }
69
+ end
70
+
35
71
  it 'is thread safe' do
36
72
  num_threads = 20
37
73
  (1..num_threads).to_a.map do
@@ -8,10 +8,34 @@ module Wisper
8
8
  let(:listener) { double }
9
9
  let(:logger) { double.as_null_object }
10
10
 
11
- it 'broadcasts the event to the listener' do
12
- publisher.subscribe(listener, :broadcaster => LoggerBroadcaster.new(logger, Wisper::Broadcasters::SendBroadcaster.new))
13
- expect(listener).to receive(:it_happened).with(1, 2)
14
- publisher.send(:broadcast, :it_happened, 1, 2)
11
+ context 'with only positional arguments' do
12
+ it 'broadcasts the event to the listener' do
13
+ publisher.subscribe(listener, :broadcaster => LoggerBroadcaster.new(logger, Wisper::Broadcasters::SendBroadcaster.new))
14
+ if RUBY_VERSION < '3.0'
15
+ # Ruby 2.7 receives **{} as a positional argument
16
+ expect(listener).to receive(:it_happened).with(1, 2, {})
17
+ else
18
+ # Ruby 3.0 doesn't pass **empty_hash
19
+ expect(listener).to receive(:it_happened).with(1, 2)
20
+ end
21
+ publisher.send(:broadcast, :it_happened, 1, 2)
22
+ end
23
+ end
24
+
25
+ context 'with only keyword arguments' do
26
+ it 'broadcasts the event to the listener' do
27
+ publisher.subscribe(listener, :broadcaster => LoggerBroadcaster.new(logger, Wisper::Broadcasters::SendBroadcaster.new))
28
+ expect(listener).to receive(:it_happened).with(key: 'value')
29
+ publisher.send(:broadcast, :it_happened, key: 'value')
30
+ end
31
+ end
32
+
33
+ context 'with positional and keyword arguments' do
34
+ it 'broadcasts the event to the listener' do
35
+ publisher.subscribe(listener, :broadcaster => LoggerBroadcaster.new(logger, Wisper::Broadcasters::SendBroadcaster.new))
36
+ expect(listener).to receive(:it_happened).with(1, 2, key: 'value')
37
+ publisher.send(:broadcast, :it_happened, 1, 2, key: 'value')
38
+ end
15
39
  end
16
40
  end
17
41
 
@@ -27,38 +51,41 @@ module Wisper
27
51
  describe '#broadcast' do
28
52
  context 'without arguments' do
29
53
  let(:args) { [] }
54
+ let(:kwargs) { {} }
30
55
 
31
56
  it 'logs published event' do
32
- expect(logger).to receive(:info).with('[WISPER] Publisher#1 published thing_created to Listener#2 with no arguments')
33
- subject.broadcast(listener, publisher, event, args)
57
+ expect(logger).to receive(:info).with('[WISPER] Publisher#1 published thing_created to Listener#2 with no arguments and no keyword arguments')
58
+ subject.broadcast(listener, publisher, event, *args, **kwargs)
34
59
  end
35
60
 
36
61
  it 'delegates broadcast to a given broadcaster' do
37
- expect(broadcaster).to receive(:broadcast).with(listener, publisher, event, args)
38
- subject.broadcast(listener, publisher, event, args)
62
+ expect(broadcaster).to receive(:broadcast).with(listener, publisher, event, *args, **kwargs)
63
+ subject.broadcast(listener, publisher, event, *args, **kwargs)
39
64
  end
40
65
  end
41
66
 
42
67
  context 'with arguments' do
43
68
  let(:args) { [arg_double(id: 3), arg_double(id: 4)] }
69
+ let(:kwargs) { {x: :y} }
44
70
 
45
71
  it 'logs published event and arguments' do
46
- expect(logger).to receive(:info).with('[WISPER] Publisher#1 published thing_created to Listener#2 with Argument#3, Argument#4')
47
- subject.broadcast(listener, publisher, event, args)
72
+ expect(logger).to receive(:info).with("[WISPER] Publisher#1 published thing_created to Listener#2 with Argument#3, Argument#4 and keyword arguments {:x=>:y}")
73
+ subject.broadcast(listener, publisher, event, *args, **kwargs)
48
74
  end
49
75
 
50
76
  it 'delegates broadcast to a given broadcaster' do
51
- expect(broadcaster).to receive(:broadcast).with(listener, publisher, event, args)
52
- subject.broadcast(listener, publisher, event, args)
77
+ expect(broadcaster).to receive(:broadcast).with(listener, publisher, event, *args, **kwargs)
78
+ subject.broadcast(listener, publisher, event, *args, **kwargs)
53
79
  end
54
80
 
55
81
  context 'when argument is a hash' do
56
82
  let(:args) { [hash] }
57
83
  let(:hash) { {key: 'value'} }
84
+ let(:kwargs) { {x: :y} }
58
85
 
59
86
  it 'logs published event and arguments' do
60
- expect(logger).to receive(:info).with("[WISPER] Publisher#1 published thing_created to Listener#2 with Hash##{hash.object_id}: #{hash.inspect}")
61
- subject.broadcast(listener, publisher, event, args)
87
+ expect(logger).to receive(:info).with("[WISPER] Publisher#1 published thing_created to Listener#2 with Hash##{hash.object_id}: #{hash.inspect} and keyword arguments {:x=>:y}")
88
+ subject.broadcast(listener, publisher, event, *args, **kwargs)
62
89
  end
63
90
  end
64
91
 
@@ -67,8 +94,17 @@ module Wisper
67
94
  let(:number) { 10 }
68
95
 
69
96
  it 'logs published event and arguments' do
70
- expect(logger).to receive(:info).with("[WISPER] Publisher#1 published thing_created to Listener#2 with #{number.class.name}##{number.object_id}: 10")
71
- subject.broadcast(listener, publisher, event, args)
97
+ expect(logger).to receive(:info).with("[WISPER] Publisher#1 published thing_created to Listener#2 with #{number.class.name}##{number.object_id}: 10 and keyword arguments {:x=>:y}")
98
+ subject.broadcast(listener, publisher, event, *args, **kwargs)
99
+ end
100
+ end
101
+
102
+ context 'when only keyword arguments are present' do
103
+ let(:args) { [] }
104
+
105
+ it 'logs published event and arguments' do
106
+ expect(logger).to receive(:info).with("[WISPER] Publisher#1 published thing_created to Listener#2 with no arguments and keyword arguments {:x=>:y}")
107
+ subject.broadcast(listener, publisher, event, *args, **kwargs)
72
108
  end
73
109
  end
74
110
  end
@@ -6,20 +6,60 @@ module Wisper
6
6
 
7
7
  describe '#broadcast' do
8
8
  context 'without arguments' do
9
+ it 'sends event to listener without any arguments' do
10
+ if RUBY_VERSION < '3.0'
11
+ expect(listener).to receive(event).with({})
12
+ else
13
+ expect(listener).to receive(event).with(no_args)
14
+ end
15
+ subject.broadcast(listener, anything, event)
16
+ end
17
+ end
18
+
19
+ context 'with empty arguments' do
9
20
  let(:args) { [] }
10
21
 
11
22
  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)
23
+ if RUBY_VERSION < '3.0'
24
+ expect(listener).to receive(event).with({})
25
+ else
26
+ expect(listener).to receive(event).with(no_args)
27
+ end
28
+ subject.broadcast(listener, anything, event, *args)
14
29
  end
15
30
  end
16
31
 
17
32
  context 'with arguments' do
18
- let(:args) { [1,2,3] }
33
+ context 'with only positional arguments' do
34
+ let(:args) { [1,2,3] }
35
+
36
+ it 'sends event to listener with arguments' do
37
+ if RUBY_VERSION < '3.0'
38
+ expect(listener).to receive(event).with(1, 2, 3, {})
39
+ else
40
+ expect(listener).to receive(event).with(1, 2, 3)
41
+ end
42
+ subject.broadcast(listener, anything, event, *args)
43
+ end
44
+ end
45
+
46
+ context 'with only keyword arguments' do
47
+ let(:kwargs) { { key: 'value' } }
48
+
49
+ it 'sends event to listener with arguments' do
50
+ expect(listener).to receive(event).with({key: 'value'})
51
+ subject.broadcast(listener, anything, event, **kwargs)
52
+ end
53
+ end
54
+
55
+ context 'with positional and keyword arguments' do
56
+ let(:args) { [1,2,3] }
57
+ let(:kwargs) { { key: 'value' } }
19
58
 
20
- it 'sends event to listener with arguments' do
21
- expect(listener).to receive(event).with(*args)
22
- subject.broadcast(listener, anything, event, args)
59
+ it 'sends event to listener with arguments' do
60
+ expect(listener).to receive(event).with(1,2,3, {key: 'value'})
61
+ subject.broadcast(listener, anything, event, *args, **kwargs)
62
+ end
23
63
  end
24
64
  end
25
65
  end
@@ -2,7 +2,7 @@ describe Wisper::ObjectRegistration do
2
2
 
3
3
  describe 'broadcaster' do
4
4
  it 'defaults to SendBroadcaster' do
5
- subject = Wisper::ObjectRegistration.new(double('listener'), {})
5
+ subject = Wisper::ObjectRegistration.new(double('listener'))
6
6
  expect(subject.broadcaster).to be_instance_of(Wisper::Broadcasters::SendBroadcaster)
7
7
  end
8
8
 
data/wisper.gemspec CHANGED
@@ -17,15 +17,16 @@ Gem::Specification.new do |gem|
17
17
  gem.homepage = "https://github.com/krisleech/wisper"
18
18
  gem.license = "MIT"
19
19
 
20
- signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
20
+ gem.required_ruby_version = '>= 2.7'
21
+
22
+ signing_key = File.expand_path(ENV['HOME'].to_s + '/.ssh/gem-private_key.pem')
21
23
 
22
24
  if File.exist?(signing_key)
23
25
  gem.signing_key = signing_key
24
26
  gem.cert_chain = ['gem-public_cert.pem']
25
27
  end
26
28
 
27
- gem.files = `git ls-files`.split($/)
28
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
29
+ gem.files = `git ls-files`.split($/).reject { |f| f.split('/').first == 'bin' }
29
30
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
30
31
  gem.require_paths = ["lib"]
31
32
  end
data.tar.gz.sig ADDED
Binary file
metadata CHANGED
@@ -1,14 +1,34 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wisper
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kris Leech
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
- cert_chain: []
11
- date: 2017-03-07 00:00:00.000000000 Z
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIC/jCCAeagAwIBAgIBAjANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBprcmlz
14
+ LmxlZWNoL0RDPWdtYWlsL0RDPWNvbTAeFw0yMzA3MDYxNTI0MDdaFw0yNDA3MDUx
15
+ NTI0MDdaMCUxIzAhBgNVBAMMGmtyaXMubGVlY2gvREM9Z21haWwvREM9Y29tMIIB
16
+ IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8gg/iZE6RFkl6GjisJJf9wQl
17
+ 5Tog+mrGqtylNpe9QKeyQS1fKLNR3Cqmv6sT3f3GlU8hhG+802MXmlJo7VyENs+s
18
+ HdMy85fBnYwhS/szhTzBqduXw43RAAGqB0VaHAoHdufTZBkmFSXET5c0/jUqQrPL
19
+ Zxsm2i0NV8cVpvrZlQcsW4Kf97DG1ZFNncS0IfCDqnluh21qMSgVAuiKHGoVKCYG
20
+ Igey486VuuJ8j6axwW3ii8HoBOxjF8Ka/rJ/t4sB9Ql/p6RHR4RhxP5xIS9geCpI
21
+ dsPGnCSleTW1EnXGHLdOdJpVmJXueYJEQJ1Rh/rR+9PeqAT3RA0D/dxSGIVtrQID
22
+ AQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUyupX2gb3
23
+ +isBRwf4/fhgLnh02sYwDQYJKoZIhvcNAQELBQADggEBABdaC++ZW0a2N7oLen4+
24
+ i/4JKdVqa2+BKCQtJdaFsejjGURcudXaY5wqEhgP4ADQ8ANOFF0dCDPPsEn6qMhf
25
+ cNEbRQhMexbe5xQTRVey+20i6vvnJ/kwqnftxAm28Jc3QMMsPk+iM2HZiOsBzICj
26
+ b7k2mPBUf3M6aj8i2fRo6H79PsAN3y7AyU9/KhgkcwHQhr+k7kFpnXKMNlw02ctg
27
+ nzy6xfYdDTzcw47twNBWr53COJTJMgdmlVupROoNjQsJTvuSfUiuy5JFGqS0fijB
28
+ tiGTXMVCk/fOTj04HvFZi/P11KUmQ6HsevziemEgQOnG2lCp+BkyNN0Y4XVW/mno
29
+ x7Y=
30
+ -----END CERTIFICATE-----
31
+ date: 2023-07-06 00:00:00.000000000 Z
12
32
  dependencies: []
13
33
  description: |2
14
34
  A micro library providing objects with Publish-Subscribe capabilities.
@@ -20,9 +40,9 @@ executables: []
20
40
  extensions: []
21
41
  extra_rdoc_files: []
22
42
  files:
43
+ - ".github/workflows/test.yml"
23
44
  - ".gitignore"
24
45
  - ".rspec"
25
- - ".travis.yml"
26
46
  - CHANGELOG.md
27
47
  - CONTRIBUTING.md
28
48
  - Gemfile
@@ -61,7 +81,7 @@ homepage: https://github.com/krisleech/wisper
61
81
  licenses:
62
82
  - MIT
63
83
  metadata: {}
64
- post_install_message:
84
+ post_install_message:
65
85
  rdoc_options: []
66
86
  require_paths:
67
87
  - lib
@@ -69,16 +89,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
69
89
  requirements:
70
90
  - - ">="
71
91
  - !ruby/object:Gem::Version
72
- version: '0'
92
+ version: '2.7'
73
93
  required_rubygems_version: !ruby/object:Gem::Requirement
74
94
  requirements:
75
- - - ">="
95
+ - - ">"
76
96
  - !ruby/object:Gem::Version
77
- version: '0'
97
+ version: 1.3.1
78
98
  requirements: []
79
- rubyforge_project:
80
- rubygems_version: 2.5.1
81
- signing_key:
99
+ rubygems_version: 3.3.26
100
+ signing_key:
82
101
  specification_version: 4
83
102
  summary: A micro library providing objects with Publish-Subscribe capabilities
84
103
  test_files:
metadata.gz.sig ADDED
@@ -0,0 +1,3 @@
1
+ ]�gg��c�~�i�V��H+M�1��z�d9�ɱ�e4�
2
+ s��V�/R!m/;�'B0<t��wNҠ�Bp��r�hu����d��ȶ���-8)�� O/s�g\��?u��P_!4�g�*"��c�� �ݷtꁊ��E�N�_,Z�[$�
3
+ ����G��.
data/.travis.yml DELETED
@@ -1,22 +0,0 @@
1
- language: ruby
2
- before_install:
3
- - gem update --system
4
- - gem update bundler
5
- bundler_args: --without=extras
6
- script: rspec spec
7
- sudo: false
8
- rvm:
9
- - '2.1.10'
10
- - '2.2.6'
11
- - '2.3.3'
12
- - '2.4.0'
13
- - jruby-9.1.6.0
14
- # - rbx-2
15
- ### ALLOWED FAILURES ###
16
- # see how compatible we are with dev versions, but do not fail the build
17
- - ruby-head
18
- - jruby-head
19
- matrix:
20
- allow_failures:
21
- - rvm: ruby-head
22
- - rvm: jruby-head