pubsubstub 0.0.15 → 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pubsubstub::Subscriber do
4
+ describe "#start" do
5
+ let(:channel) { Pubsubstub::Channel.new('plop') }
6
+ let(:events) { (1..10).map { |i| Pubsubstub::Event.new("refresh ##{i}", id: i) } }
7
+
8
+ it "blocks and yield every published events" do
9
+ published_events = []
10
+ subject.add_event_listener('plop', -> (event) { published_events << event })
11
+ subscribe_thread = Thread.new { subject.start }
12
+
13
+ expect { subject.subscribed? }.to happen
14
+
15
+ events.each(&channel.method(:publish))
16
+
17
+ subject.stop
18
+ expect { !subject.subscribed? }.to happen
19
+
20
+ expect(subscribe_thread).to complete
21
+
22
+ expect(published_events).to be == events
23
+ end
24
+ end
25
+
26
+ describe "#dispatch_event" do
27
+ let(:event) { Pubsubstub::Event.new('Hello', id: 1) }
28
+
29
+ it "calls all listeners of the channel" do
30
+ events_a = []
31
+ events_b = []
32
+ events_c = []
33
+
34
+ subject.add_event_listener('plop', ->(event) { events_a << event })
35
+ subject.add_event_listener('plop', ->(event) { events_b << event })
36
+ subject.add_event_listener('foo', ->(event) { events_c << event })
37
+
38
+ subject.send(:dispatch_event, 'plop', event)
39
+
40
+ expect(events_a).to be == [event]
41
+ expect(events_b).to be == [event]
42
+ expect(events_c).to be_empty
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pubsubstub::Subscription do
4
+ let(:event) { Pubsubstub::Event.new('hello') }
5
+ let(:connection) { [] }
6
+ let(:channels) { %w(foo bar).map(&Pubsubstub::Channel.method(:new)) }
7
+ let(:channel) { Pubsubstub::Channel.new('foo') }
8
+ subject { described_class.new(channels, connection) }
9
+
10
+ it "has an id" do
11
+ expect(subject.id).to be_an Integer
12
+ end
13
+
14
+ describe "#push" do
15
+ it "stores the event in the queue" do
16
+ expect {
17
+ subject.push(event)
18
+ }.to change { subject.queue.size }
19
+ end
20
+ end
21
+
22
+ describe "#stream" do
23
+ context "when there is no scrollback" do
24
+ it "sends an heartbeat event immediately" do
25
+ channel.publish(event)
26
+ subscription_thread = Thread.start { subject.stream(event.id) }
27
+
28
+ begin
29
+ expect { connection.size == 1 }.to happen
30
+ expect(connection.first).to include('event: heartbeat')
31
+ ensure
32
+ subscription_thread.kill
33
+ end
34
+ end
35
+ end
36
+
37
+ context "when there is a scrollback" do
38
+ it "sends it immediately" do
39
+ channel.publish(event)
40
+ subscription_thread = Thread.start { subject.stream(event.id - 1) }
41
+
42
+ begin
43
+ expect { connection.size == 1 }.to happen
44
+ expect(connection.first).to be == event.to_message
45
+ ensure
46
+ subscription_thread.kill
47
+ end
48
+ end
49
+ end
50
+
51
+ context "when an event is published" do
52
+ it "forward it to the subscribers" do
53
+ subscriber_thread = Thread.start { Pubsubstub.subscriber.start }
54
+ subscription_thread = Thread.start { subject.stream(event.id - 1) }
55
+
56
+ begin
57
+ expect { connection.size == 1 }.to happen
58
+ expect(connection.first).to include('event: heartbeat')
59
+
60
+ channel.publish(event)
61
+ expect { connection.size == 2 }.to happen
62
+ expect(connection.last).to be == event.to_message
63
+ ensure
64
+ subscription_thread.kill
65
+ subscriber_thread.kill
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,52 @@
1
+ require 'net/http'
2
+
3
+ module HTTPHelpers
4
+ def async_get(uri, headers = {}, retries: 10, &block)
5
+ uri = URI(uri.to_s)
6
+ queue = Queue.new
7
+ Thread.start do
8
+ begin
9
+ Net::HTTP.start(uri.host, uri.port) do |http|
10
+ request = Net::HTTP::Get.new uri.request_uri
11
+ headers.each do |name, value|
12
+ request.add_field(name, value)
13
+ end
14
+
15
+ begin
16
+ http.request request do |response|
17
+ response.read_body do |chunk|
18
+ queue.push(chunk) unless chunk.empty?
19
+ end
20
+ end
21
+ rescue Errno::EINVAL, Errno::ECONNREFUSED # Happen once in a while when the server is not 100% ready
22
+ if retries > 0
23
+ retries -= 1
24
+ sleep 0.5
25
+ retry
26
+ else
27
+ raise
28
+ end
29
+ end
30
+ end
31
+ rescue Errno::ECONNRESET, Errno::EPIPE
32
+ # The server closed the connection
33
+ end
34
+ end
35
+
36
+ queue
37
+ end
38
+
39
+ ROOT_PATH = File.join(__dir__, '../..')
40
+ LOG_DIR = File.join(ROOT_PATH, 'tmp')
41
+ LOG_PATH = File.join(ROOT_PATH, 'tmp/puma.log')
42
+ def with_background_server
43
+ Dir.mkdir(LOG_DIR) unless Dir.exist?(LOG_DIR)
44
+ server_pid = Process.spawn('bin/server', chdir: ROOT_PATH, out: LOG_PATH, err: LOG_PATH)
45
+ begin
46
+ yield
47
+ ensure
48
+ Process.kill('TERM', server_pid)
49
+ Process.wait(server_pid)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,58 @@
1
+ RSpec::Matchers.define :complete do
2
+ match do |thread|
3
+ thread.join(@wait_time) == thread
4
+ end
5
+
6
+ chain :in_under do |seconds|
7
+ @wait_time = seconds
8
+ end
9
+
10
+ failure_message do |thread|
11
+ "expected that thread to complete but it didn't (status=#{thread.status.inspect})"
12
+ end
13
+ end
14
+
15
+ RSpec::Matchers.define :happen do
16
+ match do |condition|
17
+ success = false
18
+ @wait_time ||= 0.1
19
+ until @wait_time < 0
20
+ @wait_time -= 0.05
21
+ break if success = condition.call
22
+ sleep 0.05
23
+ end
24
+ success
25
+ end
26
+
27
+ supports_block_expectations
28
+
29
+ chain :in_under do |seconds|
30
+ @wait_time = seconds
31
+ end
32
+ end
33
+
34
+ RSpec::Matchers.define :listen do
35
+ match do |port|
36
+ start = Time.now.to_f
37
+ begin
38
+ connection = TCPSocket.new('localhost', port)
39
+ true
40
+ rescue
41
+ if (Time.now.to_f - start) < @wait_time
42
+ retry
43
+ else
44
+ false
45
+ end
46
+ ensure
47
+ connection.close if connection
48
+ end
49
+ end
50
+
51
+ chain :in_under do |seconds|
52
+ @wait_time = seconds
53
+ end
54
+
55
+ failure_message do |port|
56
+ "expected port #{port} to listen to connection but it didn't"
57
+ end
58
+ 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.15
4
+ version: 0.1.0.beta1
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-09-28 00:00:00.000000000 Z
11
+ date: 2016-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sinatra
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.4'
27
- - !ruby/object:Gem::Dependency
28
- name: em-hiredis
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '0.2'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '0.2'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: redis
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,20 +38,6 @@ dependencies:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
40
  version: '3.0'
