tavern 0.0.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|