wisper 1.4.0 → 1.5.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
- ---
2
- SHA1:
3
- metadata.gz: c905b4f311d82eadf52d61a11a0515109335d592
4
- data.tar.gz: 9be198a62d94f3c8fbc46253cd109a438d857206
5
- SHA512:
6
- metadata.gz: 44ddd88e30752991d23dafa678086611a05f0206f152066b18b0283b0eb9462728b06be38f46fdf08caba0ead38919e88530ad1566cc2d8d92d751813805e851
7
- data.tar.gz: d7ee6c49e1c6320596e00f1a79624e8ea3bb7f413b4fa76348415e3ff40225772522d1f861946dd83aaf1fbff0e86a89eccb72dd0dca1ca91843a72d78a3193a
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b7eced3bc4dc2f86f783081e2a3e9f592078deb4
4
+ data.tar.gz: 3ce6be1cf3f79319bf8582c86eced10458c2e65f
5
+ SHA512:
6
+ metadata.gz: c680af80578aa1d5170d4334ccda9545be5c0cbcea4ad105e82d5a4e0d5d4ab24a8f835ba1d5e4b9a7c8eb723ca7ff36c2ec0dfdc2da8fcf2e51347a95316280
7
+ data.tar.gz: 5b6501efcf475b2a157e8975e6bf7c1ab83118b73313fd3ea7b8e2c5160bb93a301572f6aa30568b6d264c2fc4d779808dfd2b1ffc8395808274c7785712ecc6
data/.rspec CHANGED
@@ -1,2 +1,4 @@
1
1
  --color
2
2
  --format progress
3
+ --require spec_helper
4
+ --warnings
@@ -0,0 +1 @@
1
+ 2.1.2
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
- bundler_args: --without=metrics
2
+ bundler_args: --without=extras
3
3
  script: rspec spec
4
4
  rvm:
5
5
  - '1.9.2'
@@ -1,6 +1,13 @@
1
1
  ## Unreleased (no breaking changes)
2
2
 
3
- None
3
+ ## 1.5.0 (6th Oct 2014)
4
+
5
+ Authors: Kris Leech
6
+
7
+ * feature: allow events to be published asynchronously
8
+ * feature: broadcasting of events is plugable and configurable
9
+ * feature: broadcasters can be aliased via a symbol
10
+ * feature: logging broadcaster
4
11
 
5
12
  ## 1.4.0 (8th Sept 2014)
6
13
 
data/Gemfile CHANGED
@@ -4,13 +4,11 @@ gemspec
4
4
 
5
5
  gem 'rubysl', '~> 2.0', :platforms => :rbx
6
6
 
7
+ gem 'rake', '~> 10.3.0'
8
+ gem 'rspec', '~> 3.0.0'
7
9
  gem 'coveralls', require: false
8
10
 
9
- group :metrics do
11
+ group :extras do
10
12
  gem 'flay'
11
- end
12
-
13
- group :development do
14
- gem 'rake', '~> 10.3.0'
15
- gem 'rspec', '~> 3.0.0'
13
+ gem 'pry'
16
14
  end
data/README.md CHANGED
@@ -70,7 +70,13 @@ end
70
70
 
71
71
  ### Asynchronous Publishing
72
72
 