55
- - !ruby/object:Gem::Dependency
56
- name: eventmachine
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1.0'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.0'
69
41
  - !ruby/object:Gem::Dependency
70
42
  name: bundler
71
43
  requirement: !ruby/object:Gem::Requirement
@@ -80,109 +52,13 @@ dependencies:
80
52
  - - "~>"
81
53
  - !ruby/object:Gem::Version
82
54
  version: '1.5'
83
- - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '10.2'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '10.2'
97
- - !ruby/object:Gem::Dependency
98
- name: rspec
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - '='
102
- - !ruby/object:Gem::Version
103
- version: 3.1.0
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - '='
109
- - !ruby/object:Gem::Version
110
- version: 3.1.0
111
- - !ruby/object:Gem::Dependency
112
- name: pry-byebug
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
- - !ruby/object:Gem::Dependency
126
- name: thin
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: '1.6'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - "~>"
137
- - !ruby/object:Gem::Version
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'
181
55
  description: Pubsubstub can be added to a rack Application or deployed standalone.
182
56
  It uses Redis to do the Pub/Sub
183
57
  email:
184
58
  - gmalette@gmail.com
185
- executables: []
59
+ executables:
60
+ - console
61
+ - server
186
62
  extensions: []
187
63
  extra_rdoc_files: []
