pubsubstub 0.0.15 → 0.1.0.beta1

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.
@@ -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