73
- Please refer to the [wisper-async](https://github.com/krisleech/wisper-async) gem.
73
+ ```ruby
74
+ my_publisher.subscribe(MyListener.new, async: true)
75
+ ```
76
+
77
+ Please refer to
78
+ [wisper-celluloid](https://github.com/krisleech/wisper-celluloid) or
79
+ [wisper-sidekiq](https://github.com/krisleech/wisper-sidekiq).
74
80
 
75
81
  ### ActiveRecord
76
82
 
@@ -1,11 +1,14 @@
1
1
  require 'set'
2
2
  require 'wisper/version'
3
+ require 'wisper/configuration'
3
4
  require 'wisper/publisher'
4
5
  require 'wisper/registration/registration'
5
6
  require 'wisper/registration/object'
6
7
  require 'wisper/registration/block'
7
8
  require 'wisper/global_listeners'
8
9
  require 'wisper/temporary_listeners'
10
+ require 'wisper/broadcasters/send_broadcaster'
11
+ require 'wisper/broadcasters/logger_broadcaster'
9
12
 
10
13
  module Wisper
11
14
  def self.included(base)
@@ -33,4 +36,20 @@ module Wisper
33
36
  end
34
37
  end
35
38
  end
39
+
40
+ def self.configure
41
+ yield(configuration)
42
+ end
43
+
44
+ def self.configuration
45
+ @configuration ||= Configuration.new
46
+ end
47
+
48
+ def self.setup
49
+ configure do |config|
50
+ config.broadcaster(:default, Broadcasters::SendBroadcaster.new)
51
+ end
52
+ end
36
53
  end
54
+
55
+ Wisper.setup
@@ -0,0 +1,31 @@
1
+ # Provides a way of wrapping another broadcaster with logging
2
+
3
+ module Wisper
4
+ module Broadcasters
5
+ class LoggerBroadcaster
6
+ def initialize(logger, broadcaster)
7
+ @logger = logger
8
+ @broadcaster = broadcaster
9
+ end
10
+
11
+ def broadcast(subscriber, publisher, event, args)
12
+ @logger.info("[WISPER] #{name(publisher)} published #{event} to #{name(subscriber)} with #{arg_names(args)}")
13
+ @broadcaster.broadcast(subscriber, publisher, event, args)
14
+ end
15
+
16
+ private
17
+
18
+ def name(object)
19
+ id_method = %w(id uuid key object_id).find { |id_method| object.respond_to?(id_method) }
20
+ id = object.send(id_method)
21
+ class_name = object.class == Class ? object.name : object.class.name
22
+ "#{class_name}##{id}"
23
+ end
24
+
25
+ def arg_names(args)
26
+ return 'no arguments' if args.empty?
27
+ args.map { |arg| name(arg) }.join(', ')
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ module Wisper
2
+ module Broadcasters
3
+ class SendBroadcaster
4
+ def broadcast(subscriber, publisher, event, args)
5
+ subscriber.public_send(event, *args)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ module Wisper
2
+ class Configuration
3
+ attr_reader :broadcasters
4
+
5
+ def initialize
6
+ @broadcasters = Broadcasters.new
7
+ end
8
+
9
+ def broadcaster(name, broadcaster)
10
+ @broadcasters[name] = broadcaster
11
+ end
12
+
13
+ class Broadcasters
14
+ extend Forwardable
15
+
16
+ def_delegators :@data, :fetch, :[], :[]=, :empty?, :include?, :clear
17
+
18
+ def initialize
19
+ @data = {}
20
+ end
21
+
22
+ def fetch(key)
23
+ raise KeyError, "broadcaster not found for #{key}" unless include?(key)
24
+ @data[key]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,19 +1,19 @@
1
1
  module Wisper
2
2
  class ObjectRegistration < Registration
3
- attr_reader :with, :prefix, :allowed_classes
3
+ attr_reader :with, :prefix, :allowed_classes, :broadcaster
4
4
 
5
5
  def initialize(listener, options)
6
6
  super(listener, options)
7
7
  @with = options[:with]
8
8
  @prefix = stringify_prefix(options[:prefix])
9
9
  @allowed_classes = Array(options[:scope]).map(&:to_s).to_set
10
- fail_on_async if options.has_key?(:async)
10
+ @broadcaster = map_broadcaster(options[:async] || options[:broadcaster])
11
11
  end
12
12
 
13
13
  def broadcast(event, publisher, *args)
14
14
  method_to_call = map_event_to_method(event)
15
15
  if should_broadcast?(event) && listener.respond_to?(method_to_call) && publisher_in_scope?(publisher)
16
- listener.public_send(method_to_call, *args)
16
+ broadcaster.broadcast(listener, publisher, method_to_call, args)
17
17
  end
18
18
  end
19
19
 
@@ -42,8 +42,15 @@ module Wisper
42
42
  'on'
43
43
  end
44
44
 
45
- def fail_on_async
46
- raise 'The async feature has been moved to the wisper-async gem'
45
+ def map_broadcaster(value)
46
+ return value if value.respond_to?(:broadcast)
47
+ value = :async if value == true
48
+ value = :default if value == nil
49
+ configuration.broadcasters.fetch(value)
50
+ end
51
+
52
+ def configuration
53
+ Wisper.configuration
47
54
  end
48
55
  end
49
56
  end
@@ -1,3 +1,3 @@
1
1
  module Wisper
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Wisper::GlobalListeners do
4
2
  let(:global_listener) { double('listener') }
5
3
  let(:local_listener) { double('listener') }
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  # Example
4
2
  class MyCommand
5
3
  include Wisper::Publisher
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  class MyPublisher
4
2
  include Wisper::Publisher
5
3
 
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Wisper::TemporaryListeners do
4
2
  let(:listener_1) { double('listener', :to_a => nil) } # [1]
5
3
  let(:listener_2) { double('listener', :to_a => nil) }
@@ -0,0 +1,73 @@
1
+ module Wisper
2
+ module Broadcasters
3
+
4
+ describe LoggerBroadcaster do
5
+
6
+ describe 'integration tests:' do
7
+ let(:publisher) { publisher_class.new }
8
+ let(:listener) { double }
9
+ let(:logger) { double.as_null_object }
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)
15
+ end
16
+ end
17
+
18
+ describe 'unit tests:' do
19
+ let(:publisher) { classy_double('Publisher', id: 1) }
20
+ let(:subscriber) { classy_double('Subscriber', id: 2) }
21
+ let(:logger) { double('Logger').as_null_object }
22
+ let(:broadcaster) { double('Broadcaster').as_null_object }
23
+ let(:event) { 'thing_created' }
24
+
25
+ subject { LoggerBroadcaster.new(logger, broadcaster) }
26
+
27
+ describe '#broadcast' do
28
+ context 'without arguments' do
29
+ let(:args) { [] }
30
+
31
+ it 'logs publised event' do
32
+ expect(logger).to receive(:info).with('[WISPER] Publisher#1 published thing_created to Subscriber#2 with no arguments')
33
+ subject.broadcast(subscriber, publisher, event, args)
34
+ end
35
+
36
+ it 'delgates broadcast to given broadcaster' do
37
+ expect(broadcaster).to receive(:broadcast).with(subscriber, publisher, event, args)
38
+ subject.broadcast(subscriber, publisher, event, args)
39
+ end
40
+ end
41
+
42
+ context 'with arguments' do
43
+ let(:args) { [arg_double(id: 3), arg_double(id: 4)] }
44
+
45
+ it 'logs published event and arguments' do
46
+ expect(logger).to receive(:info).with('[WISPER] Publisher#1 published thing_created to Subscriber#2 with Argument#3, Argument#4')
47
+ subject.broadcast(subscriber, publisher, event, args)
48
+ end
49
+
50
+ it 'delgates broadcast to given broadcaster' do
51
+ expect(broadcaster).to receive(:broadcast).with(subscriber, publisher, event, args)
52
+ subject.broadcast(subscriber, publisher, event, args)
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ # provides a way to specify `double.class.name` easily
59
+ def classy_double(klass, options)
60
+ double(klass, options.merge(class: double_class(klass)))
61
+ end
62
+
63
+ def arg_double(options)
64
+ classy_double('Argument', options)
65
+ end
66
+
67
+ def double_class(name)
68
+ double(name: name)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,28 @@
1
+ module Wisper
2
+ module Broadcasters
3
+ describe SendBroadcaster do
4
+ let(:subscriber) { double('subscriber') }
5
+ let(:event) { 'thing_created' }
6
+
7
+ describe '#broadcast' do
8
+ context 'without arguments' do
9
+ let(:args) { [] }
10
+
11
+ it 'sends event to subscriber without any arguments' do
12
+ expect(subscriber).to receive(event).with(no_args())
13
+ subject.broadcast(subscriber, anything, event, args)
14
+ end
15
+ end
16
+
17
+ context 'with arguments' do
18
+ let(:args) { [1,2,3] }
19
+
20
+ it 'sends event to subscriber with arguments' do
21
+ expect(subscriber).to receive(event).with(*args)
22
+ subject.broadcast(subscriber, anything, event, args)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ module Wisper
2
+ describe Configuration do
3
+ describe 'broadcasters' do
4
+ let(:broadcaster) { double }
5
+ let(:key) { :default }
6
+
7
+ it '#broadcasters returns empty collection' do
8
+ expect(subject.broadcasters).to be_empty
9
+ end
10
+
11
+ it '#broadcaster adds given broadcaster' do
12
+ subject.broadcaster(key, broadcaster)
13
+ expect(subject.broadcasters).to include key
14
+ expect(subject.broadcasters[key]).to eql broadcaster
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Wisper::Publisher do
4
2
  let(:listener) { double('listener') }
5
3
  let(:publisher) { publisher_class.new }
@@ -82,9 +80,6 @@ describe Wisper::Publisher do
82
80
  let(:listener_1) { double('Listener') }
83
81
  let(:listener_2) { double('Listener') }
84
82
 
85
- before do
86
- end
87
-
88
83
  it 'scopes listener to given class' do
89
84
  expect(listener_1).to receive(:it_happended)
90
85
  expect(listener_2).not_to receive(:it_happended)
@@ -115,6 +110,66 @@ describe Wisper::Publisher do
115
110
  end
116
111
  end
117
112
 
113
+ describe ':broadcaster argument'do
114
+ let(:broadcaster) { double('broadcaster') }
115
+ let(:listener) { double('listener') }
116
+ let(:event_name) { 'it_happened' }
117
+
118
+ before do
119
+ Wisper.configuration.broadcasters.clear
120
+ allow(listener).to receive(event_name)
121
+ allow(broadcaster).to receive(:broadcast)
122
+ end
123
+
124
+ after { Wisper.setup } # restore default configuration
125
+
126
+ it 'given an object which responds_to broadcast it uses object' do
127
+ publisher.subscribe(listener, broadcaster: broadcaster)
128
+ expect(broadcaster).to receive('broadcast')
129
+ publisher.send(:broadcast, event_name)
130
+ end
131
+
132
+ it 'given a key it uses a configured broadcaster' do
133
+ Wisper.configure { |c| c.broadcaster(:foobar, broadcaster) }
134
+ publisher.subscribe(listener, broadcaster: :foobar)
135
+ expect(broadcaster).to receive('broadcast')
136
+ publisher.send(:broadcast, event_name)
137
+ end
138
+
139
+ it 'given an unknown key it raises error' do
140
+ expect { publisher.subscribe(listener, broadcaster: :foobar) }.to raise_error(KeyError, /broadcaster not found/)
141
+ end
142
+
143
+ it 'given nothing it uses the default broadcaster' do
144
+ Wisper.configure { |c| c.broadcaster(:default, broadcaster) }
145
+ publisher.subscribe(listener)
146
+ expect(broadcaster).to receive('broadcast')
147
+ publisher.send(:broadcast, event_name)
148
+ end
149
+
150
+ describe 'async alias' do
151
+ it 'given an object which responds_to broadcast it uses object' do
152
+ publisher.subscribe(listener, async: broadcaster)
153
+ expect(broadcaster).to receive('broadcast')
154
+ publisher.send(:broadcast, event_name)
155
+ end
156
+
157
+ it 'given true it uses configured async broadcaster' do
158
+ Wisper.configure { |c| c.broadcaster(:async, broadcaster) }
159
+ publisher.subscribe(listener, async: true)
160
+ expect(broadcaster).to receive('broadcast')
161
+ publisher.send(:broadcast, event_name)
162
+ end
163
+
164
+ it 'given false it uses configured default broadcaster' do
165
+ Wisper.configure { |c| c.broadcaster(:default, broadcaster) }
166
+ publisher.subscribe(listener, async: false)
167
+ expect(broadcaster).to receive('broadcast')
168
+ publisher.send(:broadcast, event_name)
169
+ end
170
+ end
171
+ end
172
+
118
173
  it 'returns publisher so methods can be chained' do
119
174
  expect(publisher.add_listener(listener, :on => 'so_did_this')).to \
120
175
  eq publisher
@@ -0,0 +1,14 @@
1
+ describe Wisper::ObjectRegistration do
2
+
3
+ describe 'broadcaster' do
4
+ it 'defaults to SendBroadcaster' do
5
+ subject = Wisper::ObjectRegistration.new(double('listener'), {})
6
+ expect(subject.broadcaster).to be_instance_of(Wisper::Broadcasters::SendBroadcaster)
7
+ end
8
+
9
+ it 'default is lazily evaluated' do
10
+ expect(Wisper::Broadcasters::SendBroadcaster).to_not receive :new
11
+ Wisper::ObjectRegistration.new(double('listener'), broadcaster: double('DifferentBroadcaster').as_null_object)
12
+ end
13
+ end
14
+ end
@@ -1,4 +1,3 @@
1
- require 'spec_helper'
2
1
  require 'wisper/rspec/matchers'
3
2
 
4
3
  RSpec::configure do |config|
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  describe Wisper do
4
2
  before do
5
3
  # assign the stderr to a new StringIO to test errors
@@ -79,4 +77,18 @@ describe Wisper do
79
77
  end
80
78
  end
81
79
  end
80
+
81
+ it '.configuration returns configuration' do
82
+ expect(Wisper.configuration).to be_an_instance_of(Wisper::Configuration)
83
+ end
84
+
85
+ it '.configure yields block to configuration' do
86
+ Wisper.configure do |config|
87
+ expect(config).to be_an_instance_of(Wisper::Configuration)
88
+ end
89
+ end
90
+
91
+ it 'has a default broadcaster' do
92
+ expect(Wisper.configuration.broadcasters[:default]).to be_instance_of(Wisper::Broadcasters::SendBroadcaster)
93
+ end
82
94
  end
metadata CHANGED
@@ -1,36 +1,35 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: wisper
3
- version: !ruby/object:Gem::Version
4
- version: 1.4.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.5.0
5
5
  platform: ruby
6
- authors:
6
+ authors:
7
7
  - Kris Leech
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
-
12
- date: 2014-09-08 00:00:00 Z
11
+ date: 2014-10-06 00:00:00.000000000 Z
13
12
  dependencies: []
14
-
15
13
  description: pub/sub for Ruby objects
16
- email:
14
+ email:
17
15
  - kris.leech@gmail.com
18
16
  executables: []
19
-
20
17
  extensions: []
21
-
22
18
  extra_rdoc_files: []
23
-
24
- files:
25
- - .gitignore
26
- - .rspec
27
- - .travis.yml
19
+ files:
20
+ - ".gitignore"
21
+ - ".rspec"
22
+ - ".ruby-version"
23
+ - ".travis.yml"
28
24
  - CHANGELOG.md
29
25
  - Gemfile
30
26
  - Guardfile
31
27
  - README.md
32
28
  - Rakefile
33
29
  - lib/wisper.rb
30
+ - lib/wisper/broadcasters/logger_broadcaster.rb
31
+ - lib/wisper/broadcasters/send_broadcaster.rb
32
+ - lib/wisper/configuration.rb
34
33
  - lib/wisper/global_listeners.rb
35
34
  - lib/wisper/publisher.rb
36
35
  - lib/wisper/registration/block.rb
@@ -40,50 +39,54 @@ files:
40
39
  - lib/wisper/rspec/stub_wisper_publisher.rb
41
40
  - lib/wisper/temporary_listeners.rb
42
41
  - lib/wisper/version.rb
43
- - spec/lib/async_spec.rb
44
42
  - spec/lib/global_subscribers_spec.rb
45
43
  - spec/lib/integration_spec.rb
46
44
  - spec/lib/simple_example_spec.rb
47
45
  - spec/lib/temporary_global_listeners_spec.rb
46
+ - spec/lib/wisper/broadcasters/logger_broadcaster_spec.rb
47
+ - spec/lib/wisper/broadcasters/send_broadcaster_spec.rb
48
+ - spec/lib/wisper/configuration_spec.rb
48
49
  - spec/lib/wisper/publisher_spec.rb
50
+ - spec/lib/wisper/registrations/object_spec.rb
49
51
  - spec/lib/wisper/rspec/matchers_spec.rb
50
52
  - spec/lib/wisper/rspec/stub_wisper_publisher_spec.rb
51
53
  - spec/lib/wisper_spec.rb
52
54
  - spec/spec_helper.rb
53
55
  - wisper.gemspec
54
56
  homepage: https://github.com/krisleech/wisper
55
- licenses:
57
+ licenses:
56
58
  - MIT
57
59
  metadata: {}
58
-
59
60
  post_install_message:
60
61
  rdoc_options: []
61
-
62
- require_paths:
62
+ require_paths:
63
63
  - lib
64
- required_ruby_version: !ruby/object:Gem::Requirement
65
- requirements:
66
- - &id001
67
- - ">="
68
- - !ruby/object:Gem::Version
69
- version: "0"
70
- required_rubygems_version: !ruby/object:Gem::Requirement
71
- requirements:
72
- - *id001
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
73
74
  requirements: []
74
-
75
75
  rubyforge_project:
76
76
  rubygems_version: 2.2.2
77
77
  signing_key:
78
78
  specification_version: 4
79
79
  summary: pub/sub for Ruby objects
80
- test_files:
81
- - spec/lib/async_spec.rb
80
+ test_files:
82
81
  - spec/lib/global_subscribers_spec.rb
83
82
  - spec/lib/integration_spec.rb
84
83
  - spec/lib/simple_example_spec.rb
85
84
  - spec/lib/temporary_global_listeners_spec.rb
85
+ - spec/lib/wisper/broadcasters/logger_broadcaster_spec.rb
86
+ - spec/lib/wisper/broadcasters/send_broadcaster_spec.rb
87
+ - spec/lib/wisper/configuration_spec.rb
86
88
  - spec/lib/wisper/publisher_spec.rb
89
+ - spec/lib/wisper/registrations/object_spec.rb
87
90
  - spec/lib/wisper/rspec/matchers_spec.rb
88
91
  - spec/lib/wisper/rspec/stub_wisper_publisher_spec.rb
89
92
  - spec/lib/wisper_spec.rb
@@ -1,10 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'async option' do
4
- let(:listener) { double('listener') }
5
- let(:publisher) { publisher_class.new }
6
-
7
- it 'it raises a deprecation exception' do
8
- expect { publisher.add_listener(listener, :async => true) }.to raise_error
9
- end
10
- end