188
64
  files:
@@ -193,23 +69,32 @@ files:
193
69
  - LICENSE.txt
194
70
  - README.md
195
71
  - Rakefile
72
+ - bin/console
73
+ - bin/server
196
74
  - example/Gemfile
197
75
  - example/config.ru
76
+ - example/puma_config.rb
198
77
  - lib/pubsubstub.rb
199
78
  - lib/pubsubstub/action.rb
200
79
  - lib/pubsubstub/application.rb
201
80
  - lib/pubsubstub/channel.rb
202
81
  - lib/pubsubstub/event.rb
82
+ - lib/pubsubstub/logging.rb
203
83
  - lib/pubsubstub/publish_action.rb
204
- - lib/pubsubstub/redis_pub_sub.rb
205
84
  - lib/pubsubstub/stream_action.rb
85
+ - lib/pubsubstub/subscriber.rb
86
+ - lib/pubsubstub/subscription.rb
206
87
  - lib/pubsubstub/version.rb
207
88
  - pubsubstub.gemspec
208
89
  - spec/channel_spec.rb
209
90
  - spec/event_spec.rb
210
- - spec/redis_pub_sub_spec.rb
91
+ - spec/publish_action_spec.rb
211
92
  - spec/spec_helper.rb
212
93
  - spec/stream_action_spec.rb
94
+ - spec/subscriber_spec.rb
95
+ - spec/subscription_spec.rb
96
+ - spec/support/http_helpers.rb
97
+ - spec/support/threading_matchers.rb
213
98
  homepage: https://github.com/gmalette/pubsubstub
214
99
  licenses:
215
100
  - MIT
@@ -225,18 +110,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
225
110
  version: '0'
226
111
  required_rubygems_version: !ruby/object:Gem::Requirement
227
112
  requirements:
228
- - - ">="
113
+ - - ">"
229
114
  - !ruby/object:Gem::Version
230
- version: '0'
115
+ version: 1.3.1
231
116
  requirements: []
232
117
  rubyforge_project:
233
- rubygems_version: 2.4.8
118
+ rubygems_version: 2.5.1
234
119
  signing_key:
235
120
  specification_version: 4
236
121
  summary: Pubsubstub is a rack middleware to add Pub/Sub
237
122
  test_files:
238
123
  - spec/channel_spec.rb
239
124
  - spec/event_spec.rb
240
- - spec/redis_pub_sub_spec.rb
125
+ - spec/publish_action_spec.rb
241
126
  - spec/spec_helper.rb
242
127
  - spec/stream_action_spec.rb
128
+ - spec/subscriber_spec.rb
129
+ - spec/subscription_spec.rb
130
+ - spec/support/http_helpers.rb
131
+ - spec/support/threading_matchers.rb