tavern 0.0.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +25 -2
- data/lib/tavern/hub.rb +4 -3
- data/lib/tavern/version.rb +1 -1
- data/spec/hub_spec.rb +108 -21
- metadata +1 -1
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# Tavern
|
2
2
|
|
3
|
-
|
3
|
+
Tavern is a simple implementation of Pub / Sub for Ruby applications, allowing one
|
4
|
+
to subscribe to topics (named by a:b:c) and to then publish events. It's designed
|
5
|
+
to have a minimal surface area for the api to make it simple to integrate into other
|
6
|
+
systems (e.g. so you can publish events over a network).
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
@@ -18,7 +21,27 @@ Or install it yourself as:
|
|
18
21
|
|
19
22
|
## Usage
|
20
23
|
|
21
|
-
|
24
|
+
|
25
|
+
The core of Tavern is a `Tavern::Hub`, a default one which can be accessed at `Tavern.hub`.
|
26
|
+
|
27
|
+
The core methods on this object (and any new Taver Hub) are:
|
28
|
+
|
29
|
+
* `hub.subscribe(topic, callable = nil, &block)` - Creates a subscription for the given topic
|
30
|
+
using either a callable object or a block. Will return the instance of the subscription. Note
|
31
|
+
that this will also match any child notifications - e.g. publishing `a:b:c` will match subscriptions
|
32
|
+
for `a:b` and `a`. The callable should take one argument which includes an environment merged with
|
33
|
+
some extra details.
|
34
|
+
* `hub.publish(topic, context = {})` - Publishes an event with a given name, including the specified
|
35
|
+
context optionally passed into the callable. Topics should be of the form `a:b:c` with no limit on items.
|
36
|
+
Will return the full published metadata.
|
37
|
+
* `hub.unsubscribe(subscription)` - Given a return from `hub.subscribe`, will unsubscribe it and prevent
|
38
|
+
it from receiving any new messages.
|
39
|
+
|
40
|
+
The idea behind this decoupled architecture is that it doesn't matter how the middle layer is implemented,
|
41
|
+
you can just publish and receive messages with no worries. At the moment, subscriptions happen in app.
|
42
|
+
|
43
|
+
For conveinience sake, we also proxy `subscribe` and `publish` on the `Tavern` object to the default hub
|
44
|
+
at `Tavern.hub`.
|
22
45
|
|
23
46
|
## Contributing
|
24
47
|
|
data/lib/tavern/hub.rb
CHANGED
@@ -2,6 +2,7 @@ require 'tavern/subscription'
|
|
2
2
|
require 'tavern/subscriptions'
|
3
3
|
require 'active_support/core_ext/module/delegation'
|
4
4
|
require 'active_support/core_ext/object/blank'
|
5
|
+
require 'active_support/lazy_load_hooks'
|
5
6
|
|
6
7
|
module Tavern
|
7
8
|
|
@@ -93,9 +94,9 @@ module Tavern
|
|
93
94
|
|
94
95
|
def primary=(value)
|
95
96
|
value = !!value
|
96
|
-
if value != @
|
97
|
-
@
|
98
|
-
ActiveSupport.run_load_hooks :
|
97
|
+
if value != @primary
|
98
|
+
@primary = value
|
99
|
+
ActiveSupport.run_load_hooks :tavern_hub, self if @primary
|
99
100
|
end
|
100
101
|
end
|
101
102
|
|
data/lib/tavern/version.rb
CHANGED
data/spec/hub_spec.rb
CHANGED
@@ -15,6 +15,10 @@ describe Tavern::Hub do
|
|
15
15
|
published << ctx
|
16
16
|
end
|
17
17
|
|
18
|
+
def times_called
|
19
|
+
published.size
|
20
|
+
end
|
21
|
+
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
@@ -26,43 +30,125 @@ describe Tavern::Hub do
|
|
26
30
|
|
27
31
|
describe 'the primary hub' do
|
28
32
|
|
29
|
-
|
33
|
+
after :each do
|
34
|
+
# Force a final reset
|
35
|
+
Tavern.hub = nil
|
36
|
+
end
|
30
37
|
|
31
|
-
it 'should
|
38
|
+
it 'should let you query if something is the primary hub' do
|
39
|
+
hub.should respond_to(:primary?)
|
40
|
+
hub.should_not be_primary
|
41
|
+
Tavern.hub.should_not == hub
|
42
|
+
Tavern.hub.should be_primary
|
43
|
+
end
|
32
44
|
|
33
|
-
it 'should
|
45
|
+
it 'should run the load hook when the hub is changed' do
|
46
|
+
Tavern.hub # Force it to have loaded before hand.
|
47
|
+
called = 0
|
48
|
+
found_hub = nil
|
49
|
+
ActiveSupport.on_load(:tavern_hub) do |hub|
|
50
|
+
called += 1
|
51
|
+
found_hub = hub
|
52
|
+
end
|
53
|
+
called = 0
|
54
|
+
Tavern.hub = hub
|
55
|
+
called.should == 1
|
56
|
+
found_hub.should == hub
|
57
|
+
end
|
34
58
|
|
35
|
-
it 'should
|
59
|
+
it 'should unset it when changing the hub' do
|
60
|
+
hub = Tavern.hub
|
61
|
+
hub.should be_primary
|
62
|
+
Tavern.hub = Tavern::Hub.new
|
63
|
+
hub.should_not be_primary
|
64
|
+
end
|
36
65
|
|
37
|
-
it '
|
66
|
+
it 'should set it to primary when setting it to the hub value' do
|
67
|
+
hub = Tavern::Hub.new
|
68
|
+
hub.should_not be_primary
|
69
|
+
Tavern.hub = hub
|
70
|
+
hub.should be_primary
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'always set the default to be primary' do
|
74
|
+
# Force a recet to nil
|
75
|
+
Tavern.hub = nil
|
76
|
+
Tavern.hub.should be_primary
|
77
|
+
end
|
38
78
|
|
39
79
|
end
|
40
80
|
|
41
81
|
describe '#subscribe' do
|
42
82
|
|
43
|
-
|
83
|
+
let(:tracker) { Struct.new(:called).new(0) }
|
84
|
+
let(:callback) { lambda { |e| tracker.called += 1 } }
|
44
85
|
|
45
|
-
it 'should let you subscribe to a
|
46
|
-
|
47
|
-
|
86
|
+
it 'should let you subscribe to a top level item' do
|
87
|
+
hub.subscribe('x', &callback)
|
88
|
+
expect { hub.publish('x') }.to change tracker, :called
|
89
|
+
expect { hub.publish('x:y') }.to change tracker, :called
|
90
|
+
expect { hub.publish('x:y:z') }.to change tracker, :called
|
91
|
+
end
|
48
92
|
|
49
|
-
it 'should let you
|
93
|
+
it 'should let you subscribe to a nested item' do
|
94
|
+
hub.subscribe('x:y:z', &callback)
|
95
|
+
expect { hub.publish('x:y:z') }.to change tracker, :called
|
96
|
+
expect { hub.publish('x:y') }.to_not change tracker, :called
|
97
|
+
expect { hub.publish('x') }.to_not change tracker, :called
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should let you pass an object' do
|
101
|
+
callback = Struct.new(:tracker).new(tracker)
|
102
|
+
def callback.call(e); tracker.called += 1; end
|
103
|
+
hub.subscribe('x', callback)
|
104
|
+
expect { hub.publish('x') }.to change tracker, :called
|
105
|
+
end
|
50
106
|
|
51
|
-
it 'should
|
107
|
+
it 'should let you pass a block' do
|
108
|
+
hub.subscribe('x') { |e| tracker.called += 1 }
|
109
|
+
expect { hub.publish('x') }.to change tracker, :called
|
110
|
+
end
|
52
111
|
|
53
|
-
it 'should
|
112
|
+
it 'should return a subscription' do
|
113
|
+
subscription = hub.subscribe('x', &callback)
|
114
|
+
subscription.should be_present
|
115
|
+
subscription.should be_a Tavern::Subscription
|
116
|
+
end
|
54
117
|
|
55
|
-
it 'should raise an error when subscribing with an object that does not provide call'
|
118
|
+
it 'should raise an error when subscribing with an object that does not provide call' do
|
119
|
+
expect do
|
120
|
+
hub.subscribe 'test', Object.new
|
121
|
+
end.to raise_error ArgumentError
|
122
|
+
end
|
56
123
|
|
57
124
|
end
|
58
125
|
|
59
126
|
describe '#unsubscribe' do
|
60
127
|
|
61
|
-
|
128
|
+
let(:tracker) { Struct.new(:count, :calls).new(0, []) }
|
62
129
|
|
63
|
-
|
130
|
+
let!(:subscription) do
|
131
|
+
t = tracker
|
132
|
+
hub.subscribe 'test' do |e|
|
133
|
+
t.count += 1
|
134
|
+
t.calls << e
|
135
|
+
end
|
136
|
+
end
|
64
137
|
|
65
|
-
it 'should
|
138
|
+
it 'should remove an object from the subscription pool' do
|
139
|
+
expect { hub.publish 'test' }.to change tracker, :count
|
140
|
+
hub.unsubscribe subscription
|
141
|
+
expect { hub.publish 'test' }.to_not change tracker, :count
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'should return the subscription' do
|
145
|
+
hub.unsubscribe(subscription).should == subscription
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should do nothing with a blank item' do
|
149
|
+
hub.unsubscribe(nil).should be_nil
|
150
|
+
hub.unsubscribe('').should be_nil
|
151
|
+
end
|
66
152
|
|
67
153
|
end
|
68
154
|
|
@@ -104,11 +190,12 @@ describe Tavern::Hub do
|
|
104
190
|
hub.publish 'hello:world', {}
|
105
191
|
end
|
106
192
|
|
107
|
-
it 'should notify all subscriptions under the path'
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
193
|
+
it 'should notify all subscriptions under the path' do
|
194
|
+
expect { hub.publish 'hello:world' }.to change top_level_a, :times_called
|
195
|
+
expect { hub.publish 'hello:world' }.to change nested_a, :times_called
|
196
|
+
expect { hub.publish 'foo' }.to change top_level_b, :times_called
|
197
|
+
expect { hub.publish 'foo' }.to_not change nested_b, :times_called
|
198
|
+
end
|
112
199
|
|
113
200
|
end
|
114
201
|
|