wisper_next 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/.gitignore +17 -0
- data/.gitlab-ci.yml +14 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +13 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +150 -0
- data/Rakefile +6 -0
- data/bin/console +7 -0
- data/bin/setup +6 -0
- data/keys/gem-public_cert.pem +20 -0
- data/keys/kris.leech.gpg.public +81 -0
- data/lib/wisper_next.rb +16 -0
- data/lib/wisper_next/cast_to_options.rb +27 -0
- data/lib/wisper_next/events.rb +33 -0
- data/lib/wisper_next/publisher.rb +129 -0
- data/lib/wisper_next/publisher/callable_adapter.rb +27 -0
- data/lib/wisper_next/subscriber.rb +68 -0
- data/lib/wisper_next/subscriber/resolve_method.rb +14 -0
- data/lib/wisper_next/subscriber/send_broadcaster.rb +20 -0
- data/lib/wisper_next/version.rb +3 -0
- data/wisper_next.gemspec +34 -0
- metadata +91 -0
- metadata.gz.sig +2 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '0459c713e32f3b3412978d72d812d8f4e9255aedf613b4ebfbee65df9f8a3c11'
|
4
|
+
data.tar.gz: 2f51f8cc5cab7bb02eb4f90f6259410133b740a074cf560be8ef853c3830c966
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 05f51893bb546d9471f3de2537dbf9857d8649ea0170fef570fb00bd93184a67428e60a534a362dd04218ef554ae4d5ca2a5d7f78d2bc0192b6c399220b782a3
|
7
|
+
data.tar.gz: 0bfa67250f7b639f1c6d43d4ebc15bd1a863cef18e15c80c3c87b17c7f8e467c49a480f770911bc714f902a5ddb3afd9804065fb3723ff5f0fe279a33a570721
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
data/.gitignore
ADDED
data/.gitlab-ci.yml
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.6
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
- Added: Ability for an object to publish events
|
11
|
+
- Added: Ability for an object to map from events to methods
|
12
|
+
- Added: An Event object for creating global pub/sub
|
13
|
+
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at kris.leech@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Kris Leech
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
# WisperNext
|
2
|
+
|
3
|
+
This is the next version of Wisper. Alpha only.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'wisper_next'
|
9
|
+
```
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
### Publishing
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
class MyPublisher
|
17
|
+
include WisperNext.publisher
|
18
|
+
|
19
|
+
def call
|
20
|
+
broadcast(:wow, x: rand(10), y: rand(10), at: Time.now)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
A publisher can broadcast events, the first argument is the event name followed
|
26
|
+
by an optional payload.
|
27
|
+
|
28
|
+
### Listeners
|
29
|
+
|
30
|
+
#### Objects
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class MyListener
|
34
|
+
def on_event(event_name, payload)
|
35
|
+
puts "#{event_name} received with #{payload.inspect}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
We can then subscribe an instance of our listener to the publisher:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
publisher = MyPublisher.new
|
44
|
+
publisher.subscribe(MyListener.new)
|
45
|
+
publisher.call
|
46
|
+
```
|
47
|
+
|
48
|
+
#### Blocks
|
49
|
+
|
50
|
+
Blocks can be subscribed to a specific event name.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
publisher = MyPublisher.new
|
54
|
+
publisher.on(:wow) { |payload| puts(payload) }
|
55
|
+
publisher.call
|
56
|
+
```
|
57
|
+
|
58
|
+
### Enhanced Listeners
|
59
|
+
|
60
|
+
By including `Wisper.subscriber` you can get additional features.
|
61
|
+
|
62
|
+
Firstly instead of `#on_event` you provide a method for every event the
|
63
|
+
listener may receive. For example if our publisher broadcasts a "user_created"
|
64
|
+
event then the listener would provide a `user_created` method which receives
|
65
|
+
the payload.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class MyListener
|
69
|
+
include Wisper.subscriber
|
70
|
+
|
71
|
+
def user_created(payload)
|
72
|
+
#...
|
73
|
+
end
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
If the listener receives an event for which there is no method an exception
|
78
|
+
is raised.
|
79
|
+
|
80
|
+
You can opt-out of this behaviour by setting the `strict` option to false:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
include Wisper.subscriber(strict: false)
|
84
|
+
```
|
85
|
+
|
86
|
+
#### Prefixing broadcast events
|
87
|
+
|
88
|
+
The method called can be prefixed with `on_`:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
class MyListener
|
92
|
+
include Wisper.subscriber(prefix: true)
|
93
|
+
|
94
|
+
def on_user_created(payload)
|
95
|
+
#...
|
96
|
+
end
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
#### Handling Events Asynchronously
|
101
|
+
|
102
|
+
WisperNext has adapters for asynchronous event handling, please refer to
|
103
|
+
<TODO>.
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
include Wisper.subscriber(async: :true)
|
107
|
+
```
|
108
|
+
|
109
|
+
If you are interested in building an async adapter and releasing it as a gem
|
110
|
+
please get in touch.
|
111
|
+
|
112
|
+
#### Passing options
|
113
|
+
|
114
|
+
If the value for an option being passed to subscriber is `true` then you can
|
115
|
+
use a symbol instead to mean the same thing.
|
116
|
+
|
117
|
+
So `WisperNext.subscriber(async: true)` and `WisperNext.subscriber(:async)` are
|
118
|
+
the same. You can pass multiple symbols with any key/values at the end,
|
119
|
+
e.g. `WisperNext.subscriber(:async, :prefix, strict: false)`.
|
120
|
+
|
121
|
+
### Global Subscriptions
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
EVENTS = WisperNext::Events.new
|
125
|
+
|
126
|
+
EVENTS.subscribe(MyListener.new)
|
127
|
+
|
128
|
+
EVENTS.broadcast('user_promoted', user: user)
|
129
|
+
```
|
130
|
+
|
131
|
+
## Security
|
132
|
+
|
133
|
+
* gem releases are [signed]()
|
134
|
+
* commits are GPG [signed]()
|
135
|
+
|
136
|
+
## Development
|
137
|
+
|
138
|
+
## Contributing
|
139
|
+
|
140
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/krisleech/wisper_next.
|
141
|
+
|
142
|
+
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
143
|
+
|
144
|
+
## License
|
145
|
+
|
146
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
147
|
+
|
148
|
+
## Code of Conduct
|
149
|
+
|
150
|
+
Everyone interacting in the WisperNext project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/wisper_next/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDQDCCAiigAwIBAgIBATANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBprcmlz
|
3
|
+
LmxlZWNoL0RDPWdtYWlsL0RDPWNvbTAeFw0xOTEwMTYxNDEzNDVaFw0yMDEwMTUx
|
4
|
+
NDEzNDVaMCUxIzAhBgNVBAMMGmtyaXMubGVlY2gvREM9Z21haWwvREM9Y29tMIIB
|
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
|
+
AQABo3sweTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUyupX2gb3
|
12
|
+
+isBRwf4/fhgLnh02sYwHwYDVR0RBBgwFoEUa3Jpcy5sZWVjaEBnbWFpbC5jb20w
|
13
|
+
HwYDVR0SBBgwFoEUa3Jpcy5sZWVjaEBnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD
|
14
|
+
ggEBAExirH+Py0Wm8+ZzNnx/oyFXSF7V9FClYhPUXwQ5FmvQG+gXRlonO9IR/HL+
|
15
|
+
wGJNFh4B19fpughgQeJKjKJsG2zk2XnSK2yQyyNGdr/9dHXp/TS5sGvX9jFYVeAr
|
16
|
+
0XCWXRBK+IfEqNnbFvPkwZEK62WLnrL50gMs3u2ILmLP5gkO3EoLyezJvIj33CLO
|
17
|
+
HX0Plp0VwiSp8+gEELX5wtdrnJ/UjmrY1lg7szukE+jgOJ++1e7Rzr+woBewIWvF
|
18
|
+
xn3z1ObkNQCnLZMRmzEcSa40T10/AuuuEv32emAgb50u9vz+s4y2lnE9i4mWW7+X
|
19
|
+
5vdRgX7VnzYPA+Lc39kD+AcGIrQ=
|
20
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,81 @@
|
|
1
|
+
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
2
|
+
|
3
|
+
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
|
80
|
+
=reHQ
|
81
|
+
-----END PGP PUBLIC KEY BLOCK-----
|
data/lib/wisper_next.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "wisper_next/version"
|
2
|
+
require 'wisper_next/publisher'
|
3
|
+
require 'wisper_next/subscriber'
|
4
|
+
require 'wisper_next/events'
|
5
|
+
|
6
|
+
module WisperNext
|
7
|
+
class Error < StandardError; end
|
8
|
+
|
9
|
+
def self.publisher
|
10
|
+
Publisher.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.subscriber(*args)
|
14
|
+
Subscriber.new(*args)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module WisperNext
|
2
|
+
# @api private
|
3
|
+
class CastToOptions
|
4
|
+
# @example
|
5
|
+
#
|
6
|
+
# def call(*args)
|
7
|
+
# CastToOptions.call(args)
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# call(:strict, :async: false) # => { strict: true, async: false }
|
11
|
+
#
|
12
|
+
def self.call(arguments)
|
13
|
+
arguments.reduce({}) do |memo, item|
|
14
|
+
case item
|
15
|
+
when Symbol
|
16
|
+
memo[item] = true
|
17
|
+
when Hash
|
18
|
+
memo.merge!(item)
|
19
|
+
else
|
20
|
+
raise(ArgumentError, "Unsupported option: #{item.inspect} (#{item.class.name})")
|
21
|
+
end
|
22
|
+
|
23
|
+
memo
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Global events bus
|
2
|
+
#
|
3
|
+
# @example
|
4
|
+
#
|
5
|
+
# # In application boot/initialization
|
6
|
+
# #
|
7
|
+
# EVENTS = WisperNext::Events.new
|
8
|
+
#
|
9
|
+
# EVENTS.subscribe(MyListener.new)
|
10
|
+
#
|
11
|
+
# # Domain model
|
12
|
+
# #
|
13
|
+
# class MyCommand
|
14
|
+
# def initialize(dependencies = {})
|
15
|
+
# @events = dependencies.fetch(:events) { EVENTS }
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# def call(id)
|
19
|
+
# user = User.find(id)
|
20
|
+
#
|
21
|
+
# if user.promote!
|
22
|
+
# @events.broadcast('user_promoted', user: user)
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
module WisperNext
|
29
|
+
class Events
|
30
|
+
include ::WisperNext::Publisher.new
|
31
|
+
public :broadcast
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module WisperNext
|
2
|
+
|
3
|
+
# Extension to provide objects with subscription and publishing capabilties
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
#
|
7
|
+
# class PromoteUser
|
8
|
+
# include Wisper.publisher
|
9
|
+
#
|
10
|
+
# def call
|
11
|
+
# # ...
|
12
|
+
# broadcast(:user_promoted, user_id: user.id, ts: Time.now)
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# class NotifyUserOfPromotion
|
17
|
+
# def on_event(name, payload)
|
18
|
+
# puts "#{name} => #{payload.inspect}"
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# command = PromoteUser.new
|
23
|
+
# command.subscribe(NotifyUserOfPromotion.new)
|
24
|
+
# command.call
|
25
|
+
#
|
26
|
+
class Publisher < Module
|
27
|
+
def included(descendant)
|
28
|
+
super
|
29
|
+
descendant.send(:include, Methods)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Exception raised when a listener is already subscribed
|
33
|
+
#
|
34
|
+
# @api public
|
35
|
+
#
|
36
|
+
ListenerAlreadyRegisteredError = Class.new(StandardError) do
|
37
|
+
# @api private
|
38
|
+
def initialize(listener)
|
39
|
+
super("listener #{listener.inspect} is already subscribed")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Exception raised when a listener does not have an #on_event method
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
#
|
47
|
+
NoEventHandlerError = Class.new(ArgumentError) do
|
48
|
+
# @api private
|
49
|
+
def initialize(listener)
|
50
|
+
super("listener #{listener.inspect} does not have an #on_event method")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
module Methods
|
55
|
+
# returns true when given listener is already subscribed
|
56
|
+
#
|
57
|
+
# @param [Object] listener
|
58
|
+
#
|
59
|
+
# @return [Boolean]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
#
|
63
|
+
def subscribed?(listener)
|
64
|
+
subscribers.include?(listener)
|
65
|
+
end
|
66
|
+
|
67
|
+
# subscribes given listener
|
68
|
+
#
|
69
|
+
# @param [Object] listener
|
70
|
+
#
|
71
|
+
# @return [Object] self
|
72
|
+
#
|
73
|
+
# @api public
|
74
|
+
#
|
75
|
+
def subscribe(listener)
|
76
|
+
raise ListenerAlreadyRegisteredError.new(listener) if subscribed?(listener)
|
77
|
+
raise NoEventHandlerError.new(listener) unless listener.respond_to?(:on_event)
|
78
|
+
subscribers.push(listener)
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
# subscribes the given block to an event
|
83
|
+
#
|
84
|
+
# @param [String, Symbol] event name
|
85
|
+
#
|
86
|
+
# @return [Object] self
|
87
|
+
#
|
88
|
+
# @api public
|
89
|
+
#
|
90
|
+
def on(name, &block)
|
91
|
+
raise ArgumentError, 'must pass a block' unless block_given?
|
92
|
+
subscribe(CallableAdapter.new(name, block))
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# Broadcast event to all subscribed listeners
|
99
|
+
#
|
100
|
+
# @param [String,Symbol] event name
|
101
|
+
#
|
102
|
+
# @param [Object] optional payload
|
103
|
+
#
|
104
|
+
# @return [Object] self
|
105
|
+
#
|
106
|
+
# @api public
|
107
|
+
#
|
108
|
+
def broadcast(name, payload = nil)
|
109
|
+
subscribers.each do |s|
|
110
|
+
s.public_send(:on_event, name, payload)
|
111
|
+
end
|
112
|
+
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns subscribed listeners
|
117
|
+
#
|
118
|
+
# @return [Array<Object>] collection of subscribers
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
#
|
122
|
+
def subscribers
|
123
|
+
@subscribers ||= []
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
require_relative 'publisher/callable_adapter'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Wraps a callable (typically a proc) so it conforms to the listener interface
|
2
|
+
#
|
3
|
+
# @api private
|
4
|
+
#
|
5
|
+
module WisperNext
|
6
|
+
class Publisher
|
7
|
+
class CallableAdapter
|
8
|
+
attr_reader :event_name, :callable
|
9
|
+
|
10
|
+
def initialize(event_name, callable)
|
11
|
+
@event_name = event_name
|
12
|
+
@callable = callable
|
13
|
+
freeze
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_event(name, payload)
|
17
|
+
if name == @event_name
|
18
|
+
@callable.call(payload)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==(other)
|
23
|
+
other == callable
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'cast_to_options'
|
2
|
+
|
3
|
+
module WisperNext
|
4
|
+
class Subscriber < Module
|
5
|
+
EmptyHash = {}.freeze
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
options = CastToOptions.(args)
|
9
|
+
|
10
|
+
strict = options.fetch(:strict, true)
|
11
|
+
prefix = options.fetch(:prefix, false)
|
12
|
+
broadcaster = resolve_broadcaster(options)
|
13
|
+
|
14
|
+
# maps event to another method
|
15
|
+
#
|
16
|
+
# @param [Symbol] event name
|
17
|
+
#
|
18
|
+
# @return [Object] payload
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
#
|
22
|
+
define_method :on_event do |name, payload|
|
23
|
+
method_name = ResolveMethod.(name, prefix)
|
24
|
+
|
25
|
+
if respond_to?(method_name)
|
26
|
+
broadcaster.call(self, method_name, payload)
|
27
|
+
else
|
28
|
+
if strict
|
29
|
+
raise NoMethodError.new(name)
|
30
|
+
else
|
31
|
+
# no-op
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
NoMethodError = Class.new(StandardError) do
|
38
|
+
# @api private
|
39
|
+
def initialize(event_name)
|
40
|
+
super("No method found for #{event_name} event")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def resolve_broadcaster(options)
|
47
|
+
value = options[:async] || options[:broadcaster] || SendBroadcaster.new
|
48
|
+
|
49
|
+
case value
|
50
|
+
when Hash
|
51
|
+
broadcaster_for(:async, value)
|
52
|
+
when TrueClass
|
53
|
+
broadcaster_for(:async)
|
54
|
+
when Symbol, String
|
55
|
+
broadcaster_for(value)
|
56
|
+
else
|
57
|
+
value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def broadcaster_for(name, opts = EmptyHash)
|
62
|
+
self.class.const_get("#{name.to_s.capitalize}Broadcaster").new(opts)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
require_relative 'subscriber/send_broadcaster'
|
68
|
+
require_relative 'subscriber/resolve_method'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Default synchronous broadcaster which sends event to subscriber
|
2
|
+
#
|
3
|
+
# @api private
|
4
|
+
module WisperNext
|
5
|
+
class Subscriber
|
6
|
+
class SendBroadcaster
|
7
|
+
def initialize(options = {})
|
8
|
+
@public_send = options.fetch(:public_send, true)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(subscriber, event_name, payload)
|
12
|
+
if @public_send
|
13
|
+
subscriber.public_send(event_name, payload)
|
14
|
+
else
|
15
|
+
subscriber.send(event_name, payload)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/wisper_next.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "wisper_next/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "wisper_next"
|
7
|
+
spec.version = WisperNext::VERSION
|
8
|
+
spec.authors = ["Kris Leech"]
|
9
|
+
spec.email = ["kris.leech@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "A micro library providing Ruby objects with Publish-Subscribe capabilities"
|
12
|
+
spec.description = "A micro library providing Ruby objects with Publish-Subscribe capabilities"
|
13
|
+
spec.homepage = "https://gitlab.com/kris.leech/wisper_next"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
18
|
+
spec.metadata["changelog_uri"] = "https://gitlab.com/kris.leech/wisper_next/blob/master/CHANGELOG.md"
|
19
|
+
|
20
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.bindir = "exe"
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
|
28
|
+
signing_key = File.expand_path(ENV['HOME'].to_s + '/.ssh/gem-private_key.pem')
|
29
|
+
|
30
|
+
if File.exist?(signing_key)
|
31
|
+
spec.signing_key = signing_key
|
32
|
+
spec.cert_chain = ['keys/gem-public_cert.pem']
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wisper_next
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kris Leech
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDQDCCAiigAwIBAgIBATANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBprcmlz
|
14
|
+
LmxlZWNoL0RDPWdtYWlsL0RDPWNvbTAeFw0xOTEwMTYxNDEzNDVaFw0yMDEwMTUx
|
15
|
+
NDEzNDVaMCUxIzAhBgNVBAMMGmtyaXMubGVlY2gvREM9Z21haWwvREM9Y29tMIIB
|
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
|
+
AQABo3sweTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUyupX2gb3
|
23
|
+
+isBRwf4/fhgLnh02sYwHwYDVR0RBBgwFoEUa3Jpcy5sZWVjaEBnbWFpbC5jb20w
|
24
|
+
HwYDVR0SBBgwFoEUa3Jpcy5sZWVjaEBnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD
|
25
|
+
ggEBAExirH+Py0Wm8+ZzNnx/oyFXSF7V9FClYhPUXwQ5FmvQG+gXRlonO9IR/HL+
|
26
|
+
wGJNFh4B19fpughgQeJKjKJsG2zk2XnSK2yQyyNGdr/9dHXp/TS5sGvX9jFYVeAr
|
27
|
+
0XCWXRBK+IfEqNnbFvPkwZEK62WLnrL50gMs3u2ILmLP5gkO3EoLyezJvIj33CLO
|
28
|
+
HX0Plp0VwiSp8+gEELX5wtdrnJ/UjmrY1lg7szukE+jgOJ++1e7Rzr+woBewIWvF
|
29
|
+
xn3z1ObkNQCnLZMRmzEcSa40T10/AuuuEv32emAgb50u9vz+s4y2lnE9i4mWW7+X
|
30
|
+
5vdRgX7VnzYPA+Lc39kD+AcGIrQ=
|
31
|
+
-----END CERTIFICATE-----
|
32
|
+
date: 2019-11-01 00:00:00.000000000 Z
|
33
|
+
dependencies: []
|
34
|
+
description: A micro library providing Ruby objects with Publish-Subscribe capabilities
|
35
|
+
email:
|
36
|
+
- kris.leech@gmail.com
|
37
|
+
executables: []
|
38
|
+
extensions: []
|
39
|
+
extra_rdoc_files: []
|
40
|
+
files:
|
41
|
+
- ".gitignore"
|
42
|
+
- ".gitlab-ci.yml"
|
43
|
+
- ".rspec"
|
44
|
+
- ".ruby-version"
|
45
|
+
- CHANGELOG.md
|
46
|
+
- CODE_OF_CONDUCT.md
|
47
|
+
- Gemfile
|
48
|
+
- LICENSE.txt
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- bin/console
|
52
|
+
- bin/setup
|
53
|
+
- keys/gem-public_cert.pem
|
54
|
+
- keys/kris.leech.gpg.public
|
55
|
+
- lib/wisper_next.rb
|
56
|
+
- lib/wisper_next/cast_to_options.rb
|
57
|
+
- lib/wisper_next/events.rb
|
58
|
+
- lib/wisper_next/publisher.rb
|
59
|
+
- lib/wisper_next/publisher/callable_adapter.rb
|
60
|
+
- lib/wisper_next/subscriber.rb
|
61
|
+
- lib/wisper_next/subscriber/resolve_method.rb
|
62
|
+
- lib/wisper_next/subscriber/send_broadcaster.rb
|
63
|
+
- lib/wisper_next/version.rb
|
64
|
+
- wisper_next.gemspec
|
65
|
+
homepage: https://gitlab.com/kris.leech/wisper_next
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata:
|
69
|
+
homepage_uri: https://gitlab.com/kris.leech/wisper_next
|
70
|
+
source_code_uri: https://gitlab.com/kris.leech/wisper_next
|
71
|
+
changelog_uri: https://gitlab.com/kris.leech/wisper_next/blob/master/CHANGELOG.md
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubygems_version: 3.0.6
|
88
|
+
signing_key:
|
89
|
+
specification_version: 4
|
90
|
+
summary: A micro library providing Ruby objects with Publish-Subscribe capabilities
|
91
|
+
test_files: []
|
metadata.gz.sig
ADDED