roda-sse 0.1.0 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +7 -3
- data/lib/roda/plugins/sse.rb +53 -7
- data/spec/roda-sse_spec.rb +29 -22
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e63c433081f53c50061c380222d40ba9ea30bedd06b5d95982c3dcf9bbd15fa3
|
4
|
+
data.tar.gz: 5bd7d45309f2903e6a953d59b9c02e58c6e965d758859f990ba435eed1372f23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18b52b5dd1fc76864d52c74ee81ede34cdc266f3dde68e83c7235aae3f96fdb0a9af1c6451afe6be0a63ee4bd68bc830b275ae371d7cb6ae3fb81b56bc2bf882
|
7
|
+
data.tar.gz: b862451ef7df47f104892033286e14de121a5716c2857f8b31ce3bda2db1d66d130cbd6bcef22ddeedfdcd166372ca6c2505704cf2750efd752362ecf371027c
|
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# roda-sse
|
2
2
|
|
3
|
-
The roda-sse Roda plugin adds SSE
|
3
|
+
The roda-sse Roda plugin adds SSE provides a streaming interface for
|
4
|
+
server-sent events. Each stream is wrapped in an [Async](https://github.com/socketry/async) reactor
|
5
|
+
and events are sent asyncronously via tasks. The roda-sse plugin sets
|
6
|
+
appropriate SSE headers, handles disconnection errors, ensures
|
7
|
+
streams are properly closed and provies a `last_event_id` helper.
|
4
8
|
|
5
9
|
## Installation
|
6
10
|
|
@@ -15,6 +19,8 @@ https://github.com/havenwood/roda-sse
|
|
15
19
|
|
16
20
|
## Usage
|
17
21
|
|
22
|
+
See the [examples/](https://github.com/havenwood/roda-sse/tree/main/example) directory for an example app.
|
23
|
+
|
18
24
|
roda-sse is a Roda plugin, so you need to load it into your Roda
|
19
25
|
application similar to other plugins:
|
20
26
|
|
@@ -30,7 +36,5 @@ In your routing block, you can then use `r.sse` to stream with the correct heade
|
|
30
36
|
r.sse do |stream|
|
31
37
|
stream << "data: hello\n\n"
|
32
38
|
stream << "data: world\n\n"
|
33
|
-
ensure
|
34
|
-
stream.close
|
35
39
|
end
|
36
40
|
```
|
data/lib/roda/plugins/sse.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'async'
|
4
|
+
|
3
5
|
class Roda
|
4
6
|
module RodaPlugins
|
5
7
|
# Example:
|
@@ -11,22 +13,66 @@ class Roda
|
|
11
13
|
# # GET /
|
12
14
|
# r.sse do |stream|
|
13
15
|
# stream.write "data: hola\n\n"
|
14
|
-
# ensure
|
15
|
-
# stream.close
|
16
16
|
# end
|
17
17
|
# end
|
18
18
|
# end
|
19
19
|
module SSE
|
20
|
+
class Output
|
21
|
+
def initialize(stream)
|
22
|
+
@stream = stream
|
23
|
+
end
|
24
|
+
|
25
|
+
def write(message)
|
26
|
+
data = message.to_s
|
27
|
+
@stream.write(data)
|
28
|
+
return data.bytesize
|
29
|
+
end
|
30
|
+
|
31
|
+
def <<(message)
|
32
|
+
write(message)
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def close(error = nil)
|
37
|
+
if stream = @stream
|
38
|
+
@stream = nil
|
39
|
+
stream.close_write(error)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Body
|
45
|
+
def initialize(block)
|
46
|
+
@block = block
|
47
|
+
end
|
48
|
+
|
49
|
+
def call(stream)
|
50
|
+
output = Output.new(stream)
|
51
|
+
@block.call(output)
|
52
|
+
rescue => error
|
53
|
+
ensure
|
54
|
+
stream.close_write(error)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
20
58
|
module RequestMethods
|
21
|
-
|
22
|
-
|
23
|
-
|
59
|
+
HEADERS = {
|
60
|
+
'content-type' => 'text/event-stream',
|
61
|
+
'cache-control' => 'no-cache',
|
62
|
+
}.freeze
|
24
63
|
|
25
|
-
|
26
|
-
|
64
|
+
def sse(&block)
|
65
|
+
get do
|
66
|
+
halt [200, HEADERS.dup, Body.new(block)]
|
27
67
|
end
|
28
68
|
end
|
29
69
|
end
|
70
|
+
|
71
|
+
module InstanceMethods
|
72
|
+
def last_event_id
|
73
|
+
env['HTTP_LAST_EVENT_ID']
|
74
|
+
end
|
75
|
+
end
|
30
76
|
end
|
31
77
|
|
32
78
|
register_plugin(:sse, SSE)
|
data/spec/roda-sse_spec.rb
CHANGED
@@ -12,9 +12,8 @@ class App < Roda
|
|
12
12
|
route do |r|
|
13
13
|
r.root do
|
14
14
|
r.sse do |stream|
|
15
|
+
last_event_id
|
15
16
|
stream.write "data: hola\n\n"
|
16
|
-
ensure
|
17
|
-
stream.close
|
18
17
|
end
|
19
18
|
end
|
20
19
|
end
|
@@ -23,34 +22,42 @@ end
|
|
23
22
|
def app = App.freeze.app
|
24
23
|
|
25
24
|
describe 'roda-sse plugin' do
|
26
|
-
include Rack::Test::Methods
|
27
|
-
|
28
25
|
prove_it!
|
29
26
|
|
30
|
-
|
31
|
-
|
27
|
+
describe 'roda app' do
|
28
|
+
include Rack::Test::Methods
|
32
29
|
|
33
|
-
|
34
|
-
|
30
|
+
it 'responds 200 OK' do
|
31
|
+
get '/'
|
35
32
|
|
36
|
-
|
37
|
-
|
33
|
+
assert last_response.ok?
|
34
|
+
end
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
it 'does not respond to PUT' do
|
37
|
+
post '/'
|
38
|
+
|
39
|
+
refute last_response.ok?
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'has SSE headers' do
|
43
|
+
get '/'
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
+
headers = {'content-type' => 'text/event-stream', 'cache-control' => 'no-cache'}
|
46
|
+
assert_equal headers, last_response.headers
|
47
|
+
end
|
45
48
|
|
46
|
-
|
47
|
-
|
48
|
-
stream.expect(:close, nil)
|
49
|
-
response_body = last_response.instance_variable_get(:@body)
|
50
|
-
assert_instance_of Proc, response_body
|
49
|
+
it 'streams the body' do
|
50
|
+
get '/'
|
51
51
|
|
52
|
-
|
52
|
+
stream = Minitest::Mock.new
|
53
|
+
stream.expect(:write, nil, ["data: hola\n\n"])
|
54
|
+
stream.expect(:close_write, nil, [nil])
|
55
|
+
response_body = last_response.instance_variable_get(:@body)
|
56
|
+
assert_instance_of Roda::RodaPlugins::SSE::Body, response_body
|
53
57
|
|
54
|
-
|
58
|
+
response_body.call(stream)
|
59
|
+
|
60
|
+
stream.verify
|
61
|
+
end
|
55
62
|
end
|
56
63
|
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roda-sse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shannon Skipper
|
8
|
+
- Samuel Williams
|
8
9
|
bindir: bin
|
9
10
|
cert_chain: []
|
10
|
-
date: 2024-
|
11
|
+
date: 2024-11-04 00:00:00.000000000 Z
|
11
12
|
dependencies:
|
12
13
|
- !ruby/object:Gem::Dependency
|
13
14
|
name: roda
|
@@ -15,14 +16,28 @@ dependencies:
|
|
15
16
|
requirements:
|
16
17
|
- - "~>"
|
17
18
|
- !ruby/object:Gem::Version
|
18
|
-
version: '3.
|
19
|
+
version: '3.85'
|
19
20
|
type: :runtime
|
20
21
|
prerelease: false
|
21
22
|
version_requirements: !ruby/object:Gem::Requirement
|
22
23
|
requirements:
|
23
24
|
- - "~>"
|
24
25
|
- !ruby/object:Gem::Version
|
25
|
-
version: '3.
|
26
|
+
version: '3.85'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: async
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.18'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.18'
|
26
41
|
description: The roda-sse gem integrates simple SSE streaming into the roda web toolkit.
|
27
42
|
email: shannonskipper@gmail.com
|
28
43
|
executables: []
|