pubsubstub 0.0.9 → 0.0.10
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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/example/Gemfile +2 -0
- data/example/config.ru +3 -0
- data/lib/pubsubstub/action.rb +6 -0
- data/lib/pubsubstub/channel.rb +2 -1
- data/lib/pubsubstub/event.rb +7 -4
- data/lib/pubsubstub/redis_pub_sub.rb +15 -4
- data/lib/pubsubstub/stream_action.rb +50 -7
- data/lib/pubsubstub/version.rb +1 -1
- data/pubsubstub.gemspec +5 -2
- data/spec/channel_spec.rb +10 -0
- data/spec/event_spec.rb +5 -3
- data/spec/redis_pub_sub_spec.rb +1 -1
- data/spec/spec_helper.rb +14 -11
- data/spec/stream_action_spec.rb +75 -0
- metadata +58 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13fa1f1bf8d271516e2ed2389138bf55184d8b13
|
4
|
+
data.tar.gz: dd1bfc72703d76dc3f6a826bd8b69121efaf1a42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3948cd0bbbc474de69185969dd120135941a16ecc4eb8c24fe508f80f83130a3b3f6faf4d0a7c6845f1050580222b059bb0581630d4bb4f09916593c0c9dd82
|
7
|
+
data.tar.gz: 03fe2195a09f6c5bf6cb275399e0f2a447a609c4241e355f3ca7b732edb7d0307157bcb17aee95d89bba99126b4a3c6aa914811d80a6dbc7f436cb5a9a466154
|
data/.travis.yml
CHANGED
data/example/Gemfile
ADDED
data/example/config.ru
ADDED
data/lib/pubsubstub/action.rb
CHANGED
data/lib/pubsubstub/channel.rb
CHANGED
@@ -27,7 +27,6 @@ module Pubsubstub
|
|
27
27
|
pubsub.publish(event)
|
28
28
|
end
|
29
29
|
|
30
|
-
private
|
31
30
|
def scrollback(connection, last_event_id)
|
32
31
|
return unless last_event_id
|
33
32
|
pubsub.scrollback(last_event_id) do |event|
|
@@ -35,6 +34,8 @@ module Pubsubstub
|
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
37
|
+
private
|
38
|
+
|
38
39
|
def broadcast(json)
|
39
40
|
string = Event.from_json(json).to_message
|
40
41
|
@connections.each do |connection|
|
data/lib/pubsubstub/event.rb
CHANGED
@@ -1,35 +1,38 @@
|
|
1
1
|
module Pubsubstub
|
2
2
|
class Event
|
3
|
-
attr_reader :id, :name, :data
|
3
|
+
attr_reader :id, :name, :data, :retry_after
|
4
4
|
|
5
5
|
def initialize(data, options = {})
|
6
6
|
@id = options[:id] || time_now
|
7
7
|
@name = options[:name]
|
8
|
+
@retry_after = options[:retry_after]
|
8
9
|
@data = data
|
9
10
|
end
|
10
11
|
|
11
12
|
def to_json
|
12
|
-
{id: @id, name: @name, data: @data}.to_json
|
13
|
+
{id: @id, name: @name, data: @data, retry_after: @retry_after}.to_json
|
13
14
|
end
|
14
15
|
|
15
16
|
def to_message
|
16
17
|
data = @data.split("\n").map{ |segment| "data: #{segment}" }.join("\n")
|
17
18
|
message = "id: #{id}" << "\n"
|
18
19
|
message << "event: #{name}" << "\n" if name
|
20
|
+
message << "retry: #{retry_after}" << "\n" if retry_after
|
19
21
|
message << data << "\n\n"
|
20
22
|
message
|
21
23
|
end
|
22
24
|
|
23
25
|
def self.from_json(json)
|
24
26
|
hash = JSON.load(json)
|
25
|
-
new(hash['data'], name: hash['name'], id: hash['id'])
|
27
|
+
new(hash['data'], name: hash['name'], id: hash['id'], retry_after: hash['retry_after'])
|
26
28
|
end
|
27
29
|
|
28
30
|
def ==(other)
|
29
|
-
id == other.id && name == other.name && data == other.data
|
31
|
+
id == other.id && name == other.name && data == other.data && retry_after == other.retry_after
|
30
32
|
end
|
31
33
|
|
32
34
|
private
|
35
|
+
|
33
36
|
def time_now
|
34
37
|
(Time.now.to_f * 1000).to_i
|
35
38
|
end
|
@@ -17,14 +17,21 @@ module Pubsubstub
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def scrollback(since_event_id)
|
20
|
-
|
20
|
+
redis = if EventMachine.reactor_running?
|
21
|
+
self.class.nonblocking_redis
|
22
|
+
else
|
23
|
+
self.class.blocking_redis
|
24
|
+
end
|
25
|
+
|
26
|
+
redis.zrangebyscore(key('scrollback'), "(#{since_event_id.to_i}", '+inf') do |events|
|
21
27
|
events.each do |json|
|
22
28
|
yield Pubsubstub::Event.from_json(json)
|
23
29
|
end
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
27
|
-
|
33
|
+
private
|
34
|
+
|
28
35
|
def key(purpose)
|
29
36
|
[@channel_name, purpose].join(".")
|
30
37
|
end
|
@@ -40,11 +47,15 @@ module Pubsubstub
|
|
40
47
|
end
|
41
48
|
|
42
49
|
def blocking_redis
|
43
|
-
@blocking_redis ||= Redis.new(url:
|
50
|
+
@blocking_redis ||= Redis.new(url: redis_url)
|
44
51
|
end
|
45
52
|
|
46
53
|
def nonblocking_redis
|
47
|
-
@nonblocking_redis ||= EM::Hiredis.connect(
|
54
|
+
@nonblocking_redis ||= EM::Hiredis.connect(redis_url)
|
55
|
+
end
|
56
|
+
|
57
|
+
def redis_url
|
58
|
+
ENV['REDIS_URL'] || "redis://localhost:6379"
|
48
59
|
end
|
49
60
|
end
|
50
61
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Pubsubstub
|
2
2
|
class StreamAction < Pubsubstub::Action
|
3
|
+
RECONNECT_TIMEOUT = 10_000
|
4
|
+
|
3
5
|
def initialize(*)
|
4
6
|
super
|
5
7
|
start_heartbeat
|
@@ -12,31 +14,72 @@ module Pubsubstub
|
|
12
14
|
'X-Accel-Buffering' => 'no',
|
13
15
|
'Connection' => 'keep-alive',
|
14
16
|
})
|
17
|
+
|
18
|
+
if EventMachine.reactor_running?
|
19
|
+
subscribe_connection
|
20
|
+
else
|
21
|
+
return_scrollback
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def return_scrollback
|
28
|
+
buffer = ''
|
29
|
+
ensure_connection_has_event(buffer)
|
30
|
+
|
31
|
+
with_each_channel do |channel|
|
32
|
+
channel.scrollback(buffer, last_event_id)
|
33
|
+
end
|
34
|
+
|
35
|
+
buffer
|
36
|
+
end
|
37
|
+
|
38
|
+
def last_event_id
|
39
|
+
request.env['HTTP_LAST_EVENT_ID']
|
40
|
+
end
|
41
|
+
|
42
|
+
def subscribe_connection
|
15
43
|
stream(:keep_open) do |connection|
|
16
44
|
@connections << connection
|
17
|
-
|
18
|
-
|
19
|
-
channel
|
45
|
+
ensure_connection_has_event(connection)
|
46
|
+
with_each_channel do |channel|
|
47
|
+
channel.subscribe(connection, last_event_id: last_event_id)
|
20
48
|
end
|
21
49
|
|
22
50
|
connection.callback do
|
23
51
|
@connections.delete(connection)
|
24
|
-
|
25
|
-
channel
|
52
|
+
with_each_channel do |channel|
|
53
|
+
channel.unsubscribe(connection)
|
26
54
|
end
|
27
55
|
end
|
28
56
|
end
|
29
57
|
end
|
30
58
|
|
31
|
-
|
59
|
+
def ensure_connection_has_event(connection)
|
60
|
+
return if last_event_id
|
61
|
+
connection << heartbeat_event.to_message
|
62
|
+
end
|
63
|
+
|
32
64
|
def start_heartbeat
|
33
65
|
@heartbeat = Thread.new do
|
34
66
|
loop do
|
35
67
|
sleep Pubsubstub.heartbeat_frequency
|
36
|
-
event =
|
68
|
+
event = heartbeat_frequency.to_message
|
37
69
|
@connections.each { |connection| connection << event }
|
38
70
|
end
|
39
71
|
end
|
40
72
|
end
|
73
|
+
|
74
|
+
def with_each_channel(&block)
|
75
|
+
channels = params[:channels] || [:default]
|
76
|
+
channels.each do |channel_name|
|
77
|
+
yield channel(channel_name)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def heartbeat_event
|
82
|
+
Event.new('ping', name: 'heartbeat', retry_after: RECONNECT_TIMEOUT)
|
83
|
+
end
|
41
84
|
end
|
42
85
|
end
|
data/lib/pubsubstub/version.rb
CHANGED
data/pubsubstub.gemspec
CHANGED
@@ -25,7 +25,10 @@ Gem::Specification.new do |spec|
|
|
25
25
|
|
26
26
|
spec.add_development_dependency "bundler", "~> 1.5"
|
27
27
|
spec.add_development_dependency "rake", "~> 10.2"
|
28
|
-
spec.add_development_dependency "rspec", "
|
29
|
-
spec.add_development_dependency "pry"
|
28
|
+
spec.add_development_dependency "rspec", "3.1.0"
|
29
|
+
spec.add_development_dependency "pry-byebug"
|
30
30
|
spec.add_development_dependency "thin", "~> 1.6"
|
31
|
+
spec.add_development_dependency "rack-test"
|
32
|
+
spec.add_development_dependency "timecop"
|
33
|
+
spec.add_development_dependency "em-spec"
|
31
34
|
end
|
data/spec/channel_spec.rb
CHANGED
@@ -80,4 +80,14 @@ describe Pubsubstub::Channel do
|
|
80
80
|
subject.send(:broadcast, event.to_json)
|
81
81
|
end
|
82
82
|
end
|
83
|
+
|
84
|
+
context "#scrollback" do
|
85
|
+
it "sends events to the connection buffer" do
|
86
|
+
event = Pubsubstub::Event.new("message")
|
87
|
+
expect(pubsub).to receive(:scrollback).and_yield(event)
|
88
|
+
connection = ""
|
89
|
+
subject.scrollback(connection, 1)
|
90
|
+
expect(connection).to eq(event.to_message)
|
91
|
+
end
|
92
|
+
end
|
83
93
|
end
|
data/spec/event_spec.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Pubsubstub::Event do
|
4
|
-
subject {
|
4
|
+
subject {
|
5
|
+
Pubsubstub::Event.new("refresh #1500\nnew #1400", id: 12345678, name: "toto", retry_after: 1_000)
|
6
|
+
}
|
5
7
|
|
6
8
|
it "#to_json serialization" do
|
7
|
-
expect(subject.to_json).to be == {id: 12345678, name: "toto", data: "refresh #1500\nnew #1400"}.to_json
|
9
|
+
expect(subject.to_json).to be == {id: 12345678, name: "toto", data: "refresh #1500\nnew #1400", retry_after: 1_000}.to_json
|
8
10
|
end
|
9
11
|
|
10
12
|
context "#to_message" do
|
11
13
|
it "serializes to sse" do
|
12
|
-
expect(subject.to_message).to be == "id: 12345678\nevent: toto\ndata: refresh #1500\ndata: new #1400\n\n"
|
14
|
+
expect(subject.to_message).to be == "id: 12345678\nevent: toto\nretry: 1000\ndata: refresh #1500\ndata: new #1400\n\n"
|
13
15
|
end
|
14
16
|
|
15
17
|
it "does not have event if no name is specified" do
|
data/spec/redis_pub_sub_spec.rb
CHANGED
@@ -65,7 +65,7 @@ describe Pubsubstub::RedisPubSub do
|
|
65
65
|
it "yields the events in the scrollback" do
|
66
66
|
redis = double('redis')
|
67
67
|
expect(redis).to receive(:zrangebyscore).with('test.scrollback', '(1234', '+inf').and_yield([event1.to_json, event2.to_json])
|
68
|
-
expect(Pubsubstub::RedisPubSub).to receive(:
|
68
|
+
expect(Pubsubstub::RedisPubSub).to receive(:blocking_redis).and_return(redis)
|
69
69
|
expect { |block| subject.scrollback(1234, &block) }.to yield_successive_args(event1, event2)
|
70
70
|
end
|
71
71
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,18 +1,21 @@
|
|
1
|
+
require 'rack/test'
|
2
|
+
require 'pry'
|
3
|
+
require 'pry-byebug'
|
4
|
+
require 'timecop'
|
5
|
+
require 'em-spec/rspec'
|
6
|
+
|
7
|
+
ENV['RACK_ENV'] = 'test'
|
1
8
|
require_relative '../lib/pubsubstub'
|
2
|
-
|
3
|
-
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
4
|
-
# Require this file using `require "spec_helper"` to ensure that it is only
|
5
|
-
# loaded once.
|
6
|
-
#
|
7
|
-
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
9
|
+
|
8
10
|
RSpec.configure do |config|
|
9
|
-
config.
|
11
|
+
config.include Rack::Test::Methods
|
12
|
+
|
10
13
|
config.run_all_when_everything_filtered = true
|
11
14
|
config.filter_run :focus
|
15
|
+
config.color = true
|
12
16
|
|
13
|
-
# Run specs in random order to surface order dependencies. If you find an
|
14
|
-
# order dependency and want to debug it, you can fix the order by providing
|
15
|
-
# the seed, which is printed after each run.
|
16
|
-
# --seed 1234
|
17
17
|
config.order = 'random'
|
18
|
+
|
19
|
+
config.before(:each) { Pubsubstub::RedisPubSub.blocking_redis.flushdb }
|
18
20
|
end
|
21
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Pubsubstub::StreamAction without EventMachine" do
|
4
|
+
before {
|
5
|
+
allow(EventMachine).to receive(:reactor_running?).and_return(false)
|
6
|
+
}
|
7
|
+
|
8
|
+
let(:app) { Pubsubstub::StreamAction.new }
|
9
|
+
it "returns a heartbeat if there is no LAST_EVENT_ID" do
|
10
|
+
Timecop.freeze(DateTime.parse("2015-01-01T00:00:00+00:00")) do
|
11
|
+
event = Pubsubstub::Event.new(
|
12
|
+
'ping',
|
13
|
+
name: 'heartbeat',
|
14
|
+
retry_after: Pubsubstub::StreamAction::RECONNECT_TIMEOUT,
|
15
|
+
)
|
16
|
+
get "/"
|
17
|
+
expect(last_response.body).to eq(event.to_message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns an empty body if a LAST_EVENT_ID is provided and there is no scrollback" do
|
22
|
+
get "/", {}, 'HTTP_LAST_EVENT_ID' => 1
|
23
|
+
expect(last_response.body).to eq('')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "returns the content of the scrollback" do
|
27
|
+
event = Pubsubstub::Event.new("test")
|
28
|
+
expect_any_instance_of(Pubsubstub::Channel).to receive(:scrollback).and_return([event])
|
29
|
+
|
30
|
+
get "/", {}, 'HTTP_LAST_EVENT_ID' => 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "Pubsubstub::StreamAction with EventMachine" do
|
35
|
+
include EventMachine::SpecHelper
|
36
|
+
|
37
|
+
let(:app) {
|
38
|
+
Pubsubstub::StreamAction.new
|
39
|
+
}
|
40
|
+
|
41
|
+
it "returns a heartbeat if there is no LAST_EVENT_ID" do
|
42
|
+
Timecop.freeze(DateTime.parse("2015-01-01T00:00:00+00:00")) do
|
43
|
+
event = Pubsubstub::Event.new(
|
44
|
+
'ping',
|
45
|
+
name: 'heartbeat',
|
46
|
+
retry_after: Pubsubstub::StreamAction::RECONNECT_TIMEOUT,
|
47
|
+
)
|
48
|
+
get "/"
|
49
|
+
expect(last_response.body).to eq(event.to_message)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "subscribes the connection to the channel" do
|
54
|
+
event = Pubsubstub::Event.new('ping')
|
55
|
+
channel = Pubsubstub::Channel.new(:default)
|
56
|
+
allow_any_instance_of(Pubsubstub::StreamAction).to receive(:with_each_channel).and_yield(channel)
|
57
|
+
|
58
|
+
em do
|
59
|
+
env = current_session.send(:env_for, "/", 'HTTP_LAST_EVENT_ID' => 1)
|
60
|
+
request = Rack::Request.new(env)
|
61
|
+
status, headers, body = app.call(request.env)
|
62
|
+
|
63
|
+
response = Rack::MockResponse.new(status, headers, body, env["rack.errors"].flush)
|
64
|
+
channel.send(:broadcast, event.to_json)
|
65
|
+
|
66
|
+
EM.next_tick {
|
67
|
+
body.close
|
68
|
+
response.finish
|
69
|
+
|
70
|
+
expect(response.body).to eq(event.to_message)
|
71
|
+
EM.stop
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pubsubstub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Guillaume Malette
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra
|
@@ -98,30 +98,30 @@ dependencies:
|
|
98
98
|
name: rspec
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- -
|
101
|
+
- - '='
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 3.1.0
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- -
|
108
|
+
- - '='
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 3.1.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name: pry
|
112
|
+
name: pry-byebug
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '0
|
117
|
+
version: '0'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '0
|
124
|
+
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: thin
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,6 +136,48 @@ dependencies:
|
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '1.6'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rack-test
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: timecop
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: em-spec
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
139
181
|
description: Pubsubstub can be added to a rack Application or deployed standalone.
|
140
182
|
It uses Redis to do the Pub/Sub
|
141
183
|
email:
|
@@ -151,6 +193,8 @@ files:
|
|
151
193
|
- LICENSE.txt
|
152
194
|
- README.md
|
153
195
|
- Rakefile
|
196
|
+
- example/Gemfile
|
197
|
+
- example/config.ru
|
154
198
|
- lib/pubsubstub.rb
|
155
199
|
- lib/pubsubstub/action.rb
|
156
200
|
- lib/pubsubstub/application.rb
|
@@ -165,6 +209,7 @@ files:
|
|
165
209
|
- spec/event_spec.rb
|
166
210
|
- spec/redis_pub_sub_spec.rb
|
167
211
|
- spec/spec_helper.rb
|
212
|
+
- spec/stream_action_spec.rb
|
168
213
|
homepage: https://github.com/gmalette/pubsubstub
|
169
214
|
licenses:
|
170
215
|
- MIT
|
@@ -185,7 +230,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
185
230
|
version: '0'
|
186
231
|
requirements: []
|
187
232
|
rubyforge_project:
|
188
|
-
rubygems_version: 2.2.
|
233
|
+
rubygems_version: 2.2.3
|
189
234
|
signing_key:
|
190
235
|
specification_version: 4
|
191
236
|
summary: Pubsubstub is a rack middleware to add Pub/Sub
|
@@ -194,3 +239,4 @@ test_files:
|
|
194
239
|
- spec/event_spec.rb
|
195
240
|
- spec/redis_pub_sub_spec.rb
|
196
241
|
- spec/spec_helper.rb
|
242
|
+
- spec/stream_action_spec.rb
|